sun qingyao sun qingyao - 4 months ago 6
Python Question

Avoiding creating new method object

According to the Python 2.7.12 Documentation,


When the attribute is a user-defined method object, a new method
object is only created if the class from which it is being retrieved
is the same as, or a derived class of, the class stored in the
original method object; otherwise, the original method object is
used as it is.



I'm trying to understand this paragraph by writing code:

# Parent: The class stored in the original method object
class Parent(object):
# func: The underlying function of original method object
def func(self):
pass
func2 = func

# Child: A derived class of Parent
class Child(Parent):
# Parent.func: The original method object
func = Parent.func

# AnotherClass: Another different class, neither subclasses nor subclassed
class AnotherClass(object):
# Parent.func: The original method object
func = Parent.func

a = Parent.func
b = Parent.func2
c = Child.func
d = AnotherClass.func

print b is a
print c is a
print d is a


The output is:

False
False
False


I expect it to be:

False
False
True

Answer

Every time you write Parent.func, you are getting a new unbound method object. So AnotherClass.func never changes, but Parent.func does. You can see this if you do something like this:

a = Parent.func
a2 = Parent.func
b = Parent.func2
c = Child.func
d = AnotherClass.func
d2 = AnotherClass.func

Then:

>>> a is a2
False
>>> d is d2
True

So your comments referring to "the original method object" are somewhat misleading. Each part of your code that evaluates Parent.func is getting a different method object. (Note that the quotation you gave does not apply to Parent.func, because func as written in Parent is not a method object; it is a function object. It is only when you access the attribute by writing Parent.func that you get a method object.)

I'm adding a bit here to clarify the quotation you gave.

I think you are misunderstanding what that quotation is saying. This may be because the quotation is somewhat vague about the distinction between "the attribute" and the value retrieved by getting that attribute.

Let me clarify by separating two notions. In an expression like something.attr I'll use "the raw value" to refer to the actual value stored in the class/object dictionary, i.e., the value of something.__dict__['attr']. I'll use "the evaluated value" to refer to the actual result of evaluating something.attr. The difference between the two is that the descriptor protocol is activated on the raw value to get the evaluated value.

The documentation you quoted is describing a situation where the raw value is a method object. This is the case in your definitions of Child and AnotherClass. The quotation does not apply to func inside Parent, because in that case the raw value is not a method; it is a function. In that case, the following paragraph of the documentation applies:

When a user-defined method object is created by retrieving a user-defined function object...

In other words, the documentation you quoted ("When the attribute is a user-defined method object") applies to cases like this:

obj = [a method object]

class Foo(object):
    attr = obj

Your quoted documentation does not apply to the following case. In this case, the attribute is "a user-defined function object":

func = [a function object]

class Foo(object):
    attr = func

In your example, there is no "original method object". You can only get a method object after the class is defined. The "original" object is a function object. When you write func in the class body, you are defining a function; it is only when you access Parent.func that you get a method object.

Comments