我想创建一个可以在in
语句中使用的类,并将条件传递给__init__
中的对象。一个例子:
class Set:
def __init__(self, contains):
self.__contains__ = contains # or setattr; doesn't matter
top = Set(lambda _: True)
bottom = Set(lambda _: False)
问题是 top 中的 3
返回 TypeError: argument of type 'Set' is not iterable
,即使 top.__contains__(3)
按预期返回 True
。
更重要的是,如果我这样修改代码:
class Set:
def __init__(self, contains):
self.__contains__ = contains
def __contains__(self, x):
return False
top = Set(lambda _: True)
, 3 in top
将返回 False
,而 top.__contains__(3)
按预期返回 True
,再次。
这里发生了什么?我使用的是 Python 3.9.2。
(注意: data model 中的其他方法也会发生同样的情况,例如 __gt__
、__eq__
等)
最佳答案
这是因为魔术方法是在类上查找的,而不是在实例上查找的。解释器在执行“可重载”操作时绕过了通常的属性获取机制。
似乎是这样,因为它最初是如何在 CPython 中实现的,例如因为 type slots工作(不是 __slots__
插槽,这是另一回事):+
或 *
或其他运算符如何作用于值由其类决定,不是基于每个实例。
这样做有一个性能优势:查找 dunder 方法可能涉及字典查找,或更糟糕的是,使用 __getattr__
/__getattribute__
进行一些动态计算。不过,我不知道这是否是造成这种情况的主要原因。
我找不到详细的书面描述,但有 a talk by Armin Ronacher on YouTube对此进行了相当深入的研究。
关于python - 无法用 lambda 覆盖 __contains__,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72451109/