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



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

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")

输出符合预期(两个类的实例都请参阅正确的类属性):

base: some value
derived: some value

我可以更改此类属性的值,一切仍然很好:

# Base.cv = "new value"
b.modify_cv("new value")
b.print_cv("base")       # -> base: new value
d.print_cv("derived")    # -> derived: new value

目前为止,一切都好。问题是,如果我通过派生类访问cv,则 Base 类和派生类之间的"连接"可能会断开:

# 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

这种行为是意料之中的,但这不是我想要的!

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

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

更新

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

Base.cv = "new value"

修改是在某些类方法中完成的(实际上所有这些类方法都是在类中实现Base)。

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

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

谢谢大家的讨论和想法(一开始我将使用 getter/setter 和模块级属性)

当您需要知道哪个类正在调用该方法时,classmethod很有用,但是如果您想要相同的行为而不管调用该方法的类如何,则可以改用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

您仍然可以在任何实例或子类上调用它,但它总是Base.cv更改:

>>> 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

更新:

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

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

你必须覆盖类的类__setattr__,即元类:

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指定元类可能会更好。

若要避免此解决方案产生不必要的副作用,请考虑在更改行为之前先检查key是否位于某个预定义的属性名称集中。

在 Python 中,在方法内部,您可以使用裸__class__变量名称来表示定义该方法的实际类。

这与传递给类方法或常规方法self.__class__传递给类方法的clsarg 不同,如果该方法在子类中调用,则该方法将引用子类。因此,cls.attr = value将在子类的__dict__上设置值,并且从该点开始,属性值将独立于该子类。这就是你正在到达那里的。

相反,您可以使用:

class MyClass:
cv  = "value"
@classmethod # this is actually optional
def modify_cv(cls, new_value):
__class__.cv = new_value

__class__在 Python 3 中由 允许一个人写的机制super的无参数形式

相关内容

  • 没有找到相关文章

最新更新