Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
135 views
in Technique[技术] by (71.8m points)

python - Why is this false? `SomeClass.method is SomeClass.method`

Take this code for example:

class SomeClass():
    def a_method(self):
        pass

print(SomeClass.a_method is SomeClass.a_method)     # Example 1: False
print(SomeClass.a_method == SomeClass.a_method)     # Example 2: True
print(SomeClass().a_method is SomeClass().a_method) # Example 3: False
print(SomeClass().a_method == SomeClass().a_method) # Example 4: False
  • Example 1: I would have guessed that they were the same object. Does Python make a copy of the method each time it is referenced?
  • Example 2: Expected.
  • Example 3: Expected, since they are different objects.
  • Example 4: Why doesn't this output match example 2?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Example 1:

Someclass.a_method is an unbound method. These don't even exist in Python nowadays, so consider this a useless history lesson.

Does Python make a copy of the method each time it is referenced?

Yes, more or less. This is done via descriptor protocol.

>>> SomeClass.a_method  # unbound method via attribute access
<unbound method SomeClass.a_method>
>>> SomeClass.__dict__['a_method']  # just stored as a function in the class dict
<function __main__.a_method>
>>> SomeClass.__dict__['a_method'].__get__(None, SomeClass)
<unbound method SomeClass.a_method>

The last line is showing the "binding" operation that descriptors invoke for attribute access on a class, but written out manually. In pure Python, it's something like this

class Function(object):
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        return types.MethodType(self, obj, objtype):

You can also create a bound method that way:

>>> some_instance = SomeClass()
>>> SomeClass.__dict__['a_method'].__get__(some_instance, SomeClass)
<bound method SomeClass.a_method of <__main__.SomeClass instance at 0xcafef00d>>

Example 2:

Method comparison is done via the __func__ and __self__ attributes on the methods. In this case, they are both identical: the __func__ is the same plain old function you can dig out of the class dict, and the __self__ is None. So despite these methods being different objects, they compare equal.

Example 3:

Correct. They are different objects, and hence not identical.

Example 4:

As mentioned earlier, comparison is using the __func__ and __self__ attributes. The result doesn't match example 2 because, in this case, the __self__ attributes are referring to different instances. Those different instances don't compare equal because SomeClass instances compare by identity, therefore the methods also don't compare equal.

A final note on the current version of Python

Everything mentioned above applies to the current version of the language, too, except for Example 1. In Python, there is no longer such thing as an unbound method, this unnecessary complication in the object model was removed.

>>> SomeClass.a_method
<function __main__.SomeClass.a_method(self)>
>>> SomeClass.a_method is SomeClass.__dict__['a_method']
True

What was an "unbound method" in Python 2 is now just a plain old function, and the instance retrieved via attribute access is identical to the object in the class dict. The Example 1 result changes from False to True in a Python 2 -> Python 3 upgrade.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...