1

If one changes a object attribute which refers to an internal method to an external function after the object is created, self is not passed to the new function, even if the attribute (which contains a function) is called like before.

class Person:
    def greet(self):
        print("hello")

    do = greet

def wave(person):
    print("bye")

alice = Person()
alice.do() #prints 'hello'

#change do to point an external function
alice.do = wave
alice.do() #Error: Missing argument

The exact error which I get is:

hello
Traceback (most recent call last):
  File "C:\Users\Owner\Desktop\miniLiveMethodSwitch.py", line 15, in <module>
    alice.do() #Error: Missing argument
TypeError: wave() missing 1 required positional argument: 'person'

If one move the external function into the class as a internal method,

class Person:
    def greet(self):
        print("hello")

    def wave(person):
        print("bye")

    do = greet

Then the code works as expected. Why isn't self passed once the attribute is changed to refer to an external function? What is the proper way to call the function so that self is passed?

2 Answers2

4

When Python finds alice.do in the class dict, it invokes the descriptor protocol, calling the function's __get__ method to produce a bound method object that will be the result of the alice.do lookup. This bound method object knows what self should be and will automatically insert the self argument into the argument list when forwarding arguments to the underlying function. (This happens positionally, not by the self name.)

When Python finds alice.do in alice's instance dict, the descriptor protocol is not invoked, and alice.do is just the raw function. self is not injected.

user2357112 supports Monica
  • 215,440
  • 22
  • 321
  • 400
0

Try this:

class Person:
    def greet(self):
        print("hello")

    do = greet

def wave(person):
    print("bye")

alice = Person()
alice.do() #prints 'hello'
print(type(alice.do))

#change do to point an external function
alice.do = wave
print(type(alice.do))

The output is: method and then function:

hello
<class 'method'>
<class 'function'>
Cory Madden
  • 4,123
  • 17
  • 32