Antonio
Antonio

Reputation: 39

What is happening behind the scenes when calling object.method() and Class.method(object)?

I am pretty new to Python and am tackling OOP. I am a bit confused as to when to use calls to methods and classes. The sample code below outputs the average, however I am curious as to when you would use calling from the Class vs methods from a real-world perspective. I'm pretty sure this is just something that I may have yet to tackle, but it's just been a bit of a confusion as to when I would use one over the other.

class Student:
    def __init__(self, new_name, new_grades):
        self.name = new_name
        self.grades = new_grades

    def average(self):
        return sum(self.grades) / len(self.grades)

student_one = Student('Test User', [70, 88, 90, 99])

# object.method()
print(student_one.average())

# Class.method(object)
print(Student.average(student_one))

Upvotes: 2

Views: 309

Answers (1)

wim
wim

Reputation: 362557

In your example, there is no difference. Use the first way. The second way makes it appear as though you need to use "the back door" for some reason, for example if student_one was not actually a Student instance but you wanted to specifically call the average method of the Student class.

If an experienced Python developer were to read your code, seeing Student.average(student_one) may make them pause for a moment and wonder why the author wants to use the unbound average here. It would be an unusual style, and perhaps could imply that there may be something more subtle happening than there really was - a stumbling block.

For what's going on behind the scenes, the first way uses a bound method and the second way just uses a normal function.

>>> Student.average
<function __main__.Student.average(self)>
>>> student_one.average 
<bound method Student.average of <__main__.Student object at 0xcafef00d>>

A bound method is just a function which is bound to an instance, via descriptor protocol*, and the instance ("self") is passed as the first positional argument implicitly:

>>> student_one.average.__self__ is student_one 
True
>>> student_one.average.__func__ is Student.average 
True

By using the function on the class and passing in the instance explicitly, you essentially do the same thing as an invocation of the descriptor does automatically.

For a deeper understanding of what a method is, there's detailed documentation of this binding process in the docs here.

* Just a fancy way of saying via the "." i.e. the dotted attribute access

Upvotes: 4

Related Questions