Python documentation about the is
operator说:
运算符
is
和is not
进行测试 对象标识:如果和,则x is y
为真 仅当x
和y
是同一个对象时。x is not y
产生相反的事实 值。
我们试试看:
>>> def m():
... pass
...
>>> m is m
True
Python documentation also says:
由于自动垃圾收集, 免费清单,以及动态性 描述符,你可能会注意到 某些用途中的异常行为
is
运算符,就像那些涉及的运算符一样 实例方法之间的比较, 或常数。检查他们的 文档了解更多信息。
>>> class C:
... def m():
... pass
...
>>> C.m is C.m
False
我搜索了更多解释,但我找不到任何解释。
为什么C.m is C.m
为假?
我正在使用Python 2.x.如下面的答案中所述,在Python 3.x中C.m is C.m
是真的。
答案 0 :(得分:18)
当你要求一个实例的属性是一个函数时,你得到一个 bound方法:一个可调用的对象,它包装了类中定义的函数,并将实例作为第一个参数传递。在Python 2.x中,当您要求类的属性是一个函数时,您会得到一个类似的代理对象,称为未绑定方法:
>>> class A: m = lambda: None
...
>>> A.m
<unbound method A.<lambda>>
这个特殊对象是在你提出要求时创建的,并且在任何地方都没有显示。这意味着当你做
时>>> A.m is A.m
False
您正在创建两个不同的未绑定方法对象,并对其进行身份测试。
请注意
>>> x = A.m
>>> x is x
True
和
>>> A.m.im_func is A.m.im_func
True
工作正常。 (im_func
是未绑定方法对象包装的原始函数。)
在Python 3.x中,顺便提一句,C.m is C.m
为True,因为完全删除了(有点无意义的)未绑定方法代理对象,您只需获得您定义的原始函数。
这只是Python中属性查找非常动态的一个例子:当你要求对象的属性时,可以运行任意Python来计算该属性的值。这是另一个例子,你的测试失败了,为什么会更清楚:
>>> class ChangingAttribute(object):
... @property
... def n(self):
... self._n += 1
... return self._n
...
... def __init__(self):
... self._n = 0
...
>>> foo = ChangingAttribute()
>>> foo.n
1
>>> foo.n
2
>>> foo.n
3
>>> foo.n is foo.n
False
>>> foo.n
6
答案 1 :(得分:6)
我假设您使用的是Python 2?在Python 3中,C.m is C.m
(但C().m is C().m
仍然是假的)。如果您在REPL中输入C.m
,我打赌您会看到<UnboundMethod... >
之类的内容。除了检查isinstance(self, cls)
之外,UnboundMethod包装器的功能很少。 (为此创建一个包装器似乎没有意义吗?它是,所以它在Python 3中被删除 - C.m
只是一个函数)。每当访问该方法时,都会按需创建一个新的包装器实例 - C.m
创建一个,另一个C.m
创建另一个。由于它们是不同的实例,C.m is not C.m
。
绑定方法密切相关,允许您执行f = obj.method; f(*args)
但也会导致instance.method is not instance.method
。在实例化时,类中定义的所有函数(读取:所有方法,当然除了monkeypatched之外)都成为实例的属性。当您访问它们时,您将获得普通函数周围的包装器(绑定方法)的新实例。这个包装器会记住实例(self
),当用(arg1, arg2, ..., argN)
调用时,只需将这些操作添加到函数中 - 添加self
作为第一个参数。您通常不会注意到因为您立即调用该方法 - 但这是允许隐式传递self
而不诉诸语言级欺骗的原因。
有关详细信息,请参阅the history of Python,以及历史记录。
答案 2 :(得分:4)
因为C.m()不是C类的静态方法:
试试这样:
class C:
@staticmethod
def m():
pass
print C.m is C.m
# True
c = C()
print c.m is C.m
# True
因为静态方法就像类变量一样,我们只想要一个引用它们,这样如果我们改变它们的绑定值,这个改变应该在这个类的所有类和实例中是自动的。
另一方面,在您的示例中,C.m
不是静态方法,因此Python假设它应该被视为非静态方法,因此每当您调用C.m
时,它将返回一个新实例:
class C:
def m():
pass
a = C.m
b = C.m
print id(a), id(b)
# 43811616, 43355984
print a is b
# False
N.B:静态方法不像类方法!