In the "Dog" class, if I initialize as
self.print_func = print_func
shown below
class Pet(object):
def __init__(self, name, print_func):
self.name = name
self.print_func = print_func
class Dog(Pet):
def __init__(self, name, print_func):
Pet.name = name
self.print_func = print_func
def print_name(self):
self.print_func(self.name)
def print_string(str):
print str
when I do
j.print_name()
everything is fine.
But if I initialize Dog class as
Pet.print_func = print_fun
i.e.,
class Dog(Pet):
def __init__(self, name, print_func):
Pet.name = name
Pet.print_func = print_func
def print_name(self):
self.print_func(self.name)
when I do
j.print_name()
I get this error
TypeError: print_string() takes exactly 1 argument (2 given)
So why does calling self.print_func in the second case pass the "self" argument but not in the first case?
It is because in the case of self, you're creating so called unbound function and in the second case, bound function. Bound function effectively translates to Dog.print_func(self, self.name).
Bound methods are a bit special in a sense that they always require the self argument while functions don't. They also don't exist in the instances __dict__ - which is exactly what happens: you're not assigning it to self.__dict__, but to Pet.__dict__, therefore self by Python implicitly.
In other words, setting Pet.print_func creates a method, while self.print_func creates a function.
Reason for this is the fact, that you're not calling Pet constructor - Pet.print_func modifies the parents list of methods. Small modification makes this more clear:
class Dog(Pet):
def __init__(self, name, print_func):
Pet.name = name
super(Dog, self).print_func = print_func
Output
AttributeError: 'super' object has no attribute 'print_func'
The attribute (or method) doesn't even exist! But, if the constructor is called...
class Dog(Pet):
def __init__(self, name, print_func):
super(Dog, self).__init__(name, print_func)
You are again assigning to a function. Output (given name='test'):
test
Exploring further, the behavior is becoming a bit more predictive. It can be summarized like this: if you add an attribute to a class, it is treated as a method. Adding an attribute to an instance leaves it as a function (or local attribute). Further example
class Dog(Pet):
def __init__(self, name, print_func):
Pet.name = name
super(Dog, self).__dict__['print_func'] = print_func
Output (given name='test')
test
hopefully explains it well enough.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With