17

我有一个错误,我在使用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
4

2 回答 2

26

每次访问它们时都会创建方法对象。函数充当描述符,在调用其方法时返回一个方法对象.__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 方法类型则按__self__标识进行比较。请参阅Python 问题 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
于 2013-04-12T17:54:31.873 回答
0

Martijn 是正确的,新方法是由生成的对象,.__get__因此它们的地址指针不等同于is评估。请注意, using==将按照 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,方法运算符的行为通常与 the 相同,==因此在此示例中您将获得预期的行为。这是__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
于 2013-04-12T17:57:21.367 回答