在Python中模拟成员资格测试:正确地将__contains__委托给包含对象

时间:2009-06-20 20:39:06

标签: python containers delegation emulation iterable

我习惯于Python允许一些巧妙的技巧将功能委托给其他对象。一个例子是委托给包含的对象。

但它接缝,我没有运气,当我想委托__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看起来像一个可迭代的,但它没有帮助。我错过了什么?

1 个答案:

答案 0 :(得分:18)

__contains__之类的特殊方法只有在类上定义时才是特殊的,而不是在实例上定义的(Python 2中的遗留类除外,你不应该 使用它)。< / p>

那么,你的代表团是否在班级:

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__重新分配给一个新的类对象(其行为与前一个类似,但有一个空的dict,除了这个self之外没有其他实例),您将分配给self.__magicname__的旧式课程中的任何位置,而是分配给self.__class__.__magicname__(并确保它是内置的或staticmethod,而不是普通的Python函数,除非当然在某些不同情况下,您确实希望它在实例上调用时接收self

顺便提一下,此in类实例上的BlackMagic运算符更快,与之前提出的任何解决方案相比,或者至少所以我用我平时可靠的-mtimeit进行测量(直接进入built-in method,而不是遵循涉及继承和描述符的正常查找路由,削减一些开销。)

自动执行self.__class__ - 每个实例的元类的元类并不难编写(它可以在生成的类的__new__方法中执行脏工作,也可能将所有魔术名称设置为实际上,如果在实例上分配,则通过__setattr__或许多许多属性进行分配。但只有在对这个功能的需求真正普及的情况下才能证明这一点(例如,将一个庞大的古老Python 1.5.2项目移植到现代Python中,包括Python 3)。

推荐“聪明”或“黑魔法”解决方案吗?不,我不这样做:几乎总是以简单,直接的方式做事情。但是“几乎”在这里是一个重要的词,并且很高兴能够获得这种先进的“钩子”,用于罕见但并非不存在的情况,在这种情况下,它们的使用实际上可能是有保证的。