问题描述
在我作为python学徒的努力中,如果我尝试使用类属性,最近我陷入了某种奇怪的行为(从我的角度来看)。我不是在抱怨,但是希望您能提出一些有益的意见来阐明此问题。
In my endeavours as a python-apprentice i got recently stuck at some odd (from my point of view) behaviour if i tried to work with class attributes. I'm not complaining, but would appreciate some helpful comments to shed some light on this issue.
为了将复杂的问题简化为更简洁的问题,我将其表达为这样:
To reduce a complex matter into a more concise question i would formulate it like this:
确保类属性的行为更像继承树中的静态变量的 pythonic方法是什么?
What is the "pythonic" way to ensure that a class-attribute behaves more like a static variable in an inheritance tree?
在我看来,类属性的行为类似于具有多态特征的读取时复制默认值。只要我执行只读操作,它就保持为单例,
,但是一旦我通过派生的类或实例访问带有赋值的类属性,它就会变成一个新的引用丢失与继承的基本引用之间的关系。
It seems to me like a class-attribute behaves like a "copy on read" default value with polymorphic characteristics. As long as i do "read-only" operations it stays a "singleton",but as soon, as i access the class-attribute with an assignment through the derived class or instance it gets morphed into a new reference loosing the relation to the inherited base-reference.
(它肯定具有一些有趣的功能,但是您必须理解它才能让
接受它,因此有些
(It has sure potential for some interessting features, but you have to understand it toembrace it, so some insight is highly appreciated.)
class A(object):
classvar = 'A'
def setclassvar(self, value):
A.classvar = value
def __str__(self):
return "%s id(%s) " %(A.classvar, hex(id(A.classvar))[2:-1].upper())
class A1(A):
pass
class B(object):
classvar = 'B'
def setclassvar(self, value):
self.__class__.classvar = value
def __str__(self):
cvar = self.__class__.classvar
return "%s id(%s) " %(cvar, hex(id(cvar))[2:-1].upper())
class B1(B):
def setclassvar(self, value):
self.__class__.classvar = value
a, a1 = A(), A1()
a1.setclassvar('a')
print "new instance A: %s" %a
print "new instance A1: %s" %a
b, b1 = B(), B1()
b1.setclassvar('bb')
print "new instance B: %s" %b
print "new instance B1: %s" %b1
a1.setclassvar('aa')
print "new value a1: %s" %a
print "new value a: %s" %a
a1.classvar = 'aaa'
print "direct access a1: %s id(%s)" %(a1.classvar, hex(id(a1.classvar))[2:-1].upper())
print "method access a1: %s" %a1
print "direct access a: %s" %a
会产生以下内容:
new instance A: a id(B73468A0)
new instance A1: a id(B73468A0)
new instance B: B id(B73551C0)
new instance B1: bb id(AD1BFC)
new value a1: aa id(AD1BE6)
new value a: aa id(AD1BE6)
direct access a1: aaa id(A3A494)
method access a1: aa id(AD1BE6)
direct access a: aa id(AD1BE6)
因此,直接(分配)访问 object.classvar
或通过<$进行中介c $ c> self .__ class __。classvar 与 BASECLASS.classvar
不同。
So either the direct (assigning) access object.classvar
or mediated through self.__class__.classvar
are not the same as BASECLASS.classvar
.
这是范围问题还是完全不同。
Is this a scope issue or somethin totaly different.
期待您的回答,并感谢您。 :-)
Looking forward to your answers and thanks in forward. :-)
编辑:很短时间内就有一个答案建议使用类描述符,例如:
There was an answer for a very short time suggesting the use of class-descriptors like:How to make a class property?.
不幸的是,这似乎不起作用:
Unfortunatly that doesn't seem to work:
class Hotel(Bar):
def __init__(self):
Hotel.bar += 1
hotel = Hotel()
assert hotel.bar == 51
assert hotel.bar == foo.bar
第二个断言失败! hotel.bar与 foo.bar
引用的对象不同,而 hotel.bar
引用的对象不是Hotel。
The 2nd assertion fails! hotel.bar doesn't reference the same object as foo.bar
and hotel.bar
references somethin other then Hotel.bar!
第二编辑:我很清楚单例被认为是反模式,我并没有打算(广泛地)使用它们。因此,我没有在问题咨询中提及他们。即使有很多解决方案讨论单例并提供有关单例的解决方案,但我的问题仍然存在:为什么类变量可以如此轻松地脱离其引用? Ruby的行为方式对我来说很自然:
2nd I'm quite aware that singletons are considered an "antipattern" and i didn't intend to use them (extensivly). Therefore i didn't mention them in the question-titel. Even so there are many solutions discussing and providing solutions with and about singletons, my question stays: Why can a class-variable detach it's reference so easily? Ruby behaves more the way it feels natural to me: http://snippets.dzone.com/posts/show/6649
推荐答案
如果难以解释实现,则是个坏主意。 / strong>
If the implementation is hard to explain, it's a bad idea.
在这种情况下,为了完整起见,我包括一个实现,这是我喜欢在Python中使用的一种棘手的东西。
In this case, I am including an implementation, for sake of completness, and for this being the kind of tricky thing I like in Python.
因此,该片段的波纹管在某种程度上使用了闭包,以提供一个可以满足OP需求的Class Decorator:一个类变量,对于派生类中的读写,该类变量保持统一
Therefore, the snippet bellow abuse somewhat of closures to come up with a Class Decorator that fills the needs of the O.P. : a class variable that remains "unified" for reading and writing within derived classes.
此外,作为奖励,我将属性包装在一个描述符中,这使得该属性在实例内也不变-因此,无论何时写入该属性,子类或原始类的子类实例,可以正确更新class属性。
Moreover, as a bonus, I wrap the attribute in a descriptor which makes the attribute unchangeable within instances as well - so whenever the attribute is written - in either a subclass or instance of a subclass of the original class, the class attribute is properly updated.
作为Zen Python的作者这样说:如果难以解释实现,那将是个坏主意-我认为我再难做些了-我们在这里谈论范围动态生成的元类。它可以正常工作,但是由于大量使用了类,元类,闭包和描述符机制,因此这是一个非常神秘的代码,从而使它失去了 unpythonic代码。
As the Zen of Python puts it: "If the implementation is hard to explain, it is a bad idea" - I don't think I could come with anything harder -- we are talking of scoped dynamically generated meta-classes here. It will work, but it looses this is "unpythonic" code as it is very cryptic due to the heavy use of the class, metaclass, closures and descriptor mechanisms.
def SingletonAttrs(**names):
keys = names.keys()
def class_decorator(cls):
class Meta(type):
def __getattribute__(cls, attr):
if attr in keys:
return type.__getattribute__(owner_cls, attr)
return type.__getattribute__(cls, attr)
def __setattr__(cls, attr, value):
if attr in keys:
class Wrapper(object):
def __init__(self, value):
self.__set__(None, value)
__set__ = lambda self, instance, value: setattr(owner_cls,"__" + attr, value)
__get__ = lambda self, instance, owner: type.__getattribute__(owner_cls, "__" + attr)
return type.__setattr__(owner_cls, attr, Wrapper(value))
return type.__setattr__(cls, attr, value)
owner_cls = Meta(cls.__name__, cls.__bases__, cls.__dict__.copy())
for key in keys:
setattr(owner_cls, key, names[key])
return owner_cls
return class_decorator
if __name__ == "__main__":
@SingletonAttrs(a="value 1", b="value 2")
class Test(object):
pass
class TestB(Test):
pass
t = Test()
print t.a
print t.b
tb = TestB()
tb.a = "value 3"
print Test.a
这篇关于python:具有多态性的类属性/变量继承?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!