你们说 Python 这个是 Bug 还是 Feature?

2022-08-30 15:44:32 +08:00
 lanlanye
class Foo:
    def __init__(self, s: set = set()):
        self.s = s


if __name__ == "__main__":
    f1 = Foo()
    f2 = Foo()
    f1.s.add(1)
    f1.s.add(2)
    f1.s.add(3)
    print(f2.s)

版本 3.10.4

结果会是什么呢?

6808 次点击
所在节点    Python
71 条回复
wxf666
2022-08-30 15:47:03 +08:00
不是 {1, 2, 3} 么?
MoYi123
2022-08-30 15:47:18 +08:00
老问题了. {1,2,3}啊.
Jooooooooo
2022-08-30 15:48:42 +08:00
bug 是说不符合设计预期的行为. 这显然不可能是 bug. (虽然我不懂 python
lanlanye
2022-08-30 15:50:26 +08:00
我猜这种写法大概相当于

```
default = set([1])


class Foo:
def __init__(self, s=default):
self.s = s
```
lusi1990
2022-08-30 15:51:04 +08:00
这样用会引发内存泄露啊
lanlanye
2022-08-30 15:51:58 +08:00
@Jooooooooo 应该算符合预期吧,不过感觉有点反直觉,可能我下意识地以为这样写相当于每次创建一个新的 set
lanlanye
2022-08-30 15:53:56 +08:00
@lusi1990 会吗?不是每次实例化时 copy 一个指向 set 的引用吗?
wxf666
2022-08-30 15:54:29 +08:00
@lanlanye 人家[官方文档]( https://docs.python.org/zh-cn/3/reference/compound_stmts.html#function)写得很清楚了:

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

```python
def whats_on_the_telly(penguin=None):
__if penguin is None:
____penguin = []
__penguin.append("property of the zoo")
__return penguin
```
est
2022-08-30 15:57:07 +08:00
当年 1.6 亿美金估值的公司—— Digg 是如何被一句 Python 函数可变默参 毁掉的

/t/467817
lanlanye
2022-08-30 16:04:23 +08:00
@est 惊了居然还有这种事情……这个设计确实挺反直觉的,而且很难注意到,刚才发现 PyCharm 对这种情况有提示,Pyright 没有
Drahcir
2022-08-30 16:08:07 +08:00
开发了这么多年 Python ,还真没在意这个问题,学习了。
zone10
2022-08-30 16:22:31 +08:00
算是设计失误, 就跟 Null 的设计失误一样, 反正我用过 Rust 受不了 Go 作为一门比较新的语言居然不保证空类型安全, 特别是结构体默认为 nil 太 tm 容易出错了
mxT52CRuqR6o5
2022-08-30 16:24:14 +08:00
这个我知道,参数默认值在函数声明时就初始化了,像 js 的话参数默认值是在函数执行时初始化的
fds
2022-08-30 16:25:10 +08:00
现在流行用 https://docs.python.org/3/library/dataclasses.html#dataclasses.field
```py
@dataclass
class C:
mylist: list[int] = field(default_factory=list)

c = C()
c.mylist += [1, 2, 3]
```
20015jjw
2022-08-30 16:25:13 +08:00
正常 lint 都会提示
老问题了
Aloento
2022-08-30 16:27:53 +08:00
@est 所以我说谁用 python 谁倒霉
kkhaike
2022-08-30 16:31:25 +08:00
我刚学 python 也踩过这个坑,默认值要用 None
liuran
2022-08-30 16:33:24 +08:00
不进行 deep copy 算是 python 的 feature 吧,之前自己刚开始用的时候就遇到了这个坑,然后认真学习了一下 shallow copy 和 deep copy
abersheeran
2022-08-30 16:41:59 +08:00
新手常犯的错误了……
lyang
2022-08-30 16:45:52 +08:00
这个例子不知道,但是类似的碰到过,可以理解

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/876494

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX