繁体   English   中英

为什么方法与自身不同?

[英]Why is a method not identical to itself?

关于is运算符Python文档说:

运营商isis not测试对象的身份: x is y为真,当且仅当xy是同一个对象。 x is not y产生反真值。

让我们尝试一下:

>>> def m():
...   pass
... 
>>> m is m
True

Python文档还说

由于自动进行垃圾回收,释放列表以及描述符的动态性质,您可能会注意到在is运算符的某些用法中,看似不寻常的行为,例如涉及实例方法或常量之间比较的行为。 查看他们的文档以获取更多信息。

>>> class C:
...   def m():
...     pass
... 
>>> C.m is C.m
False

我搜索了更多的解释,但找不到任何解释。

为什么Cm is Cm错误?

我正在使用Python2.x。 如以下答案中所述,在Python 3.x Cm is Cm为true。

当您要求一个函数实例的属性时,您将获得一个绑定方法 :一个可调用对象,该对象包装了在类中定义的函数,并将实例作为第一个参数传递。 在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中, Cm is Cm为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

我假设您正在使用Python 2? 在Python 3中, Cm is Cm (但C().m is C().m仍为false)。 如果您在REPL上仅输入Cm ,我敢打赌您会看到类似<UnboundMethod... > 除了检查isinstance(self, cls)之外,UnboundMethod包装器几乎没有做。 (似乎没有必要为此创建包装器?是的,因此将其丢弃在Python 3中Cm只是一个函数)。 每当访问该方法时,都会按需创建一个新的包装实例Cm创建一个,另一个Cm创建另一个。 由于它们是不同的实例,因此Cm is not Cm

绑定方法密切相关,使您可以执行f = obj.method; f(*args) f = obj.method; f(*args)但也会导致instance.method is not instance.method 实例化后,该类中定义的所有函数(阅读:所有方法,除了猴子修补的方法外)均成为实例的属性。 当您访问它们时,您将获得围绕纯函数的包装器的新实例(绑定方法)。 该包装器会记住实例( self ),并在使用(arg1, arg2, ..., argN)进行调用时,将其交给函数-将self作为第一个参数添加。 您通常不会注意到,因为您立即调用了该方法-但这是允许隐式传递self而不诉诸于语言级技巧的原因。

有关更多详细信息和历史,请参见Python的历史。

因为Cm()不是类C的静态方法,所以:

像这样尝试:

class C:
    @staticmethod
    def m():
        pass

print C.m is C.m
# True

c = C()
print c.m is C.m
# True

因为静态方法就像类变量,所以我们只需要一个引用即可,因此,如果我们更改其绑定值,则该更改应该在该类的所有类和实例中都是自动的。

另一方面,在您的示例中, Cm不是静态方法,因此Python假定应该将其视为非静态方法,因此无论何时调用Cm ,它都会返回一个新实例:

class C:
   def m():
      pass

a = C.m
b = C.m

print id(a), id(b)
# 43811616, 43355984
print a is b
# False

注意:静态方法不像类方法!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM