Search code examples
pythonpython-class

Why calling a function inside a Python class __init__ can be both Attribute and Method?


I am quite confused with the method and attribute of a Python class. Suppose we have a Python class like this:

# case 1
class Person:

    def __init__(self, first, last):

        self.first = first
        self.last = last
        self.fun()

    def fun(self):
        value = self.first + '---' + self.last
        self.fun = value
        return value

person_1 = Person('A', 'B')  
person_1.fun

---> "A---B"

As we can see, in case_1 we initialize an instance person_1. And we can get the result we want by calling fun as an attribute. However, if we change our code to the following, fun becomes a method instead.(case_2)

# case 2
class Person:

    def __init__(self, first, last):

        self.first = first
        self.last = last
        self.fun()

    def fun(self):
        value = self.first + '---' + self.last
        return value

person_1 = Person('A', 'B')  
person_1.fun

---> <bound method Person.fun of <__main__.Person object at 0x7fd4f79168d0>>

We still contain the init process in the class. But now fun becomes a method but not an attribute. (case_2)

If we remove the self.fun() in init but keep the self.fun = value, it is still a method. (case_3)

# case 3
class Person:

    def __init__(self, first, last):

        self.first = first
        self.last = last

    def fun(self):
        value = self.first + '---' + self.last
        self.fun = value
        return value

person_1 = Person('A', 'B')  
person_1.fun

---> <bound method Person.fun of <__main__.Person object at 0x7fd4f797f390>>

Would you mind giving me some instructions about why this happens? And what is the proper way to use the function as an attribute inside a Python Class? Thank you so much in advance!


Solution

  • In case 1, your constructor calls fun() which has a line inside of it to overwrite itself with an attribute value. This is confusing and not a good thing to do because it is confusing.

    In case 2, your fun method does not include the line to overwrite itself so it doesn't get overwritten.

    In case 3, you never actually call your fun method so it never has a chance to overwrite itself. If you called it with person_1.fun() i.e. with parentheses, then it would execute and overwrite itself and from that point on, person_1.fun would be an attribute value.

    Remember that in python, a function/method ONLY executes if it is called with parentheses. If you don't express it with parentheses, then the result of evaluation is not the output of the function, but instead the expression produces a reference to the function itself, which can be put in another variable or in a data structure and called later.

    To illustrate this:

    >>> def my_func():                                      
    ...     print('got called')                             
    ...     return 42                                       
    ...                                                     
    >>> x = my_func #no parentheses, x becomes an alias to my_func                         
    >>> y = my_func()  #parentheses means actually execute                                      
    got called                                              
    >>> y                                                   
    42                                                      
    >>> x                                                   
    <function my_func at 0x765e8afdc0>                      
    >>> x()  #x is a reference to my_func and can be called itself                                               
    got called                                              
    42                                                      
    >>>