Why does `class X: mypow = pow` work? What about `self`?

1.3k views Asked by At

This works and happily prints 81:

class X:
    mypow = pow

print(X().mypow(3, 4))

But why? Isn't the method given the extra "self" argument and should be utterly confused?

For comparison, I also tried it with my own Pow function:

def Pow(x, y, z=None):
    return x ** y

class Y:
    myPow = Pow

print(Pow(3, 4))
print(Y().myPow(3, 4))

The direct function call prints 81 and the method call crashes as expected, as it does get that extra instance argument:

Python 3:  TypeError: unsupported operand type(s) for ** or pow(): 'Y' and 'int'
Python 2:  TypeError: unsupported operand type(s) for ** or pow(): 'instance' and 'int'

Why/how does Pythons own pow work here? The documentation didn't help and I couldn't find the source.

3

There are 3 answers

0
tynn On BEST ANSWER

This behavior is connected to method binding. Have a look at what Python tells you about these functions/methods:

>> pow
<built-in function pow>
>>> X.mypow
<built-in function pow>
>>> X().mypow
<built-in function pow>

and

>>> Pow
<function Pow at 0x7f88f5715f50>
>>> Y.myPow
<unbound method Y.Pow>
>>> Y().myPow
<bound method Y.Pow of <__main__.Y instance at 0x7f88f57117e8>>

Further the documentation states:

Class dictionaries store methods as functions. In a class definition, methods are written using def and lambda, the usual tools for creating functions. [...]

To support method calls, functions include the __get__() method for binding methods during attribute access. This means that all functions are non-data descriptors which return bound or unbound methods depending whether they are invoked from an object or a class. [...]

But built-in functions don't have a __get__() method. That's why pow wasn't bound and could be used the way you observed, while Pow couldn't.

4
AudioBubble On

This is because python functions defined in C (builtins) have auto handled self argument. Here is pow function header :

static PyObject * math_pow(PyObject *self, PyObject *args) Here you can see that self is always passed by interpreter.

0
LiuXiMin On

Well, @tynn has pointed the reason that pow does not have __get__, so it can not be transformed to bound method.

But why? The key thing behind all of these things is descriptor protocol. If anybody wants to understand Python better, then should go to understand descriptor protocol, which is not hard, but very simple and powerful.