我想在某些类的所有实例和所有派生类之间共享一些信息。
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类之间的“连接”:
# 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 Base():
@classmethod
def modify_cv(cls, new_value):
#cls.cv = new_value
Base.cv = new_value
谢谢大家的讨论和想法(在开始时我将使用getter / setter和module-level属性)
答案 0 :(得分:1)
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
<强>更新强>
如果由于其他原因仍然需要访问该类,请像以前一样使用classmethod
cls
参数,但仍然可以通过Base.cv
访问基类的变量而不是{ {1}}:
cls.cv
答案 1 :(得分:0)
您必须在类的类上重写__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
是否在某些预定义的属性名称集中。
答案 2 :(得分:0)
在Python中,在方法内部,您可以使用裸__class__
变量名来表示定义方法的实际类。
这与传递给classmethods的cls
arg或常规方法的self.__class__
不同,如果在子类中调用该方法,它将引用子类。因此,cls.attr = value
将在子类类上设置值&#39; __dict__
,从该点开始,属性值将独立于该子类。这就是你到达那里的目的。
相反,您可以使用:
class MyClass:
cv = "value"
@classmethod # this is actually optional
def modify_cv(cls, new_value):
__class__.cv = new_value
__class__
是在Python 3中自动创建的
允许一个人写的机制
无参数形式的super