Rome was not built in one day, coding will not advance vigorously with one effort.
利用Assert发现问题 assert 主要是为调试使用,方便检查程序的异常和不恰当的值。
__debug__的值为True。
断言不要滥用,应该使用在正常逻辑不可到达之处、正常情况下总是为真的场合
能使用python的异常处理,就可以不使用断言
函数调用后,需要确认返回值是否合理时可以使用断言
当条件是业务逻辑继续下去的先决条件时可以使用断言
数据值交换不推荐使用中间变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import disdef swap1 (): a = 1 b = 2 a, b = b, a >>>dis.dis(swap1) 2 0 LOAD_CONST 1 (1 ) 2 STORE_FAST 0 (a) 3 4 LOAD_CONST 2 (2 ) 6 STORE_FAST 1 (b) 4 8 LOAD_FAST 1 (b) 10 LOAD_FAST 0 (a) 12 ROT_TWO 14 STORE_FAST 0 (a) 16 STORE_FAST 1 (b) 18 LOAD_CONST 0 (None ) 20 RETURN_VALUE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def swap2 (): a = 11 b = 22 temp = a a = b b = temp >>>dis.dis(swap2) 2 0 LOAD_CONST 1 (11 ) 2 STORE_FAST 0 (a) 3 4 LOAD_CONST 2 (22 ) 6 STORE_FAST 1 (b) 4 8 LOAD_FAST 0 (a) 10 STORE_FAST 2 (temp) 5 12 LOAD_FAST 1 (b) 14 STORE_FAST 0 (a) 6 16 LOAD_FAST 2 (temp) 18 STORE_FAST 1 (b) 20 LOAD_CONST 0 (None ) 22 RETURN_VALUE
Python中的字节码是一种类似汇编指令的中间语言,但是一个字节码指令并不是对应一个机器指令。
swap1中的a, b = b, a对应着代码块的12-16行(2个LOAD_FAST, 2个STORE_FAST, 1个ROT_TWO),而swap2中第4-6行对应着13-18行(3个LOAD_FAST,3个STORE_FAST),ROT_TWO的主要作用是交换两个栈的最顶层元素,它比执行一个LOAD_FAST+STORE_FAST快。
充分利用Lazy evaluation特性
避免不必要的计算,带来性能上的提升
条件表达式,如if x and y, 在x为False情况,或者if x or y,在x为True的情况下,y都是不计算的
对于and条件表达式,应该将值为False可能性高的变量写前面;对于or条件表达式,将值为True可能性高的写前面
节省空间,使得无限循环的数据结构变成可能
生成器表达式:每次需要计算时才通过yield产生所需要的元素
1 2 3 4 5 def fib (): a, b = 0 , 1 while True : yield a a, b = b, a + b
不推荐使用type来检查类型 作为动态语言的强类型脚本语言,Python中的变量在定义时并不会指明具体的类型,Python解释器在运行时自动进行类型检查并根据需要进行隐式类型转换, 在出错时通过抛出异常来处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class UserInt (int ): def __init__ (self, var=0 ): super (UserInt, self ).__init__() self ._var = int (var) def __add__ (self, other ): if isinstance (other, UserInt): return UserInt(self ._var + other._var) return self ._var + other def __iadd__ (self, other ): raise NotImplementedError("not support operation" ) def __str__ (self ): return str (self ._var) def __repr__ (self ): return f"Interger {self._var} " user1 = UserInt() user2 = UserInt(2 ) user3 = UserInt('3' ) print (user1, user2, user3) print (user1 + user2) print (user3 + 12 ) type_user1 = type (user1) type_int = type (int ) if type_user1 is type_int: print (True ) else : print (False ) print (UserInt, type (UserInt)) print (UserInt(), type (UserInt())) print (user1, type (user1)) print (int , type (int ))
虽然UserInt继承于int,但是type()并不认为user1是int类型,显然是不合理的。基于内建类型扩展的用户定义类型,type函数并不能准确的返回结果。
推荐使用 isinstance()函数:
1 2 3 4 5 6 7 8 9 if isinstance (user1, type (UserInt())): print (True ) else : print (False ) if isinstance (user1, int ): print (True ) else : print (False )
使用enumerate()获取序列的索引和值 小需求:对某一序列进行迭代并获取序列中的元素进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 for i, ele in enumerate (list_): print (f"i:{i} , element:{ele} " ) enu = enumerate (list_) next (enu) def mock_enumerate (sequence, start=0 ): n = start for ele in sequence: yield n, ele n += 1 def reverse_enumerate (sequence, start=0 ): n = -1 for ele in reversed (sequence): yield len (sequence)+n, ele n -= 1 mydict = {1 :'aa' ,2 :'bb' } for key,value in mydict.items():
is 和 == 的适用场景 两个对象相等应该用 ==
操作符
意义
is
object identity
==
equal
is的作用是用来检验对象的标识符是否一致,也就是比较两个对象在内存中是否拥有同一块内存空间,它并不适合用来判断两个字符串是否相等。x is y 仅当x和y是同一个对象的时候才返回True,基本相当于id(x) = id(y)。而==是用来检验两个对象的值是否相等的,调用的是内部的__eq__方法, a == b相当于 a.__eq__(b) ,==是可以被重载的,而is不能被重载。
另外Python中的string interning(字符串驻留 )机制得处理较小的字符串和较长字符串有所不同。对于较小的字符串,为了提高系统性能会保留其值的一个副本,当创建新的字符串时直接指向该副本即可。所以有时有的对象有着相同的内容,但是标识符却不相同,用==判断为True,用is判断为False。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 short_a = 'aa' short_b = 'aa' id (short_a), id (short_b)Out[1 : (2899755577672 , 2899755577672 ) short_a == short_b Out[3 ]: True short_a is short_b Out[5 ]: True long_b = "just test long string" long_a = "just test long string" id (long_a), id (long_b)Out[2 ]: (2899904998112 , 2899906473896 ) long_a == long_b Out[4 ]: True long_a is long_b Out[6 ]: False
来源:《编写高质量代码:改善Python程序的91个建议》