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类中共享数据。如何获得单独的实例来实现我想要的行为?

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

您不必严格地在
\uuuu init\uuu
方法中初始化实例属性,但这样更容易理解。

您希望:

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

在类声明中声明变量使它们成为“类”成员,而不是实例成员。在
\uuuuu init\uuuuuu
方法中声明它们可确保在对象的每个新实例旁边创建成员的新实例,这就是您要查找的行为。

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

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

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

在原始代码中,实例化后,
列表
属性没有赋值;因此它仍然是一个类属性。在
\uuuuu init\uuuu
中定义列表是有效的,因为在实例化之后调用了
\uuuuuu 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'

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

因此,这里几乎每个响应都会错过一个特定点。类变量永远不会成为实例变量,如下代码所示。通过使用元类在类级别截取变量赋值,我们可以看到,当重新赋值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
    ,它是一个字符串
  • 一种
    \uuuu init\uuuu
    方法,用于设置
    self.x
  • 更改方法设置
    self.temp
到目前为止很直接是吗?现在让我们开始玩这个类。让我们先初始化这个类:

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

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

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

任何Python对象都会自动获得一个
\uuuu dict\uuu
属性,该属性包含其属性列表。让我们研究一下此词典包含的示例对象:

>>> 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
print(a.temp)
为我们提供了一个与
print(a.temp)
不同的值


现在,如果我们比较
id(a.temp)
id(a.temp)
,它们会有所不同。

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

发件人:

\uuuu init\uuuu()函数。每当实例化该类的新对象时,就会调用该特殊函数

这种类型的函数在O中也称为构造函数
>>> a.change('Intervals')
>>> print(a.temp)
Intervals
>>> print(A.temp)
Monuments
class example:
    list=[] #This is class variable shared by all instance
    def __init__(self):
        self.list = [] #This is instance variable referred to specific instance