Reputation: 4154
I could be lacking of very basic python basis. I'm handling a script made by others.
I have a script, let's say My_Class
, in which the code is:
import thing1
import thing_2
...etc
class My_Class:
def __init__(self, arg1, arg2 ...etc):
self.argument_1 = arg1
self.argument_2 = arg2
...etc
Is it correct that my_class has __init__
whose arguments are not passed as arguments to my_class? (my_class does not have arguments at all)
How can the arguments arg1, arg2 ...etc
be passed to my_class
if the class has no parameters?
Shouldn't it be
import thing1
import thing_2
...etc
class My_Class(arg1, arg2 ...etc):
def __init__(self, arg1, arg2 ...etc):
self.argument_1 = arg1
self.argument_2 = arg2
...etc
instead?
Note:
The class does not get instantiated, only its method are called, directly on the class, i.e:
My_Class(arg1, arg2 ...etc).method_1()
Upvotes: 1
Views: 1879
Reputation: 16476
I hope this information gives you a better understanding of why we can call our classes, where do those arguments go, where are the class's parameters and etc.
So, let's talk about __call__
:
Think about implementing __call__
inside your class. It enables the instance of the class to be callable in other words, "this" is the reason you can put ()
after your instance right ? And when you call your instances this way, __call__
is the actual method that is getting called ! You could pass arguments when you are calling the instance if you define them in the __call__
method as parameters.
class A:
def __call__(self, arg):
print('It is calling', arg)
instance = A()
instance('foo')
Now we know that classes themselves are instances --> of type type
:
class A:
pass
print(isinstance(A, type)) # True
So there has to be a __call__
method in "their" class(better to say metaclass) and of course there is one in type
. That's the reason we can put ()
after the class to call it and pass arguments to it.
When you put parentheses in front of you class, you are actually calling this __call__
method of the class type
with the given arguments here arg1
, arg2
...
Wait, But where is __init__
? where is __new__
? How do they get called ?
In fact they get called inside the __call__
method of the class type
with exactly those arguments you passed. First __new__
, if it returns an instance of the class then __init__
gets called like a normal instance method.
Now I'm going to demonstrate what I explained before: (The default metaclass of the all classes is type
class, but I'm going to use mine to show you the details)
class Mymeta(type):
def __call__(cls, *args, **kwargs):
print(f"Metaclass's __call__ gets called with: {args} and {kwargs}")
new_created_instance = cls.__new__(cls, *args, **kwargs)
if isinstance(new_created_instance, cls):
new_created_instance.__init__(*args, **kwargs)
return new_created_instance
class A(metaclass=Mymeta):
def __new__(cls, *args, **kwargs):
# return 10 # <-- If you uncomment this, __init__ won't get called.
return super(A, cls).__new__(cls)
def __init__(self, name, **kwargs):
print(
'This is printed only because'
' we called it inside `__call__` method of the meta class'
)
self.name = name
obj = A('soroush', age=5)
output :
Metaclass's __call__ gets called with: ('soroush',) and {'age': 5}
This is printed only because we called it inside `__call__` method of the meta class
Note two things, first, All the arguments that we passed in front of our class is passed directly to the __call__
method of the metaclass. Second, as you can see __call__
of the Mymeta
is the caller for both __new__
and __init__
.
Upvotes: 7
Reputation: 50076
TLDR: The "signature" of a class is defined by its __init__
and/or __new__
method.
The class
statement is an implicit call to the metaclass*. Any arguments of the class
statement, including the bases, are consumed by the metaclass. The metaclass is responsible to use the class name, arguments and body to create the actual class object.
# v arguments passed to the metaclass
class My_Class(arg1, arg2, ...):
...
Notably, these are the arguments passed to the metaclass. In common cases, these are just the base classes; passing or even expecting other arguments is only needed for advanced cases.
When calling a class, as in My_Class(arg1, arg2, ...)
, this passes the arguments to the class' __init__
and/or __new__
. As such, __init__
/__new__
define which parameters "a class" takes.
class My_Class:
# v parameters taken by the class
def __init__(self, arg1, arg2 ...etc):
self.argument_1 = arg1
self.argument_2 = arg2
*This is a slight simplification of how metaclasses work. There are various levels of indirection that are not needed to understand the broad picture.
Upvotes: 1
Reputation: 39354
Your first snippet is correct:
class My_Class:
def __init__(self, arg1, arg2 ...etc):
self.argument_1 = arg1
self.argument_2 = arg2
...etc
#Instantiation
c = My_Class(arg1, arg2 ...etc)
#Call method on instance
c.method_1()
You definitely are instantiating the class: My_Class(arg1, arg2 ...etc)
is interpreted by python as:
__init__()
on that object (with arg1, arg2 ...etc in your case)In python
the __init__()
method is considered synonymous with the term 'constructor' used in other languages.
Also note that in my snippet above the variable c
hangs onto the instance unlike your snippet.
Upvotes: 2