我遇到了类似的困惑这个问题,在我自己回答之后,为了繁荣,在这里报告我的发现似乎是谨慎的。
正如 ThiefMaster 已经指出的那样,“所有者”参数使诸如classproperty
. 有时,您希望类将方法屏蔽为非方法属性,并且使用owner
参数允许您使用普通描述符来做到这一点。
但这只是问题的一半。至于“只读”问题,这是我发现的:
我首先在这里找到了答案:http: //martyalchin.com/2007/nov/23/python-descriptors-part-1-of-2/。起初我不明白,我花了大约五分钟的时间才把头绕过去。最终说服我的是举了一个例子。
考虑最常见的描述符:property
. 让我们使用一个简单的示例类,它有一个属性 count,即变量 count 被访问的次数。
class MyClass(object):
def __init__(self):
self._count = 0
@property
def count(self):
tmp = self._count
self._count += 1
return tmp
@count.setter
def setcount(self):
raise AttributeError('read-only attribute')
@count.deleter
def delcount(self):
raise AttributeError('read-only attribute')
正如我们已经建立owner
的,函数的参数__get__
意味着当你在类级别访问属性时,__get__
函数会拦截getattr调用。碰巧的是,当在类级别访问时,代码property
只是简单地返回属性本身,但它可以做任何事情(比如返回一些静态值)。
__set__
现在,想象一下如果以同样的__del__
方式工作会发生什么。除了实例级别之外,and 方法将在类级别拦截__set__
所有__del__
和setattr
调用。delattr
因此,这意味着 的“计数”属性MyClass
实际上是不可修改的。如果您习惯于使用 Java 等静态编译语言进行编程,这似乎不是很有趣,因为您无法修改应用程序代码中的类。但在 Python 中,你可以。类被视为对象,您可以动态分配它们的任何属性。例如,假设MyClass
它是第三方模块的一部分,并且MyClass
几乎完全适合我们的应用程序(假设除了 for 的代码之外还有其他代码count
),只是我们希望 count 方法的工作方式有所不同。相反,我们希望它始终为每个实例返回 10。我们可以做到以下几点:
>>> MyClass.count = 10
>>> myinstance = MyClass()
>>> myinstance.count
10
如果__set__
截获对 的调用setattr(MyClass, 'count')
,则无法实际更改 MyClass. 相反, for 的代码setcount
会拦截它并且无法对它做任何事情。唯一的解决方案是编辑 MyClass 的源代码。(我什至不确定你是否可以在子类中覆盖它,因为我认为在子类中定义它仍然会调用setattr
代码。但我不确定,因为我们已经在这里处理了反事实,我不真的没有办法测试它。)
现在,您可能会说,“这正是我想要的!我故意不希望我的用户重新分配我的类的属性!” 对此,我只能说你想要的东西是不可能使用幼稚的描述符的,我会指导你上面的推理。允许重新分配类属性更符合当前的 Python 习惯用法。
如果你真的,真的想创建一个只读的类属性,我想我不会告诉你怎么做。但如果有解决方案,它可能涉及使用元类并创建property
元类或修改元类的setattr和delattr代码。但这是深度魔法,远远超出了这个答案的范围(以及我自己对 Python 的能力)。