Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overwritten Methods don't contain self?

Here is an example I just played through on my machine:

$ python
Python 2.7.4 (default, Apr 19 2013, 18:28:01) 
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
# just a test class 
>>> class A(object):
...   def hi(self):
...     print("hi")
... 
>>> a = A()
>>> a.hi()
hi
>>> def hello(self):
...   print("hello")
... 
>>> 
>>> hello(None)
hello
>>> 
>>> 
>>> 
>>> a.hi = hello
# now I would expect for hi to work the same way as before
# and it just prints hello instead of hi.
>>> a.hi()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: hello() takes exactly 1 argument (0 given)
>>> 
>>> def hello():
...   print("hello")
... 
# but instead this one works, which doesn't contain any
# reference to self
>>> a.hi = hello
>>> a.hi()
hello
>>> 
>>> 
>>> 
>>> 
>>> a.hello = hello
>>> a.hello()
hello

What is happening here? Why doesn't the function get the parameter self, when it is used as a method? What would I need to do, to get a reference to self inside it?

like image 220
erikbstack Avatar asked Dec 20 '22 04:12

erikbstack


2 Answers

A method in a class referenced via an instance is bound to that instance, in your case:

In [3]: a.hi
Out[3]: <bound method A.hi of <__main__.A object at 0x218ab10>>

Compare to:

In [4]: A.hi
Out[4]: <unbound method A.hi>

So, to achieve the effect you probably want, do

In [5]: def hello(self):
   ...:     print "hello"
   ...:     

In [6]: A.hi = hello

In [7]: a.hi()
hello

Beware - this will apply to all instances of A. But if you want to override a method on one instance only, do you really need to pass self?

like image 80
Koterpillar Avatar answered Dec 29 '22 11:12

Koterpillar


It is the way functions are accessed when they are class attributes.

A function added as a class attribute gets accessed as a descriptor. You see that if you do

class A(object):
    pass

def f(*a): pass
A.f = f
print f
print A.f
print A().f

Here, you get the output

<function f at 0x00F03D70>
<unbound method A.f>
<bound method A.f of <__main__.A object at 0x00F089D0>>

The same output you'd get with

print f
print f.__get__(None, A)
print f.__get__(A(), A)

because that's the way how descriptors work.

All this - the transformation from a function to a method via the descriptor protocol - does not happen on instance attributes.

If you do

a = A()
a.f = f

then a.f is as well read back as a function, not as a method. Thus, you should consider this at the time you do the assignment and rather do

a.f = lambda: f(a)

in order to pass a to the function.

like image 30
glglgl Avatar answered Dec 29 '22 11:12

glglgl