2009-06-20 55 views
6

我习惯于Python允许一些巧妙的技巧将功能委托给其他对象。一个例子是委托包含的对象。在Python中模拟成员测试:正确委托__contains__包含对象

但它接缝,我没有运气,当我想委托__contains __:

class A(object): 
    def __init__(self): 
     self.mydict = {} 
     self.__contains__ = self.mydict.__contains__ 

a = A() 
1 in a 

我得到:

Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
TypeError: argument of type 'A' is not iterable 

我所作出错误的?当我调用.__包含__(1)时,一切都很顺利。我甚至试图在A中定义一个__iter __方法来使A看起来更像一个迭代,但它没有帮助。我在这里错过了什么? (在Python 2除了在传统的班,你应该反正用)在类中定义的时候,而不是在实例

回答

18

特殊的方法,如__contains__只有特殊。

所以,做你的代表团在类级别:

class A(object): 
    def __init__(self): 
     self.mydict = {} 

    def __contains__(self, other): 
     return self.mydict.__contains__(other) 

其实我更喜欢拼写后者作为return other in self.mydict,但是这是一个轻微的风格问题。如果当“完全动态的每个实例重定向特殊方法”(如提供的旧式类)不可或缺时,用新式类实现它并不难:你只需要每个实例这种特殊需要被包装在自己的特殊类别中。例如:

class BlackMagic(object): 
    def __init__(self): 
     self.mydict = {} 
     self.__class__ = type(self.__class__.__name__, (self.__class__,), {}) 
     self.__class__.__contains__ = self.mydict.__contains__ 

从本质上讲,之后的黑魔法重新分配self.__class__到一个新的类对象的点点(其行为就像前面的一个,但有一个空的字典和除这一个self没有其他情况下),任何地点在旧式课程中,您将分配给self.__magicname__,而不是分配给self.__class__.__magicname__(并确保它是内置的或staticmethod,不是普通的Python函数,除非在某些不同的情况下,您确实希望它在接收self时收到self调用实例)。

顺便说一句,in运营商在这个BlackMagic类的一个实例是更快,因为它发生,比任何先前提出的解决方案 - 或者至少使我和我平时的信赖-mtimeit(去测量直接到built-in method,而不是遵循正常的查找路由涉及继承和描述符,削减了一点开销)。

自动执行self.__class__ -per实例想法并不难写一个元(它可以做在生成的类的__new__方法肮脏的工作,也许还设置所有魔法的名字实际上对类分配,如果分配在实例上,通过__setattr__或许多,许多属性)。但只有当这个特性的需求非常普遍时(例如,移植一个巨大的古代Python 1.5.2项目,它可以自由地将“每实例特殊方法”用于现代Python,包括Python 3),这才是合理的。

我是否推荐“聪明”还是“黑魔法”解决方案?不,我不这样做:几乎总是以简单,直接的方式来做事情会更好。但“差不多”在这里是一个重要的词,很高兴有这样的先进的“钩子”为罕见的,但并非不存在的情况下,他们的使用可能实际上是有保证的。

+0

好的,这对我做了一些解释,但对我来说并不完全令人满意,因为这种类型的授权需要额外的时间(我可以在旧式类中保存 - 为什么我不应该使用它们?)。 – Juergen 2009-06-20 20:49:45