【问题标题】:Why don't methods have reference equality?为什么方法没有引用相等性?
【发布时间】:2013-04-05 08:40:29
【问题描述】:

我有一个错误,我在使用 is 时依赖于彼此相等的方法。事实证明并非如此:

>>> class What:
...     def meth(self):
...         pass

>>> What.meth is What.meth
True
>>> inst = What()
>>> inst.meth is inst.meth
False

为什么会这样?它适用于常规功能:

>>> def func(): pass
>>> func is func
True

【问题讨论】:

  • 这适用于 Python 3 顺便说一句。
  • @poke no,What.meth is What.meth 有效(返回 True)但 inst = What(); inst.meth is inst.meth 在 Python 3.8.0 上返回 False

标签: python methods identity equality


【解决方案1】:

每次访问它们时都会创建方法对象。函数充当descriptors,在调用其.__get__ 方法时返回一个方法对象:

>>> What.__dict__['meth']
<function What.meth at 0x10a6f9c80>
>>> What.__dict__['meth'].__get__(What(), What)
<bound method What.meth of <__main__.What object at 0x10a6f7b10>>

如果您使用的是 Python 3.8 或更高版本,则可以改用 == 相等测试。在 Python 3.8 及更高版本上,如果 .__self__.__func__ 属性是相同的对象,则两个方法是相等的(因此,如果它们包装相同的函数,并绑定到相同的实例,都使用 is 进行测试)。

在 3.8 之前,方法 == 的行为根据方法的实现方式不一致 - Python 方法和两种 C 方法类型之一比较 __self__ 是否相等而不是标识,而另一个 C 方法类型比较 @987654332 @按身份。见Python issue 1617161

如果您需要测试这些方法是否代表相同的底层函数,请测试它们的__func__ 属性:

>>> What.meth == What.meth     # functions (or unbound methods in Python 2)
True
>>> What().meth == What.meth   # bound method and function
False
>>> What().meth == What().meth # bound methods with *different* instances
False
>>> What().meth.__func__ == What().meth.__func__ # functions
True

【讨论】:

  • 如果我使用==,我会得到我正在寻找的身份平等行为吗?
  • 我认为方法相等性检查.im_self 的身份,而不是相等性。 check it out
  • @Claudiu:是的,对不起,它会测试im_self 是否相同。
  • @Claudiu Claudiu, Martijn :您的意思是,在 cmets 中,该方法相等性检查 im_self AND im_func 的身份,不是吗?这就是在编辑中更正的一点,不是吗?
  • 是的,两者都经过身份测试;函数只有在它们无论如何都相同时才相等。
【解决方案2】:

Martijn 是正确的,新方法是由 .__get__ 生成的对象,因此它们的地址指针不等同于 is 评估。请注意,使用 == 将按照 Python 2.7 中的预期进行评估。

Python2.7
class Test(object):
    def tmethod(self):
        pass

>>> Test.meth is Test.meth
False
>>> Test.meth == Test.meth
True

>>> t = Test()
>>> t.meth is t.meth
False
>>> t.meth == t.meth
True

但是请注意,从实例引用的方法并不等同于从类引用的方法,因为自引用与来自实例的方法一起携带。

>>> t = Test()
>>> t.meth is Test.meth
False
>>> t.meth == Test.meth
False

在 Python 3.3 中,方法的 is 运算符的行为通常与 == 相同,因此在此示例中您将获得预期的行为。这是由于__cmp__ 消失和 Python 3 中更清晰的方法对象表示造成的;方法现在有 __eq__ 并且引用不是构建在运行中的对象,因此行为会按照人们可能预期的方式进行,而不需要 Python 2 的期望。

Python3.3
>>> Test.meth is Test.meth
True
>>> Test.meth == Test.meth
True
>>> Test.meth.__eq__(Test.meth)
True

【讨论】:

  • 您的分析已关闭。您在 Python 3 中观察到的变化与 is 无关;它们是由于 Python 3 中未绑定方法对象的消失。Test.meth 现在只是您定义的原始函数对象,而不是动态创建的未绑定方法对象。
  • 啊,是的,我添加了一些澄清细节以包括未绑定对象问题。
猜你喜欢
  • 2018-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-23
  • 2020-03-18
相关资源
最近更新 更多