Python:如何在派生类之间共享类属性?

Python:如何在派生类之间共享类属性?,python,class-attributes,Python,Class Attributes,我想在某个类的所有实例和它的所有派生类之间共享一些信息 class Base(): cv = "some value" # information I want to share def print_cv(self, note): print("{}: {}".format(note, self.cv)) @classmethod def modify_cv(cls, new_value): # do some class-s

我想在某个类的所有实例和它的所有派生类之间共享一些信息

class Base():
    cv = "some value"  # information I want to share

    def print_cv(self, note):
        print("{}: {}".format(note, self.cv))

    @classmethod
    def modify_cv(cls, new_value):
        # do some class-specific stuff
        cls.cv = new_value

class Derived(Base):
    pass

b = Base()
d = Derived()

b.print_cv("base")
d.print_cv("derived")
输出与预期一致(两个类的实例请参见正确的类属性):

我可以更改这个class属性的值,但一切都很好:

# Base.cv = "new value"
b.modify_cv("new value")
b.print_cv("base")       # -> base: new value
d.print_cv("derived")    # -> derived: new value
到目前为止还不错。问题是,如果我通过派生类访问
cv
,基类和派生类之间的“连接”可能会断开:

# Derived.cv = "derived-specific value"
d.modify_cv("derived-specific value")
b.print_cv("base")       # -> base: new value
d.print_cv("derived")    # -> derived: derived-specific value
这种行为是意料之中的,但这不是我想要的

我理解为什么
a
b
会看到
cv
的不同值,因为它们是不同类的实例。我已经重写了派生类中的
cv
值,现在派生类的行为不同了,我已经多次使用了此功能

但是对于我当前的任务,我需要
a
b
始终使用相同的
cv

更新

我已经更新了这个问题,现在它更好地描述了现实生活中的情况。实际上我没有像这样修改
cv
值:

Base.cv = "new value"
对一些类方法进行了修改(实际上所有这些类方法都是在
Base
class中实现的)

现在解决方案变得显而易见,我只需要稍微修改一下方法:

class Base():
    @classmethod
    def modify_cv(cls, new_value):
        #cls.cv = new_value
        Base.cv = new_value

感谢大家的讨论和想法(在开始时,我打算使用getter/setter和模块级属性)

您必须覆盖类的类,即元类上的
\uuuuuuuu setattr\uuuuu

class InheritedClassAttributesMeta(type):
    def __setattr__(self, key, value):
        cls = None

        if not hasattr(self, key):
            # The attribute doesn't exist anywhere yet,
            # so just set it here
            cls = self
        else:
            # Find the base class that's actually storing it
            for cls in self.__mro__:
                if key in cls.__dict__:
                    break
        type.__setattr__(cls, key, value)


class Base(metaclass=InheritedClassAttributesMeta):
    cv = "some value"


class Derived(Base):
    pass


print(Derived.cv)
Derived.cv = "other value"
print(Base.cv)
使用元类通常是过分的,因此直接指定
Base
可能更好


为了避免不必要的副作用,在更改行为之前,首先考虑检查<代码> >密钥>代码>属性名称。

< p> <代码>类方法> />代码>当您需要知道哪个类正在调用该方法时,是有用的。但是,如果不管调用该方法的类是什么,您都需要相同的行为,那么可以使用
staticmethod
。然后,您可以使用
base.cv
通过基类的名称访问类变量:

class Base:
    cv = "some value"  # information I want to share

    def print_cv(self, note):
        print("{}: {}".format(note, self.cv))

    @staticmethod
    def modify_cv(new_value):
        Base.cv = new_value
>>> b = Base()
>>> d = Derived()
>>> Base.cv == Derived.cv == b.cv == d.cv == "some value"
True
>>> d.modify_cv("new value")
>>> Base.cv == Derived.cv == b.cv == d.cv == "new value"
True
@classmethod
def modify_cv(cls, new_value):
    do_stuff_with(cls)
    Base.cv = new_value
您仍然可以在任何实例或子类上调用它,但它总是更改
Base.cv

class Base:
    cv = "some value"  # information I want to share

    def print_cv(self, note):
        print("{}: {}".format(note, self.cv))

    @staticmethod
    def modify_cv(new_value):
        Base.cv = new_value
>>> b = Base()
>>> d = Derived()
>>> Base.cv == Derived.cv == b.cv == d.cv == "some value"
True
>>> d.modify_cv("new value")
>>> Base.cv == Derived.cv == b.cv == d.cv == "new value"
True
@classmethod
def modify_cv(cls, new_value):
    do_stuff_with(cls)
    Base.cv = new_value

更新:

如果出于其他原因仍然需要访问该类,请像以前一样使用
classmethod
cls
参数,但仍然通过
base.cv
而不是
cls.cv
访问基类的变量:

class Base:
    cv = "some value"  # information I want to share

    def print_cv(self, note):
        print("{}: {}".format(note, self.cv))

    @staticmethod
    def modify_cv(new_value):
        Base.cv = new_value
>>> b = Base()
>>> d = Derived()
>>> Base.cv == Derived.cv == b.cv == d.cv == "some value"
True
>>> d.modify_cv("new value")
>>> Base.cv == Derived.cv == b.cv == d.cv == "new value"
True
@classmethod
def modify_cv(cls, new_value):
    do_stuff_with(cls)
    Base.cv = new_value

在Python中,在方法内部,可以使用裸
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

这不同于传递给classmethods的
cls
arg,或常规方法上的
self.\uuuuu class\uuuu
,如果在子类中调用该方法,则该参数将引用子类。因此,
cls.attr=value
将设置子类“
\uu dict\uuu
上的值,并且从那时起,属性值将独立于该子类。这就是你要达到的目标

相反,您可以使用:

class MyClass:
   cv  = "value"

   @classmethod # this is actually optional
   def modify_cv(cls, new_value):
        __class__.cv = new_value
\uuuu类\uuuuu
是由 允许一个人写东西的机制
super的无参数形式

,因此只需将值分配给
Base.cv
,而不是
Derived.cv
?这似乎是最简单的解决方案,不是吗?@Aran Fey我的情况有点复杂,
cv
由几个派生类的类方法访问和修改。我想我总是可以在所涉及的方法中指定基类名,但可能有更好的方法吗?我宁愿在元类上使用属性,也不愿意与
\uuuuuuuuuuu setattr\uuuuuu
@brunodesshuilliers混为一谈?这将为每个类创建一个属性。这里不需要元类,这绝对是多余的!请看我的回答,这太过分了-另一方面,元类解决方案将适用于任何属性,而不需要为每个属性指定显式的setter。谢谢,我知道在这个特定示例中,
staticmethod
就可以了。在我的例子中,修改了
cv
的方法必须是classmethods,对于
cv
@jsbueno没有显式的setter。请检查,
staticmethod
在这个例子中工作得很好。好的-它工作了-它没有错误,因为这是对不了解该语言某些机制的人来说最简单的方法,只是有一种更合适的机制。@lesnik我已经更新了答案,您可以直接使用
classmethod
和访问
Base.cv
。编辑:但是这你已经知道了,因为它在你的主要帖子里。正确的。