如何确保每个子类都能获得其父类的新副本';s类属性(在Python中)?

如何确保每个子类都能获得其父类的新副本';s类属性(在Python中)?,python,oop,inheritance,pass-by-reference,mutable,Python,Oop,Inheritance,Pass By Reference,Mutable,我有一个基类,它有一个空白列表作为类属性。许多子类继承自这个基类。其目的是将此列表类属性与类方法结合使用,以允许每个子类跟踪其自身实例的特定集合。每个类都应该有一个单独的列表,其中只包含自己的实例 当我对这个继承方案进行了一个简单的实现时,在调试过程中,我注意到事实上每个子类都与类属性共享完全相同的列表,并且我再次从python通过引用传递列表的事实中得到乐趣。仔细观察,似乎父类的每个类属性(方法和值都一样)都是通过引用传递给每个子类的,除非在子类的定义中显式重写某个特定属性,否则这一点不会改变

我有一个基类,它有一个空白列表作为类属性。许多子类继承自这个基类。其目的是将此列表类属性与类方法结合使用,以允许每个子类跟踪其自身实例的特定集合。每个类都应该有一个单独的列表,其中只包含自己的实例

当我对这个继承方案进行了一个简单的实现时,在调试过程中,我注意到事实上每个子类都与类属性共享完全相同的列表,并且我再次从python通过引用传递列表的事实中得到乐趣。仔细观察,似乎父类的每个类属性(方法和值都一样)都是通过引用传递给每个子类的,除非在子类的定义中显式重写某个特定属性,否则这一点不会改变


在创建子类时重新初始化其类变量的最佳方法是什么?理想情况下,我想要一种方法来确保每个子级都以cls.instances属性开始,该属性是一个唯一的空白列表,在任何子级中都不涉及额外的代码。否则,我为什么要为继承而烦恼呢?(不要回答这个问题)

你的意思是这样的:

    >>> class Foo():
    ...   alist = []
    ...   def __init__(self):
    ...     pass
    >>> foo = Foo()
    >>> foo.alist
    []
    >>> Foo.alist
    []
    >>> Foo.alist.append(7)
    >>> Foo.alist
    [7]
    >>> foo.alist
    [7]
    >>> foo2 = Foo()
    >>> foo2.alist
    [7]

你的意思是这样的:

    >>> class Foo():
    ...   alist = []
    ...   def __init__(self):
    ...     pass
    >>> foo = Foo()
    >>> foo.alist
    []
    >>> Foo.alist
    []
    >>> Foo.alist.append(7)
    >>> Foo.alist
    [7]
    >>> foo.alist
    [7]
    >>> foo2 = Foo()
    >>> foo2.alist
    [7]

这是您可能需要进入元类的要点。您的意思是,每次定义新的子类时,都需要一组新的变量。因此,元类是类类型的类:每次定义子类时,都要实例化元类的新实例。每个元类实例都可以有自己的实例变量,这意味着每个子类都有自己的类变量集


有一篇关于如何使用元类来做这类事情的精彩文章。

这是您可能需要深入了解元类的要点。您的意思是,每次定义新的子类时,都需要一组新的变量。因此,元类是类类型的类:每次定义子类时,都要实例化元类的新实例。每个元类实例都可以有自己的实例变量,这意味着每个子类都有自己的类变量集


有一篇关于如何使用元类来做这类事情的文章写得很好。

我认为您在这里遇到的问题是类实际上是对象,类成员是类的
类型对象的成员,而不是其实例的成员。从这个角度来看,很容易理解为什么子类实际上没有父类级别成员的唯一实例。我过去用来解决这类问题的一种方法是将父对象中的
.instances
类变量设置为字典,然后在父对象的
\uuuuu init\uuuu
方法中,在
字典中存储的列表中,在
self
类型的键下对每个子对象的实例进行编目。请记住,即使是在父范围内,在
self
上执行
type()
也会为您提供直接创建对象的类型,而不是本地范围的类型。

我认为您遇到的问题是类实际上是对象,类成员是类的
类型
类型对象的成员,而不是其实例的成员。从这个角度来看,很容易理解为什么子类实际上没有父类级别成员的唯一实例。我过去用来解决这类问题的一种方法是将父对象中的
.instances
类变量设置为字典,然后在父对象的
\uuuuu init\uuuu
方法中,在
字典中存储的列表中,在
self
类型的键下对每个子对象的实例进行编目。请记住,即使是在父范围内,在
self
上执行
type()
也会给您直接创建对象的类型,而不是本地范围的类型。

不要不尊重@DanielRoseman及其深刻的回答,但我想我会写下你是如何使用元类来完成你想完成的事情的:

mc.py:

class Meta(type):
    def __init__(cls, name, bases, attrs):
        cls.foo = []

class A:
    __metaclass__ = Meta

class B(A):
    pass
鉴于此:

>>> import mc
>>> mc.A.foo
[]
>>> mc.A.foo.append(1)
>>> mc.A.foo
[1]
>>> mc.B.foo
[]

对于@DanielRoseman和他富有洞察力的回答,我没有不敬,但我想我应该写下你如何使用元类来完成我理解的你想要完成的事情:

mc.py:

class Meta(type):
    def __init__(cls, name, bases, attrs):
        cls.foo = []

class A:
    __metaclass__ = Meta

class B(A):
    pass
鉴于此:

>>> import mc
>>> mc.A.foo
[]
>>> mc.A.foo.append(1)
>>> mc.A.foo
[1]
>>> mc.B.foo
[]

你说的同一份名单是什么意思?它们具有相同的值,例如,
[]
,或者它们实际上是内存中相同的引用;这是有区别的,我的意思是他们完全一样。指向内存中的同一位置。id(列表)返回了相同的值等等。相同的列表是什么意思?它们具有相同的值,例如,
[]
,或者它们实际上是内存中相同的引用;这是有区别的,我的意思是他们完全一样。指向内存中的同一位置。id(list)返回相同的值,等等。不,他是说如果你将
Bar(Foo)
子类化,
id(Bar.alist)=id(Foo.alist)
。这是一个相关的问题,但就像sr2222说的,这有点不同。不,他是说如果你将
Bar(Foo)
子类化,
id(Bar.alist)==id(Foo.alist)
。这是一个相关的问题,但就像sr2222说的,这有点不同。唉。。。元类?在这一点上,我真的只想去把这个项目淹没在浴缸里。但我会坚持下去!我去看看你链接的博客。唉。。。元类?在这一点上,我真的只想去把这个项目淹没在浴缸里。但我会坚持下去!我去看看你链接的博客。