为什么一个方法与其自身不相同?

发布于 2024-10-11 06:17:44 字数 935 浏览 10 评论 0原文

关于 is 运算符的 Python 文档 说:

运算符 isis not 测试 对象标识:x is y 为 true,如果且 仅当 xy 是同一对象时。 x 不是 y 产生相反的事实 值。

让我们尝试一下:

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

Python 文档还指出

由于自动垃圾收集, 空闲列表和动态特性 描述符,你可能会注意到 某些用途中的异常行为 is 运算符,例如涉及 实例方法之间的比较, 或常数。检查他们的 文档以获取更多信息。

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

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

为什么Cm is Cm 是假的?

我正在使用 Python 2.x。正如下面的答案中所指出的,在 Python 3.x 中 Cm is Cm 是正确的。

The Python documentation about the is operator says:

The operators is and is not test for
object identity: x is y is true if and
only if x and y are the same object.
x is not y yields the inverse truth
value.

Let's try that:

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

The Python documentation also says:

Due to automatic garbage-collection,
free lists, and the dynamic nature of
descriptors, you may notice seemingly
unusual behaviour in certain uses of
the is operator, like those involving
comparisons between instance methods,
or constants. Check their
documentation for more info.

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

I searched for more explanations, but I was not able to find any.

Why is C.m is C.m false?

I am using Python 2.x. As noted in the answers below, in Python 3.x C.m is C.m is true.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

老街孤人 2024-10-18 06:17:44

当您请求一个作为函数的实例属性时,您会得到一个绑定方法:一个可调用对象,它包装类中定义的函数并将实例作为第一个参数传递。在 Python 2.x 中,当您请求一个作为函数的类属性时,您会得到一个类似的代理对象,称为未绑定方法

>>> class A:
...     def m():
...         return None
... 
>>> A.m
<unbound method A.m>

这个特殊对象是在您请求时创建的,并且显然不是缓存在任何地方。这意味着当您这样做时,

>>> 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 中属性查找的动态特性的一个示例:当您请求对象的属性时,可以运行任意代码来计算该属性的值。这是您的测试失败的另一个示例,其中原因更清楚:

>>> 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

When you ask for an instance attribute which is a function, you get a bound method: a callable object which wraps the function defined in the class and passes the instance as the first argument. In Python 2.x, when you ask for a class attribute which is a function, you get a similar proxy object called an unbound method:

>>> class A:
...     def m():
...         return None
... 
>>> A.m
<unbound method A.m>

This special object is created when you ask for it, and not apparently cached anywhere. That means that when you do

>>> A.m is A.m
False

you are creating two distinct unbound method objects and testing them for identity.

Notice that these work fine:

>>> x = A.m
>>> x is x
True

and

>>> A.m.im_func is A.m.im_func
True

(im_func is the original function which the unbound method object is wrapping.)

In Python 3.x, incidentally, C.m is C.m is True, because the (somewhat pointless) unbound method proxy objects were removed entirely and you just get the original function which you defined.


This is just one example of the very dynamic nature of attribute lookup in Python: when you ask for an attribute of an object, it is possible to run arbitrary code to calculate the value of that attribute. Here's another example where your test fails in which it is much clearer why:

>>> 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
躲猫猫 2024-10-18 06:17:44

我假设你使用的是Python 2?在 Python 3 中,Cm is Cm(但 C().m is C().m 仍然为 false)。如果您在 REPL 中仅输入 Cm,我敢打赌您会看到类似于 的内容。 UnboundMethod 包装器除了检查 isinstance(self, cls) 之外几乎不做任何事情。 (为此创建一个包装器似乎毫无意义?确实如此,所以它在 Python 3 中被删除 - Cm 只是一个函数)。每当访问该方法时,就会按需创建一个新的包装器实例 - Cm 创建一个,另一个 Cm 创建另一个。由于它们是不同的实例,Cm 不是 Cm

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

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

I assume you are using Python 2? In Python 3, C.m is C.m (but C().m is C().m is still false). If you enter just C.m at the REPL, I bet you see something like <UnboundMethod... >. An UnboundMethod wrapper does very little, except checking isinstance(self, cls). (Seems pretty pointless to create a wrapper for this? It is, so it was dropped in Python 3 - C.m is just a function). A fresh wrapper instance is created on-demand whenever the method is accessed - C.m creates one, another C.m creates another one. Since they're different instances, C.m is not C.m.

Closely related are the bound methods, which allow you to do f = obj.method; f(*args) but also cause instance.method is not instance.method. Upon instanciation, all functions defined in the class (read: all methods, except of course monkeypatched ones) become properties of the instance. When you access them, you instead get a fresh instance of a wrapper (the bound method) around the plain function. This wrapper remembers the instance (self) and when called with (arg1, arg2, ..., argN) just hands these on to the function - with self added as first argument. You usually don't notice because you call the method right away - but this is what allows passing self implicitly without resorting to language-level trickery.

See the history of Python for more details and, well, history.

我最亲爱的 2024-10-18 06:17:44

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

请像这样尝试:

class C:
    @staticmethod
    def m():
        pass

print C.m is C.m
# True

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

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

另一方面,在您的示例中,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

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

Because C.m() is not a static method of the class C:

Try it like this:

class C:
    @staticmethod
    def m():
        pass

print C.m is C.m
# True

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

Because static methods are like class variables, we only want one reference for them so that if we change their bound value this change should be automatic in all the class and instance of this class.

On the other hand, in your example, C.m is not a static method so Python makes the assumption that it should be treated like a non-static method, so whenever you call C.m, it will return a new instance:

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: static methods are not like class methods!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文