为什么`instance_of_object.foo是instance_of_object.foo`评估为False?

时间:2018-05-23 23:19:48

标签: python introspection

如果我有

class A:
  def foo(self):
    pass

这评估为True

getattr(A, 'foo') is A.foo

但评估结果为False

a = A()
getattr(a, 'foo') is a.foo

一样
a.foo is a.foo

为什么?

我发现getattr(a, 'foo')a.foo都由

表示
<bound method A.foo of <__main__.A object at 0x7a2c4de10d50>>)

所以没有提示......

3 个答案:

答案 0 :(得分:3)

至少在CPython中,绑定方法是作为类method的实例实现的。每次询问绑定函数的值时,都会得到此类的 new 实例。

x = a.foo
y = a.foo

assert x is not y
id(x)  # E.g. 139664144271304
id(y)  # E.g. 139664144033992
type(x)  # <class 'method'>
type(y)  # <class 'method'>

所有这个类都存储对实例和未绑定函数的引用,当您调用该类时,它会使用存储的实例(以及其他参数)调用未绑定函数。 / p>

未绑定的函数(如A.foo)只是常规的旧函数 - 没有构造新的代理类实例,因此身份可以按预期工作。

这种差异的原因是a.foo的语义含义取决于两个事物,a的值和A.foo的值。为了能够在以后的任何时间点获得此含义,需要存储这两个值。这就是method类的作用。

相反,A.foo的含义仅取决于单个值:A.foo。因此,不需要额外的工作来存储任何东西,并且使用值本身。

您可能会考虑预先分配绑定方法实例的想法,以便a.foo始终返回相同的不可变对象 - 但考虑到Python的动态特性,每个构造一个新的更简单,更便宜时间,即使它们可能是相同的。

答案 1 :(得分:3)

添加到@GManNickG回答:

getattr(a, 'foo').__func__ is a.foo.__func__ 

将返回True

答案 2 :(得分:2)

存储在类中的某些对象是descriptors,它们不遵循对象查找的常规规则。您在示例中处理的CaseUtils.toCamelCase("camel_case", false, new char[]{'_'}); // returns "camelCase" 方法是一个(函数对象是描述符)。

描述符是定义foo(以及可选的__get____set__)方法的类的实例。这些方法控制当你在存储的类的实例上查找desciptor时会发生什么。

我认为一个例子可以说明这一点:

__delete__

class DescriptorClass: def __get__(*args): print("__get__ was called") return "whatever" class OtherClass: descriptor_instance = DescriptorClass() # the descriptor instance is a class variable other_instance = OtherClass() # this calls the descriptor's __get__ method, which prints "__get__ was called" result = other_instance.descriptor_instance print(result) # will print "whatever", since that's what the __get__ method returned 方法每次调用时都不需要返回相同的内容。事实上,它通常不会。在函数被用作描述符(即方法)的特定情况下,每次查看函数时都会创建一个新的“绑定方法”对象。因此,__get__运算符不会将多个绑定方法看作同一个对象,即使它们可能将同一个函数绑定到同一个实例。