我在理解以下内容时遇到问题:
class Test():
def __init__(self, data = []):
self.data = data
a = Test()
b = Test()
a.data.append(1)
print(b.data) # prints [1]
class Test1():
def __init__(self, data):
self.data = data
a = Test1([])
b = Test1([])
a.data.append(1)
print(b.data) # prints []
class Test2():
def __init__(self, data = 1):
self.data = data
a = Test2()
b = Test2()
a.data = 2
print(b.data) # prints 1
它打印出[1]
我期望data
的实例变量b
是一个空列表,因为它是一个实例变量而不是类变量!
如果我在没有默认参数的情况下执行相同的操作,只是将一个空列表传递给参数data
,它将起作用。它也可以使用int作为默认参数。我知道列表是通过引用传递的,但是无论如何都不应该发生。
怎么样?为什么?我在这里没看到东西吗?
答案 0 :(得分:0)
您的data
的默认参数是可变类型!当python创建Test
类的定义时,它仅生成默认参数的一个实例。这意味着每次创建一个Test
对象时,它的data
属性都将指向相同的列表。创建两个Test
对象之后,它们的两个.data
属性都指向内存中的完全相同的列表。如果您一次更改该列表,它将对所有Test
类进行更改。 list.append
方法会在适当的位置修改列表,因此您会看到此行为。
在Test1
情况下,请注意,您正在创建两个不同的空列表,并将它们分配给.data
属性。由于这是两个单独的列表,因此您可以随意更改其中一个,而不会影响另一个。
Python int
是不可变的,即使它们是可变的,您也要覆盖.data
示例中的Test2
属性,以便获得预期的行为。
但是有时候,您的函数需要默认参数为list
或其他可变类型!您可以做的是将默认值分配给None
,然后在函数内部检查该值是否为None
。如果是这样,请将您的属性分配给您实际的期望默认值。像这样:
class Test:
def __init__(self, data = None):
self.data = [ ] if data is None else data
# Set self.data to an empty list if data is none, otherwise, set it to whatever data already is.
a = Test()
b = Test()
a.data.append(1)
print(f"a.data:{a.data}, b.data:{b.data}")
# >> a.data:[1], b.data:[]
答案 1 :(得分:-1)
Python的默认参数在定义函数时会被评估一次,而不是在每次调用函数时都会被评估(例如Ruby)。这意味着,如果您使用可变的默认参数并对它进行突变,那么将来该函数的所有调用也将已经对该对象进行了突变。