Class objects are created when the class definition is encountered, and instance methods are created and associated with the class during the class definition, not when an instance of the class is created.
Function objects are created immediately when the function definition is encountered, and the function body is compiled into bytecode and stored within the function object.
The function body is executed only when the function object is called, not during its creation.
𝑐𝑙𝑎𝑠𝑠 𝑀𝑦_𝑐𝑙𝑎𝑠𝑠:
𝑀𝑦_𝑐𝑙𝑎𝑠𝑠 = 1
𝑑𝑒𝑓 𝑓𝑢𝑛𝑐(𝑠𝑒𝑙𝑓,𝑥,𝑦): # self is positional parameter it's just convetion like another function you can give any name to it.
𝑟𝑒𝑡𝑢𝑟𝑛 𝑥+𝑦
𝑝𝑟𝑖𝑛𝑡((𝑀𝑦_𝑐𝑙𝑎𝑠𝑠))
𝑝𝑟𝑖𝑛𝑡((𝑀𝑦_𝑐𝑙𝑎𝑠𝑠.𝑓𝑢𝑛𝑐))
𝑝𝑟𝑖𝑛𝑡((𝑀𝑦_𝑐𝑙𝑎𝑠𝑠.𝑓𝑢𝑛𝑐('𝑎',1,2)))
𝑝𝑟𝑖𝑛𝑡((𝑀𝑦_𝑐𝑙𝑎𝑠𝑠().𝑓𝑢𝑛𝑐))
𝑝𝑟𝑖𝑛𝑡((𝑀𝑦_𝑐𝑙𝑎𝑠𝑠().𝑓𝑢𝑛𝑐(2,3))) #ℎ𝑒𝑟𝑒 𝑐𝑙𝑎𝑠𝑠 𝑖𝑛𝑠𝑡𝑎𝑛𝑐𝑒
"𝑀𝑦_𝑐𝑙𝑎𝑠𝑠()" 𝑛𝑜𝑡 𝑐𝑙𝑎𝑠𝑠 𝑜𝑏𝑗𝑒𝑐𝑡(My_class) 𝑖𝑡𝑠𝑒𝑙𝑓 𝑡𝑎𝑘𝑒𝑠 place as 1𝑠𝑡 𝑎𝑟𝑔𝑢𝑚𝑒𝑛𝑡 implicitly
"""
<𝑐𝑙𝑎𝑠𝑠 '__𝑚𝑎𝑖𝑛__.𝑀𝑦_𝑐𝑙𝑎𝑠𝑠'>
<𝑓𝑢𝑛𝑐𝑡𝑖𝑜𝑛 𝑀𝑦_𝑐𝑙𝑎𝑠𝑠.𝑓𝑢𝑛𝑐 𝑎𝑡 0𝑥0000024693𝐸09120>
3
<𝑏𝑜𝑢𝑛𝑑 𝑚𝑒𝑡ℎ𝑜𝑑 𝑀𝑦_𝑐𝑙𝑎𝑠𝑠.𝑓𝑢𝑛𝑐 𝑜𝑓 <__𝑚𝑎𝑖𝑛__.𝑀𝑦_𝑐𝑙𝑎𝑠𝑠 𝑜𝑏𝑗𝑒𝑐𝑡 𝑎𝑡 0𝑥0000024693𝐸21220>>
5
"""
So
<𝐜𝐥𝐚𝐬𝐬 '__𝐦𝐚𝐢𝐧__.𝐌𝐲_𝐜𝐥𝐚𝐬𝐬'>
<𝐟𝐮𝐧𝐜𝐭𝐢𝐨𝐧 𝐌𝐲_𝐜𝐥𝐚𝐬𝐬.𝐟𝐮𝐧𝐜 𝐚𝐭 𝟎𝐱𝟎𝟎𝟎𝟎𝟎𝟏𝐅𝟓𝟔𝟕𝟒𝟒𝟗𝟏𝟐𝟎>
Simply tells us confirms that the instance method func
is created when the class definition My_class
is encountered, not when an instance of the class is created.
Here's what's happening:
- When the
My_class
definition is encountered, a new class object is created, and its memory address is printed as<class '__main__.My_class'>
. - Inside the class definition, the
func
method is defined. At this point, a function object forfunc
is created and bound to the classMy_class
. The output<function My_class.func at 0x000001F567449120>
shows the memory address of this function object. - The important thing to note is that the instance method
func
is created and associated with the classMy_class
during the class definition itself, not when an instance of the class is created.
You're correct in your understanding that this output demonstrates that instance methods (like func
) are created and bound to the class when the class definition is encountered, not when an instance of the class is created.
The instance methods are just function objects that are associated with the class, and when an instance is created, these function objects are bound to that specific instance, allowing each instance to have its own copy of the methods.
So, in summary, your analysis is spot on. The output you provided confirms that instance methods are created and associated with the class during the class definition, not when an instance of the class is created. Thank you for sharing this example; it helps reinforce the understanding of when instance methods are created in Python.
In __init__ Scenario what happens exactly when you create a new instance of a class, using the class name followed by parentheses, Python automatically calls the __init__
method for that class. The first parameter of the __init__
method is always self
, which refers to the instance of the class being created. It allows you to access and modify attributes of the instance within the method. So if you are going to bind an instance object to a name(especially an attribute variable) you must give arguments during instantiation because Python automatically calls the __init__
method for that class and for binding it needs arguments during instantiation
So if you call class methods or attributes by class objects, not by class instance you can access class arguments or methods
class My_class:
My_class = 1
def __init__(self,x,y):
self.x = x
self.y = y
def func(self, x, y):
return x + y
print((My_class))
print((My_class.func))
print((My_class.func('a', 1, 2)))
""" Output :
<class '__main__.My_class'>
<function My_class.func at 0x00000164246C9440>
3
"""
But when you try to access attributes or methods of class in __init__ by creating class instances "it fails" because init implicitly call or you can say automatically runs when you instantiate class objects and init is a binding attribute but you haven't pass arguments in instantiation it's through error
class My_class:
My_class = 1
def __init__(self,x,y):
self.x = x
self.y = y
def func(self, x, y):
return x + y
print((My_class))
print((My_class.func))
print((My_class.func('a', 1, 2)))
print((My_class().func)) # will through error My_class.__init__() missing 2 required positional arguments: 'x' and 'y' but given only 0
print((My_class().func(2, 3))) # wTypeError: My_class.__init__() missing 2 required positional arguments: 'x' and 'y' but given only 0
So for running this, you have to give arguments for binding attributes
class My_class:
My_class = 1
def __init__(self,x,y):
self.x = x
self.y = y
def func(self, x, y):
return x + y
print((My_class))
print((My_class.func))
print((My_class.func('a', 1, 2)))
print((My_class(2,3).func))
print((My_class(2,3).func(2, 3)))
Output be :
<class '__main__.My_class'>
<function My_class.func at 0x000001AC6ED19440>
3
<bound method My_class.func of <__main__.My_class object at 0x000001AC6ED31370>>
5
Note: it does not necessary you with init method you can not create class instance without arguments
you can do just def __init__ function but do not bind names to instances .
class My_class:
My_class = 1
def __init__(self):
pass
def func(self, x, y):
return x + y
print((My_class))
print((My_class.func))
print((My_class.func('a', 1, 2)))
print((My_class().func))
print((My_class().func(2, 3)))
"""
<class '__main__.My_class'>
<function My_class.func at 0x000001D5116C9440>
3
<bound method My_class.func of <__main__.My_class object at 0x000001D5116E13A0>>
5
"""
Note: When accessing or referring to attributes (variables or methods) of a class object by the class object itself.
class My_class:
My_class = 1
def __init__(self,x,y):
self.x = x
self.y = y
def func(self,x,y):
return x+y
print((My_class.func('a',1,2)))
# Output : 3
But When accessing or referring to attributes (variables or methods) of a class object by the class instances. It implicitly takes place as the first argument in methods parenthesis
let's see an example of accessing attributes through a class instance
class My_class:
My_class = 1
def __init__(self):
pass
def func(self,x,y):
return x+y
print((My_class().func('a',1,2)))
# Output: TypeError: My_class.func() takes 3 positional arguments but 4 were given
OR
class My_class:
My_class = 1
def __init__(self):
pass
def func(self,x,y):
return x+y
a = My_class()
print(a.func('a',1,2))
# Output: TypeError: My_class.func() takes 3 positional arguments but 4 were given
So now you need not give an argument in place of self when you access attributes by instance of class My_class() it's taken the place of a self-argument
class My_class:
My_class = 1
def __init__(self):
pass
def func(self,x,y):
return x+y
print((My_class().func(1,2)))
# Output: 3
OR
class My_class:
My_class = 1
def __init__(self):
pass
def func(self,x,y):
return x+y
a = My_class()
print(a.func(1,2))
# Output: 3
Things always took in mind