Python 如何避免实例之间共享类数据?

Python 如何避免实例之间共享类数据?,python,class,Python,Class,我想要的是这种行为: class a: list = [] x = a() y = a() x.list.append(1) y.list.append(2) x.list.append(3) y.list.append(4) print(x.list) # prints [1, 3] print(y.list) # prints [2, 4] 当然,当我打印时真正发生的是: print(x.list) # prints [1, 2, 3, 4] print(y.list) #

我想要的是这种行为:

class a:
    list = []

x = a()
y = a()

x.list.append(1)
y.list.append(2)
x.list.append(3)
y.list.append(4)

print(x.list) # prints [1, 3]
print(y.list) # prints [2, 4]
当然,当我打印时真正发生的是:

print(x.list) # prints [1, 2, 3, 4]
print(y.list) # prints [1, 2, 3, 4]

很明显,他们在a级共享数据。如何获得单独的实例来实现我想要的行为?

您将列表声明为类级属性,而不是实例级属性。为了在实例级别确定属性的作用域,您需要通过在_init____方法中引用self参数或根据情况在其他地方对其进行初始化

您不必严格地初始化uu init uuu方法中的实例属性,但这样更易于理解。

您希望:

class a:
    def __init__(self):
        self.list = []

在类声明中声明变量使它们成为类成员而不是实例成员。在_init__方法中声明它们可确保在对象的每个新实例旁边创建成员的新实例,这是您正在寻找的行为。

如果您希望列表成为对象属性而不是类属性,则必须在构造函数中声明。

接受的答案是可行的,但多做一点解释也无妨

创建实例时,类属性不会成为实例属性。当为它们指定值时,它们将成为实例属性

在原始代码中,实例化后列表属性没有赋值;因此它仍然是一个类属性。在uuu init_uuuu内定义列表是有效的,因为uuu init_uuuu在实例化后被调用。或者,此代码也会产生所需的输出:

>>> class a:
    list = []

>>> y = a()
>>> x = a()
>>> x.list = []
>>> y.list = []
>>> x.list.append(1)
>>> y.list.append(2)
>>> x.list.append(3)
>>> y.list.append(4)
>>> print(x.list)
[1, 3]
>>> print(y.list)
[2, 4]
然而,这个问题中令人困惑的场景永远不会发生在数字和字符串等不可变对象上,因为它们的值在没有赋值的情况下是无法更改的。例如,类似于原始字符串属性类型的代码可以正常工作:

>>> class a:
    string = ''


>>> x = a()
>>> y = a()
>>> x.string += 'x'
>>> y.string += 'y'
>>> x.string
'x'
>>> y.string
'y'

因此,总结一下:类属性成为实例属性的条件是且仅当实例化后为它们分配了值时,无论是否在_init__方法中。这是一件好事,因为通过这种方式,如果在实例化之后从未为属性赋值,那么就可以拥有静态属性。

因此,这里几乎每个响应都会错过一个特定点。如下面的代码所示,类变量永远不会成为实例变量。通过使用元类在类级别截取变量赋值,我们可以看到,当重新赋值a.myattr时,不会调用类上的字段赋值魔术方法。这是因为赋值创建了一个新的实例变量。这种行为与类变量完全无关,第二个类没有类变量,但仍然允许字段赋值

class mymeta(type):
    def __init__(cls, name, bases, d):
        pass

    def __setattr__(cls, attr, value):
        print("setting " + attr)
        super(mymeta, cls).__setattr__(attr, value)

class myclass(object):
    __metaclass__ = mymeta
    myattr = []

a = myclass()
a.myattr = []           #NOTHING IS PRINTED
myclass.myattr = [5]    #change is printed here
b = myclass()
print(b.myattr)         #pass through lookup on the base class

class expando(object):
    pass

a = expando()
a.random = 5            #no class variable required
print(a.random)         #but it still works
简言之,类变量与实例变量无关


