6

我已经习惯了 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

我做错了什么?当我调用 a.__contains __(1) 时,一切都很顺利。我什至尝试在 A 中定义一个 __iter __ 方法,以使 A 看起来更像一个可迭代的,但它没有帮助。我在这里错过了什么?

4

1 回答 1

18

特殊方法,例如__contains__仅在类上定义时才特殊,而不是在实例上定义(Python 2 中的遗留类除外,无论如何都不应该使用)。

所以,在课堂上做你的代表团:

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__(并确保它是内置的 or staticmethod,而不是普通的 Python 函数,当然,除非在某些不同的情况下您确实希望它self在实例上调用时接收)。

顺便说一下,这个类in的实例上的操作符比以前提出的任何解决方案都要——或者至少我用我通常的信任来测量(直接去,而不是遵循正常的查找涉及继承和描述符的路由,减少了一些开销)。BlackMagic-mtimeitbuilt-in method

一个自动执行self.__class__每个实例想法的元类并不难编写(它可以在生成的类的__new__方法中完成繁琐的工作,并且如果在实例上分配,也可以将所有魔术名称设置为实际分配给类,或者通过__setattr__或许多许多属性)。但这只有在对这个特性的需求真的很普遍的情况下才是合理的(例如,将一个巨大的古老的 Python 1.5.2 项目移植到现代 Python,包括 Python 3,该项目自由地使用“每个实例的特殊方法”)。

推荐“聪明”还是“黑魔法”解决方案?不,我不这样做:几乎总是以简单、直接的方式做事更好。但是“几乎”在这里是一个重要的词,很高兴手头有这样先进的“钩子”,用于可能实际需要使用它们的罕见但并非不存在的情况。

于 2009-06-20T20:42:37.457 回答