[基于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....?
这到底是怎么回事?为什么我无法从实例访问类属性?
在找到我相信的答案之前,我一直在努力解决这个问题。Python @properties 是描述符的子集,并且来自描述符文档(强调我的):
属性访问的默认行为是从对象的字典中获取、设置或删除属性。例如,有一个以, thena.x
开头的查找链,并继续通过排除元类的基类。a.__dict__['x']
type(a).__dict__['x']
type(a)
所以方法解析顺序不包括我们的类属性(或元类中定义的任何其他内容)。可以创建行为不同的内置属性装饰器的子类,但是(需要引用)我在谷歌上得到的印象是开发人员有充分的理由(我不明白)这样做。
这并不意味着我们不走运。我们可以很好地访问类本身的属性......我们可以从type(self)
实例中获取类,我们可以使用它来制作@property 调度程序:
class Foo(object, metaclass=MetaFoo):
_thingy = 23
@property
def thingy(self):
return type(self).thingy
现在Foo().thingy
可以按预期对类和实例工作!_thingy
如果派生类替换了它的底层(这是最初让我参与这个搜索的用例),它也将继续做正确的事情。
这对我来说并不是 100% 满意——必须在元类和对象类中进行设置感觉违反了 DRY 原则。但后者只是一个单行调度器;我对它的存在基本没意见,如果你真的想要的话,你可以将它压缩成一个 lambda 或其他东西。