更清楚的是,它们恰好在实例的查找范围内。类变量实际上是类对象本身上的实例变量。如果需要,也可以使用元类变量,因为元类本身也是对象。无论是否用于创建其他对象,一切都是一个对象,因此不要被其他语言的语义和单词类的用法所束缚。在python中,类实际上只是一个对象,用于确定如何创建其他对象及其行为。元类是创建类的类,只是为了进一步说明这一点

虽然公认的答案是正确的,但我想补充一点说明

让我们做一个小练习

首先,定义一个类,如下所示:

class A:
    temp = 'Skyharbor'

    def __init__(self, x):
        self.x = x

    def change(self, y):
        self.temp = y
我们这里有什么

我们有一个非常简单的类,它有一个属性temp,它是一个字符串 设置self.x的_uinit__uu方法 更改方法设置self.temp 到目前为止很直接是吗?现在让我们开始玩这个类。让我们先初始化这个类:

a = A('Tesseract')
现在请执行以下操作:

>>> print(a.temp)
Skyharbor
>>> print(A.temp)
Skyharbor
a.temp工作正常,但a.temp到底是怎么工作的?因为temp是一个类属性,所以它起作用了。python中的一切都是对象。这里A也是类类型的对象。因此,属性temp是由类持有的属性,如果通过而不是通过实例A更改temp的值,则更改后的值将反映在类的所有实例中。 让我们继续这样做:

>>> A.temp = 'Monuments'
>>> print(A.temp)
Monuments
>>> print(a.temp)
Monuments
很有趣,不是吗?请注意,ida.temp和ida.temp仍然相同

任何Python对象都会自动被赋予一个_dict_____属性,其中包含其属性列表。让我们研究一下此词典包含的示例对象:

>>> print(A.__dict__)
{
    'change': <function change at 0x7f5e26fee6e0>,
    '__module__': '__main__',
    '__init__': <function __init__ at 0x7f5e26fee668>,
    'temp': 'Monuments',
    '__doc__': None
}
>>> print(a.__dict__)
{x: 'Tesseract'}
现在我们已经使用了self,printa.temp给了我们一个不同于printa.temp的值


现在,如果我们比较ida.temp和ida.temp,它们会有所不同。

为了保护其他实例共享的变量,您需要在每次创建实例时创建新的实例变量。Whe n您正在类中声明一个变量,它是类变量,由所有实例共享。如果您想使其成为实例智能的,则需要使用init方法重新初始化变量,以引用实例

发件人:

__初始化函数。每当实例化该类的新对象时,就会调用该特殊函数

这种类型的函数在面向对象中也称为构造函数 面向对象编程。我们通常使用它来初始化所有变量

例如:

class example:
    list=[] #This is class variable shared by all instance
    def __init__(self):
        self.list = [] #This is instance variable referred to specific instance

补充说明:如果要在其中一个实例中重新分配list属性,则不会影响其他实例。因此,如果您做了类似于x.list=[]的操作,那么您可以更改它,而不会影响其他任何操作。您面临的问题是,x.list和y.list是同一个列表,所以当您对其中一个调用append时,它会影响另一个。但为什么只对list发生这种情况呢?当我在init外部声明一个整数或字符串时,它没有在对象之间共享?有人能分享这个概念的文档链接吗?@AmalTs看起来你不明白python中的赋值是如何工作的。看到或看到。您看到的行为是由于您正在对列表进行变异,但正在重新绑定对int和string的引用。@AmalTs注意:将类属性用作实例属性的延迟默认值被认为是一种不好的做法。即使属性是不可变的类型,最好在_uinit__;中分配它们。我每天都在用Python编程。请不要将列表用作属性名称。list是一个内置函数,用于构造新列表。你应该用大写字母写名字类。
class example:
    list=[] #This is class variable shared by all instance
    def __init__(self):
        self.list = [] #This is instance variable referred to specific instance