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

发布于 2024-07-25 06:20:22 字数 547 浏览 9 评论 0原文

我已经习惯了 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

独闯女儿国 2024-08-01 06:20:22

诸如 __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__ 重新分配给一个新的类对象之后(其行为与前一个类似,但有一个空字典,除了这个之外没有其他实例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:

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

    def __contains__(self, other):
       return self.mydict.__contains__(other)

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:

class BlackMagic(object):
    def __init__(self):
        self.mydict = {}
        self.__class__ = type(self.__class__.__name__, (self.__class__,), {})
        self.__class__.__contains__ = self.mydict.__contains__

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 one self), anywhere in an old-style class you would assign to self.__magicname__, assign to self.__class__.__magicname__ instead (and make sure it's a built-in or staticmethod, not a normal Python function, unless of course in some different case you do want it to receive the self when called on the instance).

Incidentally, the in operator on an instance of this BlackMagic 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 the built-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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文