- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我偶然发现了非常奇怪的 python 3 问题,我不明白其原因。
我想通过检查它们的所有属性是否相等来比较我的对象。
一些子类的字段包含对绑定(bind)到自身的方法的引用 - 这会导致 RecursionError
这是 PoC:
class A:
def __init__(self, field):
self.methods = [self.method]
self.field = field
def __eq__(self, other):
if type(self) != type(other):
return False
return self.__dict__ == other.__dict__
def method(self):
pass
first = A(field='foo')
second = A(field='bar')
print(first == second)
在 python 3 中运行上面的代码会引发 RecursionError
,我不确定为什么。 A.__eq__
似乎是用来比较保存在self.methods
中的函数。所以我的第一个问题是——为什么? 为什么调用对象的 __eq__
来比较该对象的绑定(bind)函数?
第二个问题是 - 我应该在 __dict__
上使用什么样的过滤器来保护 __eq__
免受这个问题的影响?我的意思是 - 在self.method
上面的 PoC 简单地保存在一个列表中,但有时它可能在另一个结构中。过滤必须包括所有可能包含自引用的容器。
一个澄清:我确实需要将 self.method
函数保留在 self.methods
字段中。这里的usecase类似于unittest.TestCase._cleanups
——测试完成后要调用的一堆方法。该框架必须能够运行以下代码:
# obj is a child instance of the A class
obj.append(obj.child_method)
for method in obj.methods:
method()
另一个说明:我唯一可以更改的代码是 __eq__
实现。
最佳答案
“为什么调用对象的 __eq__
来比较该对象的绑定(bind)函数?”:
因为绑定(bind)方法通过以下算法进行比较:
self
是否相等?第 1 步导致无限递归;在比较 __dict__
时,它最终会比较绑定(bind)的方法,为此,它必须再次将对象相互比较,现在你又回到了起点,它永远持续下去。
我能随手想出的唯一“解决方案”是:
reprlib.recursive_repr
装饰器(这将是非常 hacky,因为你会根据是否重新输入 __eq__
来启发式地确定你是否正在比较与绑定(bind)方法相关的原因),或者 self
的相等性测试。绑定(bind)方法的包装器至少并不糟糕。您基本上只需制作一个简单的表单包装器:
class IdentityComparableMethod:
__slots__ = '_method',
def __new__(cls, method):
# Using __new__ prevents reinitialization, part of immutability contract
# that justifies defining __hash__
self = super().__new__(cls)
self._method = method
return self
def __getattr__(self, name):
'''Attribute access should match bound method's'''
return getattr(self._method, name)
def __eq__(self, other):
'''Comparable to other instances, and normal methods'''
if not isinstance(other, (IdentityComparableMethod, types.MethodType)):
return NotImplemented
return (self.__self__ is other.__self__ and
self.__func__ is other.__func__)
def __hash__(self):
'''Hash identically to the method'''
return hash(self._method)
def __call__(self, *args, **kwargs):
'''Delegate to method'''
return self._method(*args, **kwargs)
def __repr__(self):
return '{0.__class__.__name__}({0._method!r})'.format(self)
然后在存储绑定(bind)方法时,将它们包装在该类中,例如:
self.methods = [IdentityComparableMethod(self.method)]
您可能想让 methods
本身通过额外的魔法来强制执行此操作(因此它只存储函数或 IdentityComparableMethod
),但这是基本思想。
其他答案解决了更有针对性的过滤问题,这只是一种使过滤变得不必要的方法。
性能说明:我没有大量优化性能; __getattr__
是反射(reflect)底层方法所有属性的最简单方式。如果你想让比较进行得更快,你可以在初始化时取出__self__
,直接缓存在self
上,避免调用__getattr__
,改变__slots__
和 __new__
声明:
__slots__ = '_method', '__self__'
def __new__(cls, method):
# Using __new__ prevents reinitialization, part of immutability contract
# that justifies defining __hash__
self = super().__new__(cls)
self._method = method
self.__self__ = method.__self__
return self
这在比较速度上有很大的不同;在本地 %timeit
测试中,first == second
比较从 2.77 μs 下降到 1.05 μs。如果你愿意,你也可以缓存 __func__
,但由于它是回退比较,所以根本不太可能被检查(而且你会减慢构建一个你不太可能进行优化的提示使用)。
或者,您可以手动为 __self__
和 __func__
定义 @property
,而不是缓存,这比原始属性慢(比较在 1.41 μs 内运行),但根本不会产生构建时间成本(因此,如果从未运行过比较,则无需支付查找成本)。
关于python - 设计 __eq__ 来比较 self 和 other 的 __dict__ 免受 RecursionError 的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56277404/
根据 this page , set.intersection 使用 __eq__ 方法测试元素是否相等。谁能向我解释为什么这会失败? >>> Class Foo(object): >>> d
所以下面的内置类有一个 __eq__ 属性,(我假设)这就是为什么你可以测试它们的实例是否相等: >>> 1.2.__eq__( 1.2 ) True >>> 1.2 == 1.2 True >>>
在我正在处理的问题中,数据标识符的形式为 scope:name,既是 scope 又是 name 字符串. name 由点分隔的不同部分,如 part1.part2.part3.part4.part5
我有一组对象,并且有兴趣从该组中获取特定对象。经过一番研究,我决定使用此处提供的解决方案:http://code.activestate.com/recipes/499299/ 问题是它似乎不起作用。
我有一个父数据类和一个子数据类继承第一个类。我重新定义了__eq__()父数据类中的方法。但是当我比较对象子数据类时,它不使用 __eq__()父数据类中定义的方法。为什么会发生这种情况?我该如何解决
我正在用 Python 编写 DSL,我想重载运算符以便能够轻松编写 DSL 的表达式。例如,我想编写 Var("a") + Var("b") 并获得 Add(Var("a"), Var("b") 的
如果我像这样在 Python 2.7 中定义一个对象: class C(object): def __eq__(self, other): return object.__eq
目前,我正在研究 Python 的内置类型。我很困惑调用什么方法来检查键是否在字典中。例如,如果我检查 int 类型的键是否在字典中,则仅当 dictionary.keys() 包含它时,才会在后台调
我读过这个(来自 here ): User-defined classes have __eq__() and __hash__() methods by default; with them, al
在将功能移植到程序的 Python 3.1 分支时,我遇到了一个奇怪的错误。我将其缩小为以下假设: 与 Python 2.x 相比,在 Python 3.x 中,如果对象具有 __eq__ 方法,则它
我有一个类型,其中相等比较没有意义。明确比较此类型的两个实例的引用或值相等性将指示调用代码中的逻辑错误。 定义 __eq__ 引发异常是不是很糟糕?这有什么陷阱吗?它是否作为某些常见操作的一部分被隐式
我有一个Python程序,其中有一个名为Vector的类,并且该类内部有一个空列表,该列表正在运行时填充。这是初始化: def __init__(self,n): self.vector =
以下代码给出错误信息: class Test(object): def __init__(self, test = 0): self.test = test if __
我还有一个问题要问你。 我有一个带有列表“元信息”的 python 类。此列表包含我的类可能 包含的变量名称。我编写了一个 __eq__ 方法,如果 self 和 other 具有来自 metainf
我正在处理一个集合类,我想为其创建一个 __eq__ 方法。事实证明,它比我想象的要微妙得多,而且我注意到内置集合类的工作方式有几个错综复杂的地方。 真正对我帮助最大的是一个很好的例子。在标准库或任何
假设我有以下程序: class A(object):
为类实现自定义相等函数时,首先检查身份是否有意义?一个例子: def __eq__(self, other): return (self is other) or (other criteri
当我尝试重写魔术方法 __eq__,并使用 super 访问 object 中的基本方法时,出现错误.这不可能是一个错误,但感觉确实像一个错误: class A(object): def __
我遇到了一个问题,我将一个实例添加到一个集合中,然后进行测试以查看该对象是否存在于该集合中。我已经重写了 __eq__() 但在包含测试期间它没有被调用。我是否必须改写 __hash__()?如果是这
在将功能移植到我的程序的 Python 3.1 分支时,我遇到了一个奇怪的错误。我将其缩小到以下假设: 与 Python 2.x 相比,在 Python 3.x 中,如果一个对象具有 __eq__ 方
我是一名优秀的程序员,十分优秀!