PyCharm警告默认参数是可变的,这可能看起来很晦涩,但它意味着使用默认参数创建的对象都共享对该默认参数的同一引用。
下面是一个演示问题的代码片段:
class foo:
def __init__(self, key, stuff: list = []):
self.key = key
self.stuff = stuff
def __str__(self):
return f"{self.key} :: {self.stuff}"
def add_item(self, item):
self.stuff.append(item)
如果我接下来创建了一些 foo 类的实例,并且没有为每个实例提供一个新的 stuff 列表,那么每个实例都将共享对同一个默认列表的引用!
a = foo('a')
b = foo('b')
print(a, b)
a.add_item(1)
a.add_item(2)
print(a, b)
>>> a :: [] b :: []
>>> a :: [1, 2] b :: [1, 2]
你可以看到我已经向stuff列表中添加了一些项目,但是当我第二次打印这两个实例时,b的stuff也有两个项目...事实上,它们是相同的两个项目!
解决这个问题的最好方法是稍微改变你的代码,并将None作为默认值提供,然后使用or运算符将其与构造函数内的一个新列表合并起来。
class foo:
def __init__(self, key, stuff: list = None):
self.key = key
self.stuff = stuff or []
现在,如果我们重复构建a和b,并且像之前一样向a添加内容,我们会得到一个不同的结果。
>>> a :: [] b :: [] # before adding stuff
>>> a :: [1, 2] b :: [] # after adding stuff to a, b is still empty!
这是因为实例
a
和实例
b
不再共享对同一(默认)列表的引用,而是在初始化实例时使用新构建的列表。虽然Python隐藏了大部分指针和引用的丑陋细节,但它们仍然存在于幕后,有时我们仍然需要意识到它们的存在。顺便说一下,如果您将值(原始类型)作为默认值,它们就不会有这个问题,因为值本身被放置在实例中,而不是引用(例如stuff=1而不是stuff=[])。