Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arguments to an object's parent's function

Tags:

python

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?

like image 482
yl_elm_city Avatar asked Jul 14 '26 11:07

yl_elm_city


1 Answers

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.

like image 100
mpolednik Avatar answered Jul 23 '26 08:07

mpolednik