构造函数中的defaultparameter神奇地成为类变量

时间:2019-05-10 10:21:27

标签: python-3.x

我在理解以下内容时遇到问题:

    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作为默认参数。我知道列表是通过引用传递的,但是无论如何都不应该发生。 怎么样?为什么?我在这里没看到东西吗?

2 个答案:

答案 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)。这意味着,如果您使用可变的默认参数并对它进行突变,那么将来该函数的所有调用也将已经对该对象进行了突变。

https://docs.python-guide.org/writing/gotchas/