你所需要的,不仅仅是一个好用的代理。
Tim Peters 的 《The Zen of Python》相信学过 Python 的都耳熟能详,在交互式环境中输入import this可以查看,其实有意思的是这段 Python 之禅的源码:
d = {}
for c in (65, 97):
for i in range(26):
d[chr(i+c)] = chr((i+13) % 26 + c)
print "".join([d.get(c, c) for c in s])
哈哈哈,相信这是大佬在跟我们举反例吧。
书中还举了一个快排的例子:
def quicksort(array):
less = []
greater = []
if len(array) <= 1:
return array
pivot =array.pop()
for x in array:
if x <= pivot:
less.append(x)
else:
greater.append(x)
return quicksort(less) + [pivot] + quicksort(greater)
代码风格
通过对语法、库和应用程序的理解来编写代码,充分体现 Python 自身的特色:
# 变量交换
a, b = b, a
# 上下文管理
with open(path, 'r') as f:
do_sth_with(f)
# 不应当过分地追求奇技淫巧
a = [1, 2, 3, 4]
a[::-1] # 不推荐。好吧,自从学了切片我一直用的这个
list(reversed(a)) # 推荐
然后表扬了 Flask 框架,提到了 generator 之类的特性尤为 Pythonic,有个包和模块的约束:
包和模块的命名采用小写、单数形式,而且短小
包通常仅作为命名空间,如只含空的__init__.py文件
命名的规范:
def find_num(searchList, num):
for listValue in searchList:
if num == listValue:
return True
else:
pass
尝试去通读官方手册,掌握不断发展的新特性,这将使你编写代码的执行效率更高,推荐深入学习 Flask、gevent 和 requests。
提到了三点:
Python 使用代码缩进的方式来分割代码块,不要混用 Tab 键和空格
Python 中单、双引号的使用
三元操作符:x if bool else y
这一点已经受教了,现在编写代码都会合理地加入块注释、行注释和文档注释,可以使用__doc__输出。
函数设计要尽量短小,嵌套层次不宜过深
函数申明应该做到合理、简单、易于使用
函数参数设计应该考虑向下兼容
一个函数只做一件事,尽量保证函数语句粒度的一致性
Python 中函数设计的好习惯还包括:不要在函数中定义可变对象作为默认值,使用异常替换返回错误,保证通过单元测试等。
# 关于函数设计的向下兼容
def readfile(filename): # 第一版本
pass
def readfile(filename, log): # 第二版本
pass
def readfile(filename, logger=logger.info): # 合理的设计
pass
最后还有个函数可读性良好的例子:
def GetContent(ServerAdr, PagePath):
http = httplib.HTTP(ServerAdr)
http.putrequest('GET', PagePath)
http.putheader('Accept', 'text/html')
http.putheader('Accept', 'text/plain')
http.endheaders()
httpcode, httpmsg, headers = http.getreply()
if httpcode != 200:
raise "Could not get document: Check URL and Path."
doc = http.getfile()
data = doc.read() # 此处是不是应该使用 with ?
doc.close
return data
def ExtractData(inputstring, start_line, end_line):
lstr = inputstring.splitlines() # split
j = 0
for i in lstr:
j += 1
if i.strip() == start_line: slice_start = j
elif i.strip() == end_line: slice_end = j
return lstr[slice_start:slice_end]
def SendEmail(sender, receiver, smtpserver, username, password, content):
subject = "Contented get from the web"
msg = MIMEText(content, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')
smtp = smtplib.SMTP()
smtp.connect(smtpserver)
smtp.login(username, password)
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()
在 Python 中应当如何使用常量:
通过命名风格提醒使用者该变量代表常量,如常量名全部大写
通过自定义类实现常量功能:将存放常量的文件命名为constant.py,并在其中定义一系列常量
class _const:
class ConstError(TypeError): pass
class ConstCaseError(ConstError): pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't change const.%s" % name
if not name.isupper():
raise self.ConstCaseError, \
'const name "%s" is not all uppercase' % name
self.__dict__(name) = value
import sys
sys.modules[__name__] = _const()
import const
const.MY_CONSTANT = 1
const.MY_SECOND_CONSTANT = 2
const.MY_THIRD_CONSTANT = 'a'
const.MY_FORTH_CONSTANT = 'b'
其他模块中引用这些常量时,按照如下方式进行即可:
from constant import const
print(const.MY_CONSTANT)
>>> y = 2
>>> assert x == y, "not equals"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: not equals
>>> x = 1
>>> y = 2
# 以上代码相当于
>>> if __debug__ and not x == y:
... raise AssertionError("not equals")
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AssertionError: not equals
运行是加入-O参数可以禁用断言。
>>> Timer('temp = x; x = y; y = temp;', 'x = 2; y = 3').timeit()
0.059251302998745814
>>> Timer('x, y = y, x', 'x = 2; y = 3').timeit()
0.05007316499904846
对于表达式x, y = y, x,在内存中执行的顺序如下:
先计算右边的表达式y, x,因此先在内存中创建元组(y, x),其标识符和值分别为y, x及其对应的值,其中y和x是在初始化已经存在于内存中的对象
计算表达式左边的值并进行赋值,元组被依次分配给左边的标识符,通过解压缩,元组第一标识符y分配给左边第一个元素x,元组第二标识符x分配给左边第一个元素y,从而达到交换的目的
下面是通过字节码的分析:
>>> import dis
>>> def swap1():
... x = 2
... y = 3
... x, y = y, x
...
>>> def swap2():
... x = 2
... y = 3
... temp = x
... x = y
... y = temp
...
>>> dis.dis(swap1)
2 0 LOAD_CONST 1 (2)
3 STORE_FAST 0 (x)
3 6 LOAD_CONST 2 (3)
9 STORE_FAST 1 (y)
4 12 LOAD_FAST 1 (y)
15 LOAD_FAST 0 (x)
18 ROT_TWO # 交换两个栈的最顶层元素
19 STORE_FAST 0 (x)
22 STORE_FAST 1 (y)
25 LOAD_CONST 0 (None)
28 RETURN_VALUE
>>> dis.dis(swap2)
2 0 LOAD_CONST 1 (2)
3 STORE_FAST 0 (x)
3 6 LOAD_CONST 2 (3)
9 STORE_FAST 1 (y)
4 12 LOAD_FAST 0 (x)
15 STORE_FAST 2 (temp)
5 18 LOAD_FAST 1 (y)
21 STORE_FAST 0 (x)
6 24 LOAD_FAST 2 (temp)
27 STORE_FAST 1 (y)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
哈哈哈,我猜到肯定是生成器实现菲波拉契序列的例子,不过对比我写的版本,唉。。。
利用 Python 的动态特征,可以实现枚举:
# 方式一
class Seasons:
Spring, Summer, Autumn, Winter = range(4)
# 方式二
def enum(*posarg, **keysarg):
return type("Enum", (object,), dict(zip(posarg, range(len(posarg))), **keysarg))
Seasons = enum("Spring", "Summer", "Autumn", Winter=1)
Seasons.Spring
# 方式三
>>> from collections import namedtuple
>>> Seasons = namedtuple('Seasons', 'Spring Summer Autumn Winter')._make(range(4))
>>> Seasons.Spring
0
# 但通过以上方式实现枚举都有不合理的地方
>>> Seasons._replace(Spring=2) │
Seasons(Spring=2, Summer=1, Autumn=2, Winter=3)
# Python3.4 中加入了枚举,仅在父类没有任何枚举成员的时候才允许继承
作为动态语言,Python 解释器会在运行时自动进行类型检查并根据需要进行隐式类型转换,当变量类型不同而两者之间又不能进行隐式类型转换时便抛出TypeError异常。
>>> def add(a, b):
... return a + b
...
>>> add(1, 2j)
(1+2j)
>>> add('a', 'b')
'ab'
>>> add(1, 2)
3
>>> add(1.0, 2.3)
3.3
>>> add([1, 2], [3, 4])
[1, 2, 3, 4]
>>> add(1, 'a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in add
TypeError: unsupported operand type(s) for +: 'int' and 'str'
所以实际应用中,我们常常需要进行类型检查,但是不推荐使用type(),因为基于内建类型扩展的用户自定义类型,type()并不能准确返回结果:
class UserInt(int):
def __init__(self, val=0):
self._val = int(val)
def __add__(self, val):
if isinstance(val, UserInt):
return UserInt(self._val + val._val)
return self._val + val
def __iadd__(self, val):
raise NotImplementedError("not support operation")
def __str__(self):
return str(self._val)
def __repr__(self):
return "Integer %s" % self._val
>>> n = UserInt()
>>> n
Integer 0
>>> print(n)
0
>>> m = UserInt(2)
>>> print(m)
2
>>> type(n) is int
False # 显然不合理
>>> isinstance(n, int)
True
我们可以使用isinstance来检查:isinstance(object, classinfo)