在 Python 中模拟成员资格测试:正确地将 __contains__ 委托给包含对象
我已经习惯了 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 看起来更像一个可迭代的,但它没有帮助。 我在这里错过了什么?
I am used to that Python allows some neat tricks to delegate functionality to other objects. One example is delegation to contained objects.
But it seams, that I don't have luck, when I want to delegate __contains __:
class A(object):
def __init__(self):
self.mydict = {}
self.__contains__ = self.mydict.__contains__
a = A()
1 in a
I get:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'A' is not iterable
What I am making wrong? When I call a.__contains __(1), everything goes smooth. I even tried to define an __iter __ method in A to make A more look like an iterable, but it did not help. What I am missing out here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
诸如 __contains__ 之类的特殊方法仅在类上定义时才特殊,而不是在实例上定义(Python 2 中的遗留类除外,无论如何您都不应该使用它们)。
因此,在类级别进行委托:
我实际上更喜欢将后者拼写为
return other in self.mydict
,但这是一个小风格问题。编辑:如果“完全动态的每个实例重定向特殊方法”(如提供的旧式类)是必不可少的,那么用新式类实现它并不难:你只需要每个具有如此特殊需求的实例必须包装在其自己的特殊类中。 例如:
本质上,在使用一点点黑魔法将
self.__class__
重新分配给一个新的类对象之后(其行为与前一个类似,但有一个空字典,除了这个之外没有其他实例self
),在旧式类中的任何位置,您将分配给self.__magicname__
,改为分配给self.__class__.__magicname__
(并确保它是内置或静态方法,不是普通的 Python 函数,除非在某些不同的情况下您确实希望它在实例上调用时接收 self)。顺便说一句,这个
BlackMagic
类实例上的in
运算符实际上比之前提出的任何解决方案更快 --或者至少我用我常用的值得信赖的-mtimeit
进行测量(直接进入内置方法
,而不是遵循涉及继承和描述符的正常查找路线,剃须一点开销)。编写一个元类来自动化每个实例的 self.__class__ 的想法并不难(它可以在生成的类的 __new__ 方法中完成肮脏的工作,也许也可以)如果在实例上分配,则将所有魔术名称设置为在类上实际分配(通过
__setattr__
或许多许多属性)。 但只有当对此功能的需求确实广泛时(例如,将一个巨大的古老 Python 1.5.2 项目移植到现代 Python(包括 Python 3)中,该项目自由地使用“每个实例的特殊方法”),这才是合理的。我推荐“聪明”或“黑魔法”的解决方案吗? 不,我不这么认为:几乎总是以简单、直接的方式做事会更好。 但“几乎”在这里是一个重要的词,很高兴手头有这样先进的“钩子”,用于罕见但并非不存在的情况,在这些情况下,它们的使用实际上是有必要的。
Special methods such as
__contains__
are only special when defined on the class, not on the instance (except in legacy classes in Python 2, which you should not use anyway).So, do your delegation at class level:
I'd actually prefer to spell the latter as
return other in self.mydict
, but that's a minor style issue.Edit: if and when "totally dynamic per-instance redirecting of special methods" (like old-style classes offered) is indispensable, it's not hard to implement it with new-style classes: you just need each instance that has such peculiar need to be wrapped in its own special class. For example:
Essentially, after the little bit of black magic reassigning
self.__class__
to a new class object (which behaves just like the previous one but has an empty dict and no other instances except this oneself
), anywhere in an old-style class you would assign toself.__magicname__
, assign toself.__class__.__magicname__
instead (and make sure it's a built-in orstaticmethod
, not a normal Python function, unless of course in some different case you do want it to receive theself
when called on the instance).Incidentally, the
in
operator on an instance of thisBlackMagic
class is faster, as it happens, than with any of the previously proposed solutions -- or at least so I'm measuring with my usual trusty-mtimeit
(going directly to thebuilt-in method
, instead of following normal lookup routes involving inheritance and descriptors, shaves a bit of the overhead).A metaclass to automate the
self.__class__
-per-instance idea would not be hard to write (it could do the dirty work in the generated class's__new__
method, and maybe also set all magic names to actually assign on the class if assigned on the instance, either via__setattr__
or many, many properties). But that would be justified only if the need for this feature was really widespread (e.g. porting a huge ancient Python 1.5.2 project that liberally use "per-instance special methods" to modern Python, including Python 3).Do I recommend "clever" or "black magic" solutions? No, I don't: almost invariably it's better to do things in simple, straightforward ways. But "almost" is an important word here, and it's nice to have at hand such advanced "hooks" for the rare, but not non-existent, situations where their use may actually be warranted.