[基于python 3.4编写的答案;元类语法在2中有所不同,但我认为该技术仍然有效]
你可以通过元类来实现。Dappawit几乎可以,但我认为它有一个缺陷:
class MetaFoo(type):
@property
def thingy(cls):
return cls._thingy
class Foo(object, metaclass=MetaFoo):
_thingy = 23
这让你在Foo上获得一个类属性,但有一个问题…
print("Foo.thingy is {}".format(Foo.thingy))
# Foo.thingy is 23
# Yay, the classmethod-property is working as intended!
foo = Foo()
if hasattr(foo, "thingy"):
print("Foo().thingy is {}".format(foo.thingy))
else:
print("Foo instance has no attribute 'thingy'")
# Foo instance has no attribute 'thingy'
# Wha....?
这到底是怎么回事?为什么我不能从实例中到达class属性?
在找到我所相信的答案之前,我在这个问题上苦苦思索了很久。Python @properties是描述符的子集,并且,从描述符文档(强调我的):
属性访问的默认行为是获取、设置或删除
属性。例如,a.x有一个查找链
从a.__dict__['x']开始,然后输入(a)。__dict__['x'],并继续
通过类型(a)的基类,不包括元类。
因此方法解析顺序不包括我们的类属性(或元类中定义的任何其他属性)。有可能让内置属性装饰器的子类表现不同,但是(需要引用)我在谷歌上得到的印象是,开发人员有一个很好的理由(我不明白)这样做。
这并不意味着我们不走运;我们可以很好地访问类本身的属性……我们可以从实例中的type(self)中获取类,我们可以使用它来创建@property dispatchers:
class Foo(object, metaclass=MetaFoo):
_thingy = 23
@property
def thingy(self):
return type(self).thingy
现在Foo()。Thingy对类和实例都像预期的那样工作!如果派生类替换了它的底层_thingy(这是最初让我进行搜索的用例),它也将继续做正确的事情。
这对我来说不是百分之百的满意——必须在元类和对象类中进行设置,感觉它违反了DRY原则。但后者只是一个单行调度器;我基本同意它的存在,如果你真的想,你也可以把它压缩成。