What's the different between a constructor & method in Python? - python

Can anyone simply explain the difference between constructors and methods in Python

When a class is instantiated, its __init__ method is called to initialize the class instance. Memory is allocated for the class instance, __init__ is called, and the new class is returned. __init__ is the constructor for the class. For example:
c = MyClass(123)
When __init__ is called, the first argument, self, is bound to the new class instance, and the second argument is 123.
An ordinary method operates on an existing class instance:
c.myMethod(456)
In this case, the first argument, self, is bound to c, which is an existing class instance, and the second argument is 456.
In most ways __init__ is like any other method of the class, except it is implicitly called when a new class instance is created.

Related

instance methods sharing in python

1- is it true?
all the objects of a particular class have their own data members but share the member functions, for which only one copy in the memory exists?
2- and why the address of init in this code is similar:
class c:
def __init__(self,color):
print (f"id of self in __init__ on class is {id(self)}")
def test(self):
print("hello")
print (f"id of __init__ on class is {id(__init__)}")
a=c("red")
print(id(a.__init__))
print(id(a.test))
b=c("green")
b.test()
print(id(b.__init__))
print(id(b.test))
Output:
id of __init__ on class is 1672033309600
id of self in __init__ on class is 1672033251232
**1672028411200
1672028411200**
id of self in __init__ on class is 1672033249696
hello
**1672028411200
1672028411200**
Yes, all instances share the same code for a method. When you reference the method through a specific instance, a bound method object is created; it contains a reference to the method and the instance. When this bound method is called, it then calls the method function with the instance inserted as the first argument.
When you reference a method, a new bound method object is created. Unless you save the reference in a variable, the object will be garbage collected immediately. Referring to another method will create another bound method object, and it can use the same address.
Change your code to
init = a.__init__
test = a.test
print(id(init))
print(id(test))
and you'll get different IDs. Assigning the methods to variables keeps the memory from being reused.

Default call to __init__ when creating an instance

Just wanted some help understanding these lines of code:
class Parent:
def __init__(self):
print("instance created")
parent1=Parent()
parent2=Parent.__init__(parent1)
output
instance created
instance created
I am trying to understand how a constructor is called in OOP for python.
In the first line the the method __init__ is called by default and the self argument that is passed is somehow parent1?
The second line is the more traditional way I would've thought methods would be called. Since __init__ takes an instance of the parent class as an argument I passed parent1 and it works. I get what is happening in the second line, just wanted to ask what the computer is doing to create the instance parent1 in the first line.
__init__ is not a constructor, it's an initializer. When Python creates an object, it's actually created in __new__ (usually left as the default, which just makes an empty object of the right class), which receives a reference to the class, and returns an instance (typically empty; no attributes set). The resulting instance is passed implicitly as the self in __init__, which then establishes the instance attributes.
Typically, you don't call special methods like __init__ directly (aside from cases involving super() with cooperative inheritance), you just let Python do it for you. The only way to avoid calling __init__ would be to explicitly invoke the class's __new__ (which is also extremely unusual).
__init__ is the equivalent to a constructor in Python. Think of an object oriented language as one that has a mandatory argument for functions that represent object methods, so you always have access to that object in that function. Most languages don't make you type out the way you pass in this. Python uses self, and makes you type it out for every method. It's the same thing, it's just not doing extra work for you.
So when Python instantiates a class, it passes the class to the class's __new__ function, generates an object, and then passes that object to the class's __init__ function as the first argument.
You are correct that __init__() work like a constructor, is automatically runs when an object is instantiated (as would happen with Java constructor, if that helps). Although you can call __init__, you shouldn't call functions/methods starting with _ or __, they are meant to be called from with the class/object.
When self appears as a parameter in a class method you won't have to supply the object's name, Python will figure it out. So the second line above (Parent2 = ...) is not recommended.
See the documentation:
object.__init__(self[, ...])
Called after the instance has been created (by __new__()), but before it is returned to the caller. The arguments are those passed to the class constructor expression.
object.__new__(cls[, ...])
Called to create a new instance of class cls. __new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression
So under the hood in parent1 = Parent(), Python's basically doing this:
_temp_new_parent = Parent.__new__(Parent) # Inherited from "object.__new__"
Parent.__init__(_temp_new_parent)
parent1 = _temp_new_parent
(_temp_new_parent doesn't really exist, I'm just using it as an abstraction.)
Note that __init__() doesn't return anything, so in your code, parent2 is None. And if __init__() had set instance attributes, it would have set them on parent1 since that's what you passed in.

How is memory set aside when an object is created out of a class?

