There are many steps that beginner tutorials do not cover, so I will attempt to be brief but thorough. I will try to be precise in my terminology, so you can look up all the sections you are unclear about.
In general, methods in Python are functions in the class object. All functions are descriptors. Part of what being a descriptor means is that when you access a method through the instance of a class, it creates a closure that automatically passes the instance you created it on as the self
parameter. For example, if Cars
had a method start(self)
in addition to __init__
, then tesla.start
would be a "bound method", which is a closure that passes tesla
as self
to Cars.start
. Notice that I did not put parentheses after tesla.start
. Putting parentheses would actually invoke the bound method.
Second piece of information: if a class defines a __call__
special method, its instances are said to be callable. This means that you can invoke an instance as if it were a function using the ()
operator. You can see a case of this when you do tesla = Cars(...)
. Here Cars
is a class object, but you are calling it as if it were a function. We are now getting close to where self
actually gets passed in to __init__
.
Thirdly, pretty much everything in Python is an object and obeys the general rules you know for objects, like being created from a class, etc. This includes functions and classes. A class object is created from another class, which is appropriately named a metaclass. Normally metaclasses are a can of worms you don't want to open, so we will scratch just enough of the surface here and no more. The most common metaclass is type
: 99%1 of all class objects you will encounter as a beginner as instances of type
. type
defines a __call__
method, which is what you are invoking when you do Cars(...)
, since Cars
is an instance of type
.
type.__call__(Cars, ...)
does a couple of things. First it calls Cars.__new__(Cars, ...)
. This returns the new instance that you will later end up assigning to tesla
or ford
or whatever. Then, if the thing that __new__
returned is an instance of Cars
, it will call Cars.__init__(self, ...)
, where self
is that new instance it just created.
And that's how self
gets passed to __init__
. Keep in mind that all the steps can be customized or overridden, so this is really just a basic overview of the simplest case.
The links in this text should get you started in more specific research. All the links are completely distinct, even when they are for the same term. All the links are to Stack Exchange sites (SO with one exception), or the official Python 3 documentation, with one exception.
1 I made up that statistic, but it's probably right anyway.
Class
tutorial. SO is not a tutorial site. – Ruthenianobject.__call__(Cars, ...)
, which callsCars.__new__
and passes the result to__init__
, and the other question is not really about that. – Ministranttype.__call__
, notobject._call__
. my mistake. – Ministrant