Python小tips【持续更新】
本文发布于610 天前,最后更新于610天前,其中的信息可能已经有所发展或是发生改变。

默认参数

参考:【Python】我精心设计的默认参数,怎么就出问题了呢?_哔哩哔哩_bilibili

在定义类的时候,会定义一些默认参数,可能会像这样

class Player:
    def __init__(self, name, items=[]):
        self.name = name
        self.items = items

然后如果像这样调用的话

if __name__ == "__main__":
    p1 = Player("John", ["sword", "shield"])
    p2 = Player("Jane")
    p3 = Player("Jack")

    p2.items.append("bow")
    p3.items.append("axe")

    print(p3.items)

输出结果为['bow', 'axe']

很明显,这不应该是我们想要的结果,我们期望输出应该是['axe']

Python的Document里有这么一段话[1]

默认形参值会在执行函数定义时按从左至右的顺序被求值。 这意味着当函数被定义时将对表达式求值一次,相同的“预计算”值将在每次调用时被使用。 这一点在默认形参为可变对象,例如列表或字典的时候尤其需要重点理解:如果函数修改了该对象(例如向列表添加了一项),则实际上默认值也会被修改。 这通常不是人们所想要的。

简而言之,对于这种可变对象,如果使用默认参数,他们会指向同一个对象

Document中也提到了解决方法

使用 None 作为默认值

因此,我们将类定义修改为如下,即可解决这一问题。

class Player:
    def __init__(self, name, items=None):
        if items is None:
            items = []
        self.name = name
        self.items = items

使用is None对None进行判断

参考:【python】为什么判断一个值是否为None的时候,一定要用is呢?_哔哩哔哩_bilibili

不要直接作为布尔值判断(if None)

除了None之外,其他传入的空数据结构,如Dictionary,List,Tuple等也会被识别成False。

对于以下这种情况,我们原本期望共享ls1这个数组,最终能够输出[1, 1],可是程序运行最终只输出了[1],原因就是输入的ls1是空数组,使用逻辑判断结果为False。

class Test:
    def __init__(self, ls=None):
        if ls:
            self.ls = ls
        else:
            self.ls = []
    
    def cal(self):
        self.ls.append(1)

    def __str__(self):
        return str(self.ls)

if __name__ == "__main__":
    ls1 = []
    t1 = Test(ls1)
    t2 = Test(ls1)
    t1.cal()
    t2.cal()
    print(t2)

我们也可以使用__bool__自定义类判断为False的情况。输出结果为output2。

class Test:
    def __bool__(self):
        return False
    
if Test():
    print("output1")
else:
    print("output2")

因此,由于一些数据结构会对bool进行重载,造成对None的判断出现问题。

也不要使用==

由于可以使用__eq__对运算符==进行重载,也可能导致判断出错。以下代码结果即可输出output1。

class Test:
    def __eq__(self, other):
        return True
    
if Test() == None:
    print("output1")
else:
    print("output2")

使用is

某些运算符不能重载 —— is、and、or以及not(位运算符&、|以及~可以重载)[2]

使用dis这个module对==is进行比较,观察字节码

Python3.11中运行结果

>>> import dis
>>> dis.dis('a == None')
  0           0 RESUME                   0

  1           2 LOAD_NAME                0 (a)
              4 LOAD_CONST               0 (None)
              6 COMPARE_OP               2 (==)
             12 RETURN_VALUE
>>> dis.dis('a is None')
  0           0 RESUME                   0

  1           2 LOAD_NAME                0 (a)
              4 LOAD_CONST               0 (None)
              6 IS_OP                    0
              8 RETURN_VALUE
>>>

使用Python3.9

  1           0 LOAD_NAME                0 (a)
              2 LOAD_CONST               0 (None)
              4 COMPARE_OP               2 (==)
              6 RETURN_VALUE
None
  1           0 LOAD_NAME                0 (a)
              2 LOAD_CONST               0 (None)
              4 IS_OP                    0
              6 RETURN_VALUE
None

可以看到只有COMPARE_OPIS_OP的差别。

在Python源代码中,is是进行地址的指针比较,在c中是非常快的,而COMPARE_OP则要慢。

generator中的坑

参考:【python】几乎没人做对的题目?聊聊生成器表达式里不为人知的秘密_哔哩哔哩_bilibili

对于以下的代码,运行结果为[1, 2]。

注意:第二行是(),不是生成式的[]。

ls = [1, 2, 3, 4, 5]
g = (n for n in ls if n in ls)
ls = [0, 1, 2]
print(list(g))

实际上,这段代码等价于

ls = [1, 2, 3, 4, 5]
g = (n for n in ls if n in ls2)
ls2 = [0, 1, 2]
print(list(g))

在PEP 289[3]中提到

Only the outermost for-expression is evaluated immediately, the other expressions are deferred until the generator is run

只有最外层的 for 表达式被立即计算,其他表达式被延迟到生成器运行

本文作者: HUII
本文链接: https://www.huii.top/1950.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自飞一网 !部分图片来源见水印或引用。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