I read that __init__ doesn't create the object(sets aside memory).
I couldn't find who does the actual object creation.
How does this object creation happens internally?
You're probably looking for __new__.
From a Python point of view, what happens is something like the following, assuming you have a class, A, that inherits directly from object:
A.__new__ method gets called
object.__new__ gets called, with cls=A
A.__init__ method gets called, with arguments forwarded from your A.__new__
Example:
class A():
def __new__(cls, *args, **kwargs):
print(f'__new__ called with args {args} and kwargs {kwargs}')
return super().__new__(cls) # object.__new__
def __init__(self, *args, **kwargs):
print(f'__init__ called with args {args} and kwargs {kwargs}')
# args are discarded
for key, arg in kwargs.items():
setattr(self, key, arg)
a_instance = A('arg', kwarg=1)
a_instance.kwarg
Output:
__new__ called with args ('arg',) and kwargs {'kwarg': 1}
__init__ called with args ('arg',) and kwargs {'kwarg': 1}
1
In general, there is no need to do anything with __new__, because Python objects are usually mutable, and so there is no distinction between initialising instance attributes and modifying them.
The main use case of overriding __new__, in my experience, is when you inherit from immutable types, such as tuple. In such cases, you must initialise all instance attributes at creation, and therefore __init__ is too late.
When you type something like MyClass() in Python, Python runtime will call __new__ on MyClass, which should construct an object; this will be followed by invoking __init__ on the newly constructed object. Thus, __new__ is called "constructor", and __init__ an "initialiser". This sequence is coded outside Python (in C, in case of CPython). Visit Python documentation to read more on __new__ and __init__.
Short answer
As a short answer, the method which creates the object is __new__ and __init__ just initializes the created object.
Long answer
However, keep reading if you want to get a deeper insight into what's happening when an object is going to be created in python.
In python 2.x there were two types of classes in python old-style and new-style classes.
class C: # A old-style class sample
pass
class C(object): # A old-style class sample
pass
In old-style classes, there was no __new__ method so __init__ was the constructor.
However, in python 3.x, just new-style class remains ( Independent of whatever kind of definition you choose for your class, It'll be inherited from the base class "Object"). In new-style classes both __new__ and __init__ methods are available. __new__ and __init__ is the constructor and the initializer respectively and you're permitted to override both of them (be cautious, generally you don't need to override __new__ method expect in some cases like defining meta classes and etc. so don't manipulate it if it's not necessary.)
Finally, when an object is going to be created, the constructor will be called before initializer.
class A(object): # -> don't forget the object specified as base
def __new__(cls):
print "A.__new__ called"
return super(A, cls).__new__(cls)
def __init__(self):
print "A.__init__ called"
A()
The output will be:
A.__new__ called
A.__init__ called

Initializing parent class instance attributes

I understand how to initialize a parent class to get their instance attributes in a child class, but not exactly what's going on behind the scenes to accomplish this. (Note: not using super intentionally here, just to make illustration clear)
Below we extend class A by adding an extra attribute y to the child class B. If you look at the class dict after instantiating b=B(), we rightfully see both b.x(inherited from class A) and b.y.
I assume at a high level this is accomplished by the call to A.__init__(self,x=10) performing something similar to b.x=10 (the way a normal instance attribute would be assigned) within the __init__ of class B. It's a bit unclear to me because you are calling the __init__ of class A, not class B, yet class B still gets it's instance attributes updated accordingly. How does class A's __init__ know to update b's instance attributes.
This is different than inherited methods where the b object has no explicit inherited method in it's particular namespace, but looks up the inheritance chain when a call to a missing method is made. With the attribute, the method is actually in b's namespace (it's instance dict).
class A:
def __init__(self,x):
self.x = x
class B(A):
def __init__(self):
A.__init__(self,x=10)
self.y = 1
b = B()
print(b.__dict__)
>>>{x:10,y:1} #x added to instance dict from parent init
Below we inherit from the built-in list. Here, similar to the above, since we are calling the list's __init__ method within Foolist's __init__, I would expect to see an instance dictionary that contains elems, but it is nowhere to be found. The values 123 are in the object somewhere, as can be seen by printing alist, but not in the instance dict.
class Foolist(list):
def __init__(self, elems):
list.__init__(self, elems)
alist = Foolist('123')
So what exactly is going on in the inheriting class when a parent's __init__ is called from a child's __init__? How are values being bound? It seems different from method lookup, as you are not searching the inheritance chain on demand, but actually assigning values to the inheriting class's instance dict.
How does a call to a parents init fill out it's child's instance dict? Why does the Foolist example not do this?
The answer is simple: self.
As a very rough overview, when instantiating a class, an object is created. This is more or less literally just an empty container without affiliation to anything.* This "empty container" is then passed to the __init__ method of the class that is being instantiated, where it becomes... the self argument! You're then setting an attribute on that object. You're then calling a different class's __init__ method, explicitly passing your specific self object to that method; that method then adds another attribute to the object.
This is in fact how every instance method works. Each method implicitly receives the "current object" as its first argument, self. When calling a parent's __init__ method, you're actually making that object passing very explicit.
You can approximate that behaviour with this simple example:
def init_a(obj):
obj.x = 10
def init_b(obj):
init_a(obj)
obj.y = 20
o = {}
init_b(o)
* The object is not entirely "empty", there are particular attributes set on the object which create an affiliation with a particular class, so the object is "an instance of" a certain class, and Python can locate all the methods it so inherits from the class as needed.

understanding python self and init

As might be familiar to most of you, this is from Mark Pilgrim's book DIP, chapter 5
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
Well I am new to python, coming from basic C background and having confusion understanding it. Stating what I understand, before what I don't understand.
Statement 0: FileInfo is inheriting from class UserDict
Statement 1: __init__ is not a constructor, however after the class instantiates, this is the first method that is defined.
Statement2: self is almost like this
Now the trouble:
as per St1 init is defined as the first function.
UserDict.__init__(self)
Now within the same function __init__ why is the function being referenced, there is no inherent recursion I guess. Or is it trying to override the __init__ method of the class UserDict which the class FileInfo has inherited and put an extra parameter(key value pair) of filename and reference it to the filename being passed to __init__ method.
I am partly sure, I have answered my question, however as you can sense there is confusion, would be great if someone can explain me how to rule this confusion out with some more advanced use case and detailed example of how generally code is written.
You're correct, the __init__ method is not a constructor, it's an initializer called after the object is instantiated.
In the code you've presented, the __init__ method on the FileInfo class is extending the functionality of the __init__ method of the base class, UserDict. By calling the base __init__ method, it executes any code in the base class's initialization, and then adds its own. Without a call to the base class's __init__ method, only the code explicitly added to FileInfo's __init__ method would be called.
The conventional way to do this is by using the super method.
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
super(UserDict, self).__init__()
self["name"] = filename
A common use case is returning extra values or adding additional functionality. In Django's class based views, the method get_context_data is used to get the data dictionary for rendering templates. So in an extended method, you'd get whatever values are returned from the base method, and then add your own.
class MyView(TemplateView):
def get_context_data(self, **kwargs):
context = super(MyClass, self).get_context_data(**kwargs)
context['new_key'] = self.some_custom_method()
return kwargs
This way you do not need to reimplement the functionality of the base method when you want to extend it.
Creating an object in Python is a two-step process:
__new__(self, ...) # constructor
__init__(self, ...) # initializer
__new__ has the responsibility of creating the object, and is used primarily when the object is supposed to be immutable.
__init__ is called after __new__, and does any further configuration needed. Since most objects in Python are mutable, __new__ is usually skipped.
self refers to the object in question. For example, if you have d = dict(); d.keys() then in the keys method self would refer to d, not to dict.
When a subclass has a method of the same name as its parent class, Python calls the subclass' method and ignores the parent's; so if the parent's method needs to be called, the subclass method must call it.
"Or is it trying to override the init method of the class UserDict which the class FileInfo has inherited and put an extra parameter(key value pair) of filename and reference it to the filename being passed to init method."
It's exactly that. UserDict.__init__(self) calls the superclass init method.
Since you come from C, maybe you're not well experienced with OOP, so you could read this article : http://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming) to understand the inheritance principle better (and the "superclass" term I used).
.. the self variable represents the instance of the object itself. In python this is not a hidden parameter as in other languages. You have to declare it explicitly. When you create an instance of the FileInfo class and call its methods, it will be passed automatically,
The __init__ method is roughly what represents a constructor in Python.
The __init__ method of FileInfo is overriding the __init__ method of UserDict.
Then FileInfo.__init__ calls UserDict.__init__ on the newly created FileInfo instance (self). This way all properties and magic available to UserDict are now available to that FileInfo instance (ie. they are inherited from UserDict).
The last line is the reason for overriding UserDict.__init__ : UserDict does not create the wanted property self.filename.
When you call __init__ method for a class that is inheriting from a base class, you generally modify the ancestor class and as a part of customization, you extend the ancestor's init method with proper arguements.
__init__ is not a constructor, however after the class instantiates, this is the first method that is defined.
This method is called when an instance is being initialized, after __new__ (i.e. when you call ClassName()). I'm not sure what difference there is as opposed to a constructor.
Statement2: self is almost like this
Yes but it is not a language construct. The name self is just convention. The first parameter passed to an instance method is always a reference to the class instance itself, so writing self there is just to name it (assign it to variable).
UserDict.__init__(self)
Here you are calling the UserDict's __init__ method and passing it a reference to the new instance (because you are not calling it with self.method_name, it is not passed automatically. You cannot call an inherited class's constructor without referencing its name, or using super). So what you are doing is initializing your object the same way any UserDict object would be initialized.

Categories