Python小tips【持续更新】
默认参数
参考:【Python】我精心设计的默认参数,怎么就出问题了呢?_哔哩哔哩_bilibili
在定义类的时候,会定义一些默认参数,可能会像这样
1 | class Player: |
然后如果像这样调用的话
1 | if __name__ == "__main__": |
输出结果为['bow', 'axe']
。
很明显,这不应该是我们想要的结果,我们期望输出应该是['axe']
。
Python的Document里有这么一段话8. 复合语句 — Python 3.11.2 文档
默认形参值会在执行函数定义时按从左至右的顺序被求值。 这意味着当函数被定义时将对表达式求值一次,相同的“预计算”值将在每次调用时被使用。 这一点在默认形参为可变对象,例如列表或字典的时候尤其需要重点理解:如果函数修改了该对象(例如向列表添加了一项),则实际上默认值也会被修改。 这通常不是人们所想要的。
简而言之,对于这种可变对象,如果使用默认参数,他们会指向同一个对象。
Document中也提到了解决方法
使用
None
作为默认值
因此,我们将类定义修改为如下,即可解决这一问题。
1 | class Player: |
使用is None对None进行判断
参考:【python】为什么判断一个值是否为None的时候,一定要用is呢?_哔哩哔哩_bilibili
不要直接作为布尔值判断(if None)
除了None之外,其他传入的空数据结构,如Dictionary,List,Tuple等也会被识别成False。
对于以下这种情况,我们原本期望共享ls1这个数组,最终能够输出[1, 1]
,可是程序运行最终只输出了[1]
,原因就是输入的ls1是空数组,使用逻辑判断结果为False。
1 | class Test: |
我们也可以使用__bool__
自定义类判断为False
的情况。输出结果为output2。
1 | class Test: |
因此,由于一些数据结构会对bool进行重载,造成对None的判断出现问题。
也不要使用==
由于可以使用__eq__
对运算符==进行重载,也可能导致判断出错。以下代码结果即可输出output1。
1 | class Test: |
使用is
某些运算符不能重载 —— is、and、or以及not(位运算符&、|以及~可以重载)[ref]流畅的Python 笔记12——运算符重载 - 知乎 (zhihu.com)[/ref]
使用dis这个module对==
和is
进行比较,观察字节码
Python3.11中运行结果
1 | >>> import dis |
使用Python3.9
1 | 1 0 LOAD_NAME 0 (a) |
可以看到只有COMPARE_OP
和IS_OP
的差别。
在Python源代码中,is是进行地址的指针比较,在c中是非常快的,而COMPARE_OP
则要慢。
generator中的坑
参考:【python】几乎没人做对的题目?聊聊生成器表达式里不为人知的秘密_哔哩哔哩_bilibili
对于以下的代码,运行结果为[1, 2]。
注意:第二行是(),不是生成式的[]。
1 | ls = [1, 2, 3, 4, 5] |
实际上,这段代码等价于
1 | ls = [1, 2, 3, 4, 5] |
在PEP 289PEP 289 – Generator Expressions | peps.python.org中提到
Only the outermost for-expression is evaluated immediately, the other expressions are deferred until the generator is run
只有最外层的 for 表达式被立即计算,其他表达式被延迟到生成器运行