Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 900
Stefan Pochmann Avatar asked Jun 07 '15 12:06

Stefan Pochmann


2 Answers

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.

like image 197
Filip Sohajek Avatar answered Oct 19 '22 06:10

Filip Sohajek


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.

like image 25
tynn Avatar answered Oct 19 '22 07:10

tynn