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

Jue*_*gen 6 python containers iterable delegation emulation

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

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

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

a = A()
1 in a
Run Code Online (Sandbox Code Playgroud)

我明白了:

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

我错了什么?当我打电话给.__包含__(1)时,一切顺利.我甚至试图在A中定义一个__iter __方法,使A看起来像一个可迭代的,但它没有帮助.我在这里错过了什么?

Ale*_*lli 18

特殊的方法,如__contains__在类中定义的时候,而不是在实例(除在Python 2遗产类,你应该仅仅是特殊的无论如何使用).

那么,你的代表团是否在课堂上:

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

    def __contains__(self, other):
       return self.mydict.__contains__(other)
Run Code Online (Sandbox Code Playgroud)

我实际上更喜欢拼写后者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__
Run Code Online (Sandbox Code Playgroud)

基本上,在一小部分黑魔法重新分配self.__class__给一个新的类对象(它的行为与前一个类似,但有一个空的dict,除了这个之外没有其他实例self)之后,你将分配给旧的类self.__magicname__,以self.__class__.__magicname__代替(并确保它是一个内置的或staticmethod不正常的Python的功能,当然,除非在一些不同的情况下你希望它收到self的实例调用时).

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

用于自动化self.__class__-per-instance构思的元类并不难写(它可以在生成的类的__new__方法中执行脏工作,并且如果在实例上分配,也可以通过设置所有魔术名称实际分配给类__setattr__或许多很多属性).但是这将是正义的,如果需要这个功能真的很普遍(如移植是自由的使用"每个实例特殊的方法"现代的Python,包括Python 3一个巨大的古老的Python 1.5.2项目).

推荐 "聪明"或"黑魔法"解决方案吗?不,我不这样做:几乎总是以简单,直接的方式做事情.但"几乎"是一个重要的词在这里,它是很好的手边有这样先进的"钩子"为罕见,但并非不存在,他们的使用实际上可能是必要的情况下.

  • "不惜一切代价表现"并不是一个合理的目标:"过早优化是编程中所有邪恶的根源"(Knuth,引用Hoare).现代风格的类功能更丰富,更通用,更规则,更简单(参见"果壳中的Python"第103页的底部,您可以在Google图书搜索中阅读 - 搜索"每个实例的方法" ).在Python 3及更高版本中,遗留类已经永远消失了 - 不要将代码绑定到它们以节省小纳秒的小相关性.顺便说一下,更快的是来自dict的_inherit_(这也给你一个新式的课程). (6认同)