如何测试函数的平等性或身份?

jac*_*ack 17 python

我希望能够测试两个可调用对象是否相同.我更喜欢身份语义(使用"是"运算符),但我发现当涉及方法时,会发生不同的事情.

#(1) identity and equality with a method
class Foo(object):
    def bar(self):
        pass
foo = Foo()
b = foo.bar
b == foo.bar    #evaluates True. why?
b is foo.bar    #evaluates False. why?
Run Code Online (Sandbox Code Playgroud)

我用Python 2.7和3.3(CPython)重现了这一点,以确保它不是旧版本的实现细节.在其他情况下,身份测试按预期工作(翻译会议从上面继续):

#(2) with a non-method function
def fun(self):
    pass
f = fun
f == fun    #evaluates True
f is fun    #evaluates True

#(3) when fun is bound as a method 
Foo.met = fun
foo.met == fun    #evaluates False
foo.met is fun    #evaluates False

#(4) with a callable data member
class CanCall(object):
    def __call__(self):
        pass
Foo.can = CanCall()
c = foo.can
c == foo.can    #evaluates True
c is foo.can    #evaluates True
Run Code Online (Sandbox Code Playgroud)

根据问题,Python如何区分作为类成员的回调函数?,当绑定为方法时,函数被包装.这是有道理的,并且与上面的情况(3)一致.

是否有一种可靠的方法将方法绑定到其他名称,然后让它们比较可调用对象或普通函数?如果"=="成功,那该怎么办?为什么"=="和"是"在上面的情况(1)中表现不同?

编辑

正如@Claudiu指出的那样,为什么没有方法的答案有参考平等?也是这个问题的答案.

use*_*ica 10

Python没有foo.bar为每个foo类实例保留规范对象Foo.相反,在Python求值时会创建一个方法对象foo.bar.从而,

foo.bar is not foo.bar
Run Code Online (Sandbox Code Playgroud)

至于==事情变得混乱.Python有大量的方法对象类型,具体取决于方法是用Python实现还是用C语言实现的几种方法之一.这些方法对象类型的响应方式==不同:

  • 对于用Python编写==的方法,比较方法__func____self__属性,如果方法对象表示由同一个函数实现并绑定到相等对象的方法而不是同一个对象,则返回True .因此,x.foo == y.foo如果是x == yfoo用Python编写的话将是True .
  • 对于大多数"特殊"方法(__eq__,__repr__等等),如果它们是用C实现的,那么Python会比较__self__类似于内部的东西__func__,如果方法具有相同的实现并且绑定到相等的对象,则再次返回True.
  • 对于在C中实现的其他方法,Python执行您实际期望的操作,如果方法对象表示同一对象的相同方法,则返回True.

因此,如果您运行以下代码:

class Foo(object):
    def __eq__(self, other):
        return True if isinstance(other, Foo) else NotImplemented
    def foo(self):
        pass

print Foo().foo == Foo().foo
print [].__repr__ == [].__repr__
print [].append == [].append
Run Code Online (Sandbox Code Playgroud)

你得到以下奇怪的输出:

True
True
False
Run Code Online (Sandbox Code Playgroud)

您很可能不需要身份语义,因为许多对象可以表示相同的方法.你也不应该依赖于plain ==方法,因为语义是如此混乱,通常是无用的,并且如果方法在C中被重写,则容易改变.幸运的是,你可能处理的所有方法对象类型都有一个__self__属性表示他们受约束的对象,所以

meth1.__self__ is meth2.__self__ and meth1 == meth2
Run Code Online (Sandbox Code Playgroud)

应该是比较两个方法对象是否表示同一对象的相同方法的一般方法.