I am learning Python and so far I can tell the things below about __new__
and __init__
:
__new__
is for object creation__init__
is for object initialization__new__
is invoked before __init__
as __new__
returns a new instance and __init__
invoked afterwards to initialize inner state.__new__
is good for immutable object as they cannot be changed once they are assigned. So we can return new instance which has new state.__new__
and __init__
for both mutable object as its inner state can be changed.But I have another questions now.
a = MyClass("hello","world")
, how these arguments are passed? I mean how I should structure the class using __init__
and __new__
as they are different and both accepts arbitrary arguments besides default first argument.self
keyword is in terms of name can be changed to something else? But I am wondering cls
is in terms of name is subject to change to something else as it is just a parameter name?I made a little experiments as such below:
>>> class MyClass(tuple): def __new__(tuple): return [1,2,3]
and I did below:
>>> a = MyClass() >>> a [1, 2, 3]
Albeit I said I want to return tuple
, this code works fine and returned me [1,2,3]
. I knew we were passing the first parameters as the type we wanted to receive once the __new__
function is invoked. We are talking about New
function right? I don't know other languages return type other than bound type?
And I did anther things as well:
>>> issubclass(MyClass,list) False >>> issubclass(MyClass,tuple) True >>> isinstance(a,MyClass) False >>> isinstance(a,tuple) False >>> isinstance(a,list) True
I didn't do more experiment because the further wasn't bright and I decided to stop there and decided to ask StackOverflow.
The SO posts I read:
The __new__() is a static method of the object class. When you create a new object by calling the class, Python calls the __new__() method to create the object first and then calls the __init__() method to initialize the object's attributes.
The __init__ method is the Python equivalent of the C++ constructor in an object-oriented approach. The __init__ function is called every time an object is created from a class. The __init__ method lets the class initialize the object's attributes and serves no other purpose. It is only used within classes.
TLDR; __init() is not the constructor, __new__() is. Generally, developers new to python are told that __init__() method is called the constructor.
The self in keyword in Python is used to all the instances in a class. By using the self keyword, one can easily access all the instances defined within a class, including its methods and attributes. init. __init__ is one of the reserved methods in Python. In object oriented programming, it is known as a constructor.
how I should structure the class using
__init__
and__new__
as they are different and both accepts arbitrary arguments besides default first argument.
Only rarely will you have to worry about __new__
. Usually, you'll just define __init__
and let the default __new__
pass the constructor arguments to it.
self
keyword is in terms of name can be changed to something else? But I am wonderingcls
is in terms of name is subject to change to something else as it is just a parameter name?
Both are just parameter names with no special meaning in the language. But their use is a very strong convention in the Python community; most Pythonistas will never change the names self
and cls
in these contexts and will be confused when someone else does.
Note that your use of def __new__(tuple)
re-binds the name tuple
inside the constructor function. When actually implementing __new__
, you'll want to do it as
def __new__(cls, *args, **kwargs): # do allocation to get an object, say, obj return obj
Albeit I said I want to return
tuple
, this code works fine and returned me[1,2,3]
.
MyClass()
will have the value that __new__
returns. There's no implicit type checking in Python; it's the responsibility of the programmer to return the correct type ("we're all consenting adults here"). Being able to return a different type than requested can be useful for implementing factories: you can return a subclass of the type requested.
This also explains the issubclass
/isinstance
behavior you observe: the subclass relationship follows from your use of class MyClass(tuple)
, the isinstance
reflects that you return the "wrong" type from __new__
.
For reference, check out the requirements for __new__
in the Python Language Reference.
Edit: ok, here's an example of potentially useful use of __new__
. The class Eel
keeps track of how many eels are alive in the process and refuses to allocate if this exceeds some maximum.
class Eel(object): MAX_EELS = 20 n_eels = 0 def __new__(cls, *args, **kwargs): if cls.n_eels == cls.MAX_EELS: raise HovercraftFull() obj = super(Eel, cls).__new__(cls) cls.n_eels += 1 return obj def __init__(self, voltage): self.voltage = voltage def __del__(self): type(self).n_eels -= 1 def electric(self): """Is this an electric eel?""" return self.voltage > 0
Mind you, there are smarter ways to accomplish this behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With