在Python中模拟成员资格测试:正确地将__contains__委托给包含对象
-
06-07-2019 - |
题
我习惯于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看起来像一个可迭代的,但它没有帮助。我错过了什么?
解决方案
__ contains __
等特殊方法只有在类上定义时才是特殊的,而不是在实例上定义(Python 2中的遗留类除外,你应该不使用它们)。
那么,你的代表团是否在班级:
class A(object):
def __init__(self):
self.mydict = {}
def __contains__(self, other):
return self.mydict.__contains__(other)
我实际上更喜欢将后者拼写为在self.mydict
中返回其他内容,但这是一个次要的样式问题。
修改:if和when“完全动态的每个实例重定向特殊方法” (就像提供的旧式类一样)是必不可少的,用新式类实现它并不困难:你只需要每个具有这种特殊需要的实例都包含在它自己的特殊类中。例如:
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
。 / p>
顺便提一下,此 BlackMagic
类的实例上的
运算符更快,因为它发生了,而不是之前提出的任何一个解决方案 - 或者至少我用我通常可靠的 -mtimeit
进行测量(直接转到内置方法
,而不是遵循涉及继承的正常查找路由和描述符,削减了一些开销。)
自动化 self .__ class __
-per-instance idea的元类并不难写(它可以在生成的类的 __ new __
方法中执行脏工作,并且如果在实例上分配,也可以通过 __ setattr __
或许多许多属性)将所有魔术名称设置为在类上实际分配。但只有在对这个功能的需求真正普及的情况下才能证明这一点(例如,将一个庞大的古代Python 1.5.2项目移植到现代Python,包括Python 3),这个项目大量使用“每个实例特殊方法”。
我推荐“聪明”吗?或“黑魔法”解决方案?不,我不这样做:几乎总是以简单,直接的方式做事情。但是“几乎”这里是一个重要的词,手头有这样的高级“钩子”,这很好。对于罕见但并非不存在的情况,实际上可能需要使用它们。