Python在性能方面不卓越,但是使用一些小技巧,可以提高Python程序的性能,避免不必要的资源浪费。Python最大的优点之一就是语法简洁,好的代码就像伪代码一样,干净、整洁、一目了然。要写出 Pythonic(优雅的、地道的、整洁的)代码,需要多看多学大牛们写的代码,github 上有很多非常优秀的源代码值得阅读,比如:requests、flask、tornado,下面列举一些常见的Pythonic写法。
1、列表推导式
>>> chars = [ c for c in 'python' ]
>>> chars
['p', 'y', 't', 'h', 'o', 'n']
2、字典推导式
>>> dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
>>> double_dict1 = {k:v*2 for (k,v) in dict1.items()}
>>> double_dict1
{'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}
3、集合推导式
>>> set1 = {1,2,3,4}
>>> double_set = {i*2 for i in set1}
>>> double_set
{8, 2, 4, 6}
4、合并字典
>>> x = {'a':1,'b':2}
>>> y = {'c':3, 'd':4}
>>> z = {**x, **y}
>>> z
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
5、复制列表
>>> nums = [1,2,3]
>>> nums[::]
[1, 2, 3]
>>> copy_nums = nums[::]
>>> copy_nums
[1, 2, 3]
6、反转列表
>>> reverse_nums = nums[::-1]
>>> reverse_nums
[3, 2, 1]
PACKING / UNPACKING/
7、变量交换
>>> a,b = 1, 2
>>> a ,b = b,a
>>> a
2
>>> b
1
8、高级拆包
>>> a, *b = 1,2,3
>>> a
1
>>> b
[2, 3]
或者
>>> a, *b, c = 1,2,3,4,5
>>> a
1
>>> b
[2, 3, 4]
>>> c
5
9、函数返回多个值(其实是自动packing成元组)然后unpacking赋值给4个变量
>>> def f():
... return 1, 2, 3, 4
...
>>> a, b, c, d = f()
>>> a
1
>>> d
4
10、列表合并成字符串
>>> " ".join(["I", "Love", "Python"])
'I Love Python'
11、链式比较
>>> if a > 2 and a < 5:
... pass
...
>>> if 2<a<5:
... pass
12、yield from
# 没有使用 field from
def dup(n):
for i in range(n):
yield i
yield i
# 使用yield from
def dup(n):
for i in range(n):
yield from [i, i]
for i in dup(3):
print(i)
>>>
0
0
1
1
2
2
13、in 代替 or
>>> if x == 1 or x == 2 or x == 3:
... pass
...
>>> if x in (1,2,3):
... pass
14、使用字典映射来替换条件搜索(字典代替多个if else)
def fun(x):
if x == 'a':
return 1
elif x == 'b':
return 2
else:
return None
def fun(x):
return {"a": 1, "b": 2}.get(x)
def fun(x):
if x == 1:
b = 10
elif x == 5:
b = 8
else:
...
def fun(x):
d = {1:10, 5:8, ...}
b = d[x]
15、有下标索引的枚举
>>> for i, e in enumerate(["a","b","c"]):
... print(i, e)
...
0 a
1 b
2 c
16、生成器
注意区分列表推导式,生成器效率更高
>>> g = (i**2 for i in range(5))
>>> g
<generator object <genexpr> at 0x10881e518>
>>> for i in g:
... print(i)
...
0
1
4
9
16
17、默认字典 defaultdict
>>> d = dict()
>>> d['nums']
KeyError: 'nums'
>>>
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> d["nums"]
[]
18、字符串格式化
>>> lang = 'python'
>>> f'{lang} is most popular language in the world'
'python is most popular language in the world'
19、列表中出现次数最多的元素
>>> nums = [1,2,3,3]
>>> max(set(nums), key=nums.count)
3
或者
from collections import Counter
>>> Counter(nums).most_common()[0][0]
3
20、读写文件
>>> with open("test.txt", "w") as f:
... f.writelines("hello")
21、判断对象类型,可指定多个类型
>>> isinstance(a, (int, str))
True
22、类似的还有字符串的 startswith,endswith
>>> "http://foofish.net".startswith(('http','https'))
True
>>> "https://foofish.net".startswith(('http','https'))
True
23、__str__
与 __repr__
区别
>>> str(datetime.now())
'2018-11-20 00:31:54.839605'
>>> repr(datetime.now())
'datetime.datetime(2018, 11, 20, 0, 32, 0, 579521)'
前者对人友好,可读性更强,后者对计算机友好,支持 obj == eval(repr(obj))
24、使用装饰器
def makebold(f):
return lambda: "<b>" + f() + "</b>"
def makeitalic(f):
return lambda: "<i>" + f() + "</i>"
@makebold
@makeitalic
def say():
return "Hello"
>>> say()
<b><i>Hello</i></b>
不使用装饰器,可读性非常差
def say():
return "Hello"
>>> makebold(makeitalic(say))()
<b><i>Hello</i></b>
25、尽量使用生成器代替列表
##不推荐
def my_range(n):
i = 0
result = []
while i &lt; n:
result.append(fn(i))
i += 1
return result # 返回列表
##推荐
def my_range(n):
i = 0
result = []
while i &lt; n:
yield fn(i) # 使用生成器代替列表
i += 1
*尽量用生成器代替列表,除非必须用到列表特有的函数。
26、中间结果尽量使用imap/ifilter代替map/filter
##不推荐
reduce(rf, filter(ff, map(mf, a_list)))
##推荐
from itertools import ifilter, imap
reduce(rf, ifilter(ff, imap(mf, a_list)))
*lazy evaluation 会带来更高的内存使用效率,特别是当处理大数据操作的时候。
27、使用any/all函数
any(x)判断x对象是否为空对象,如果都为空、0、false,则返回false,如果不都为空、0、false,则返回true("or")
all(x)如果all(x)参数x对象的所有元素不为0、''、False或者x为空对象,则返回True,否则返回False("and")
##不推荐
found = False
for item in a_list:
if condition(item):
found = True
break
if found:
# do something if found...
##推荐
if any(condition(item) for item in a_list):
# do something if found...
28、使用 with 忽视异常(仅限Python 3)
##不推荐
try:
os.remove("somefile.txt")
except OSError:
pass
##推荐
from contextlib import ignored # Python 3 only
with ignored(OSError):
os.remove("somefile.txt")
29、属性(property)
##不推荐
class Clock(object):
def __init__(self):
self.__hour = 1
def setHour(self, hour):
if 25 &gt; hour &gt; 0: self.__hour = hour
else: raise BadHourException
def getHour(self):
return self.__hour
##推荐
class Clock(object):
def __init__(self):
self.__hour = 1
def __setHour(self, hour):
if 25 &gt; hour &gt; 0: self.__hour = hour
else: raise BadHourException
def __getHour(self):
return self.__hour
hour = property(__getHour, __setHour)
30、 使用 with 处理加锁
##不推荐
import threading
lock = threading.Lock()
lock.acquire()
try:
# 互斥操作...
finally:
lock.release()
##推荐
import threading
lock = threading.Lock()
with lock:
# 互斥操作...
31、 使用局部变量
尽可能使用局部变量替代全局变量,可以是程序易于维护并且有助于提高性能节约成本。
在模块命名空间中将变量替换为局部变量,例如ls = os.linesep。一方面,可以提高程序性能,因为局部变量的搜素速度更快;另一方面,用短标识替换长模块变量,提高可阅读性。
32、减少函数调用的数量
(1)当需要确定对象类型时,使用isinstance()方法最好,id()次之,type()最差。
(2)为了避免重复计算,不要把重复操作作为参数放入循环中。
(3)使用模块X中的函数或者对象Y时,应该用from X import Y,而不是import X; X.Y。因此,当使用Y时,可以减少一次查询(解析器不必先找到模块X,然后在模块X的字典中查找Y)。
33、用生成器表达式替换列表解析
列表解析生成整个列表,会对大量数据的迭代产生负面作用。
而生成器表达式不会。生成器表达式不会创建一个列表,相反返回一个生成器,在需要的时候生成具体值(延迟的),这种方式对内存友好。
34、使用三元操作符来进行条件赋值
x = 10 if (y == 9) else 20
同样地,我们可以对类做这种操作:
x = (classA if y == 1 else classB)(param1, param2)
在上面的例子里 classA 与 classB 是两个类,其中一个类的构造函数会被调用。
下面是另一个多个条件表达式链接起来用以计算最小值的例子:
def small(a, b, c):
return a if a <= b and a <= c else (b if b <= a and b <= c else c)
print(small(1, 0, 1))
print(small(1, 2, 2))
print(small(2, 2, 3))
print(small(5, 4, 3))
#Output
#0 #1 #2 #3
我们甚至可以在列表推导中使用三元运算符:
[m**2 if m > 10 else m**4 for m in range(50)]
#=> [0, 1, 16, 81, 256, 625, 1296, 2401, 4096, 6561, 10000, 121, 144, 169, 196, 225, 256, 289, 32
35、join函数组合多个字符串
test = ['I', 'Like', 'Python', 'automation']
str = '|'.join(test)
print(str)
结果:“I|like|python|automation”