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

3

7

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.

Pegu answered 7/6, 2015 at 12:38 Comment(5)
Same way it gets passed to my Pow? @PadraicCunninghamPegu
That's because the __self__ attribute of builtin functions is not writeable, hence it is always None for pow.Bus
@vaultah Try in Python 2.Bus
@AshwiniChaudhary Thanks, I'm looking into __self__ now, didn't know that yet.Pegu
__self__ on built-in functions is not as documentedBus
C
3

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.

Confidant answered 7/6, 2015 at 14:30 Comment(0)
C
5

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.

Cirrose answered 7/6, 2015 at 12:54 Comment(4)
Isn't that math.pow, not pow?Pegu
It's math.pow, but behavior is same.Cirrose
Hmm, I checked and confirm it does behave the same in that regard (i.e., happily returns 81.0). I found the source of that and it just ignores the self argument. I guess I'd need to know how user-defined methods don't ignore it to really understand it. AshwiniChaudhary also commented something about builtin's __self__ not writable, which I confirmed, but I guess that shouldn't matter if it's not used anyway. I'll look a bit into both issues.Pegu
The problem is user defined methods behave different way when compared to C methods. Each python C method must have defined self, and it's filled always with something (current object, global etc.). User methods don't have that option, so that means when used way you have shown, first argument will be filled with self, but when used as usual, it will not.Cirrose
C
3

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.

Confidant answered 7/6, 2015 at 14:30 Comment(0)
P
0

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.

Passenger answered 26/11, 2019 at 1:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.