替换代码0】、
staticmethod
以及普通方法如何被查询/绑定的功能是通过以下方式实现的
descriptors
.同样地,我们可以定义一个描述符,即
forbids
在一个实例上进行查找/绑定。
这种描述符的天真实现会检查它是否通过一个实例来查找,在这种情况下会引发一个错误。
class NoInstanceMethod:
"""Descriptor to forbid that other descriptors can be looked up on an instance"""
def __init__(self, descr, name=None):
self.descr = descr
self.name = name
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
# enforce the instance cannot look up the attribute at all
if instance is not None:
raise AttributeError(f"{type(instance).__name__!r} has no attribute {self.name!r}")
# invoke any descriptor we are wrapping
return self.descr.__get__(instance, owner)
这可以应用于其他描述符之上,以防止它们在一个实例上被查询到。特别是,它可以与classmethod
或staticmethod
相结合,以防止在一个实例上使用它们。
class A:
def __init__(self, a):
self.a = a
@NoInstanceMethod
@classmethod
def foo(cls):
return 'hello world!'
A.foo() # Stdout: hello world!
A(1).foo() # AttributeError: 'A' object has no attribute 'foo'
上面的NoInstanceMethod
是 "天真 "的,因为它没有考虑到将__get__
以外的描述符调用传播到它的包装描述符。例如,我们可以传播__set_name__
的调用,让被包裹的描述符知道它的名字。
由于描述符可以自由地(不)实现任何描述符的方法,所以可以支持,但需要适当的错误处理。扩展NoInstanceMethod
以支持实践中需要的任何描述符方法。