<p>其他答案中的mixin方法很好,而且可能在大多数情况下更好。但无论如何,它破坏了部分乐趣-也许迫使你有单独的星球等级-像必须生活在两个抽象类的祖先“可摧毁”和“不可摧毁”。在</p>
<h2>第一种方法:描述符修饰符</h2>
<p>但是Python有一种强大的机制,叫做“描述符协议”,用于从类或实例中检索任何属性,甚至通常用于从实例中检索方法,因此,可以通过检查方法检索是否“应该属于”该类来定制方法检索,否则会引发属性错误。在</p>
<p>描述符协议规定,每当您试图从Python中的实例对象获取任何属性时,Python将检查该属性是否存在于该对象的类中,如果存在,则检查该属性本身是否具有名为<code>__get__</code>的方法。如果有,则调用<code>__get__</code>(使用实例和类将其定义为参数),并且返回的是属性。Python使用它来实现方法:python3中的函数有一个<code>__get__</code>方法,当调用该方法时,它将返回另一个可调用对象,而当调用该对象时,它将在对原始函数的调用中插入<code>self</code>参数。在</p>
<p>因此,可以创建一个类,该类的<code>__get__</code>方法将决定是否将函数作为绑定方法返回,这取决于外部类是否标记为So—例如,它可以检查特定的标志<code>non_destrutible</code>。这可以通过使用decorator用描述符功能包装方法来完成</p>
<pre><code>class Muteable:
def __init__(self, flag_attr):
self.flag_attr = flag_attr
def __call__(self, func):
"""Called when the decorator is applied"""
self.func = func
return self
def __get__(self, instance, owner):
if instance and getattr(instance, self.flag_attr, False):
raise AttributeError('Objects of type {0} have no {1} method'.format(instance.__class__.__name__, self.func.__name__))
return self.func.__get__(instance, owner)
class Planet:
def __init__(self, name=""):
pass
@Muteable("undestroyable")
def destroy(self):
print("Destroyed")
class BorgWorld(Planet):
undestroyable = True
</code></pre>
<p>在交互式提示下:</p>
^{pr2}$
<p>请注意,与简单地重写方法不同,此方法在检索属性时会引发错误,甚至会使<code>hasattr</code>工作:</p>
<pre><code>In [113]: hasattr(BorgWorld(), "destroy")
Out[113]: False
</code></pre>
<p>但是,如果试图直接从类中而不是从实例中检索方法,那么它将不起作用——在这种情况下,<code>instance</code>参数被设置为None,而且我们不能说它是从哪个类中检索的——只需要声明它的<code>owner</code>类。在</p>
<pre><code>In [114]: BorgWorld.destroy
Out[114]: <function __main__.Planet.destroy>
</code></pre>
<h2>第二种方法:<code>__delattr__</code>在元类上:</h2>
<p>在写上述内容时,我突然想到Pythn确实有<code>__delattr__</code>特殊方法。如果<code>Planet</code>类本身实现了<code>__delattr__</code>,并且我们试图删除特定派生类上的<code>destroy</code>方法,它将无法工作:<code>__delattr__</code>禁止实例中属性的属性删除-如果您试图在实例中<code>del</code>调用“destroy”方法,它无论如何都会失败,因为该方法在类中。在</p>
<p>然而,在Python中,类本身就是它的“元类”的一个实例。通常是<code>type</code>。“Planet”元类上的一个适当的<code>__delattr__</code>可以通过发出一个'del'来实现“destroy”方法的“deshinity”不可摧毁的飞机。摧毁“课后创作。在</p>
<p>同样,我们使用描述符协议来拥有一个适当的“删除子类的方法”:</p>
<pre><code>class Deleted:
def __init__(self, cls, name):
self.cls = cls.__name__
self.name = name
def __get__(self, instance, owner):
raise AttributeError("Objects of type '{0}' have no '{1}' method".format(self.cls, self.name))
class Deletable(type):
def __delattr__(cls, attr):
print("deleting from", cls)
setattr(cls, attr, Deleted(cls, attr))
class Planet(metaclass=Deletable):
def __init__(self, name=""):
pass
def destroy(self):
print("Destroyed")
class BorgWorld(Planet):
pass
del BorgWorld.destroy
</code></pre>
<p>使用此方法,即使尝试检索或检查类本身存在的方法也会起作用:</p>
<pre><code>In [129]: BorgWorld.destroy
...
AttributeError: Objects of type 'BorgWorld' have no 'destroy' method
In [130]: hasattr(BorgWorld, "destroy")
Out[130]: False
</code></pre>
<h2>具有自定义<code>__prepare__</code>方法的元类</h2>
<p>由于元类允许自定义包含类名称空间的对象,因此可以在类主体中添加一个响应<code>del</code>语句的对象,并添加一个<code>Deleted</code>描述符。在</p>
<p>对于使用此元类的用户(程序员),它几乎是相同的,但是对于<code>del</code>语句,它被允许进入类主体本身:</p>
<pre><code>class Deleted:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
raise AttributeError("No '{0}' method on class '{1}'".format(self.name, owner.__name__))
class Deletable(type):
def __prepare__(mcls,arg):
class D(dict):
def __delitem__(self, attr):
self[attr] = Deleted(attr)
return D()
class Planet(metaclass=Deletable):
def destroy(self):
print("destroyed")
class BorgPlanet(Planet):
del destroy
</code></pre>
<p>(“deleted”描述符是将方法标记为“deleted”的正确形式-但是在这个方法中,它在类创建时无法知道类名)</p>
<h2>作为班级装饰师:</h2>
<p>如果使用“deleted”描述符,您可以简单地将要删除的方法作为类装饰器删除-不需要f或者本例中的元类:</p>
<pre><code>class Deleted:
def __init__(self, cls, name):
self.cls = cls.__name__
self.name = name
def __get__(self, instance, owner):
raise AttributeError("Objects of type '{0}' have no '{1}' method".format(self.cls, self.name))
def mute(*methods):
def decorator(cls):
for method in methods:
setattr(cls, method, Deleted(cls, method))
return cls
return decorator
class Planet:
def destroy(self):
print("destroyed")
@mute('destroy')
class BorgPlanet(Planet):
pass
</code></pre>
<h2>修改<code>__getattribute__</code>机制:</h2>
<p>为了完整性起见,真正让Python访问超级类上的方法和属性的是<code>__getattribute__</code>调用中发生的事情。n在<code>object</code>版本的<code>__getattribute__</code>中,对属性检索的具有“数据描述符、实例、类、基类链等”优先级的算法进行编码。在</p>
<p>因此,对类进行更改是一个简单的独特点,可以获得“合法”的属性错误,而不需要前面的方法中使用的“不存在”描述符。在</p>
<p>问题是<code>object</code>的<code>__getattribute__</code>没有使用<code>type</code>的一个来搜索类中的属性-如果这样做了,只在元类上实现{<cd24>}就足够了。必须在实例上这样做以避免方法的实例查找,而在元类上这样做可以避免元类查找。当然,元类可以注入所需的代码:</p>
<pre><code>def blocker_getattribute(target, attr, attr_base):
try:
muted = attr_base.__getattribute__(target, '__muted__')
except AttributeError:
muted = []
if attr in muted:
raise AttributeError("object {} has no attribute '{}'".format(target, attr))
return attr_base.__getattribute__(target, attr)
def instance_getattribute(self, attr):
return blocker_getattribute(self, attr, object)
class M(type):
def __init__(cls, name, bases, namespace):
cls.__getattribute__ = instance_getattribute
def __getattribute__(cls, attr):
return blocker_getattribute(cls, attr, type)
class Planet(metaclass=M):
def destroy(self):
print("destroyed")
class BorgPlanet(Planet):
__muted__=['destroy'] # or use a decorator to set this! :-)
pass
</code></pre>