66

I am learning Python and so far I can tell the things below about __new__ and __init__:

  1. __new__ is for object creation
  2. __init__ is for object initialization
  3. __new__ is invoked before __init__ as __new__ returns a new instance and __init__ invoked afterwards to initialize inner state.
  4. __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.
  5. We can use __new__ and __init__ for both mutable object as its inner state can be changed.

But I have another questions now.

  1. When I create a new instance such as 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.
  2. 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:

  1. Python object creation
  2. Python's use of __new__ and __init__?
Community
  • 1
  • 1
Tarik
  • 76,703
  • 82
  • 229
  • 338

1 Answers1

61

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 wondering cls 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.

Community
  • 1
  • 1
Fred Foo
  • 342,876
  • 71
  • 713
  • 819
  • 1
    But still, could you please give me a sample code with contains both `__new__` and `__init__` with arbitrary arguments passed. – Tarik Nov 12 '11 at 19:41
  • @Braveyard: posted a toy example. – Fred Foo Nov 12 '11 at 20:00
  • @larsmans as I understand, should we inherit from `object` if we want to use `__new__` and `__init__`? Or we just inherit it to use default `__new__` from the `object`? – Tarik Nov 12 '11 at 20:10
  • 1
    @Braveyard: inherit from `object` to get `__new__`. In fact, it's a good idea to just always inherit from `object` unless you're writing backwards compat code for very old Python versions. In Python 3, old-style classes are gone and inheritance from `object` is implicit in class definitions. – Fred Foo Nov 12 '11 at 20:13
  • @larsmans that makes sense right now :) inheritance from `object` is being implicit in 3.0. Since I used static languages more, inheritance from `object` was always implicit :) – Tarik Nov 12 '11 at 20:15
  • @Braveyard: Python old-style classes are more like those in C++, where there's no global root class either. The oldest Python versions did not have classes at all, I believe, and new-style classes were introduced in an attempt to [unify *all* Python types](http://www.python.org/download/releases/2.2.3/descrintro/) into a single hierarchy. – Fred Foo Nov 12 '11 at 20:29
  • @Braveyard: correction; if I must believe the [Wikipedia](https://secure.wikimedia.org/wikipedia/en/wiki/History_of_Python), then Python had classes from its earliest releases. Sorry about that. – Fred Foo Nov 12 '11 at 20:37
  • 4
    You shouldn't call `__init__` from `__new__`, Python will do that on its own if you return an instance of type `cls`. – interjay Nov 13 '11 at 10:06
  • 5
    I would add to this that the seemingly strange behaviour of being able to return something from `__new__` can (at least theoretically) be useful. I can imagine something like a `BalancedBinaryTree` class, that can be initialised from a list. It has subclasses for `LeafNode` and `EmptyTree`, and `__new__` returns one of those if the input list has 0 or 1 elements. Essentially it means you can implement factories without seeming to, and therefore you can change whether your classes use factories or direct instantiation without necessarily breaking your interface. – Ben Nov 13 '11 at 12:05
  • 1
    I think there is a bug, it should be `type(self).n_eels -= 1`, not `self.n_eels -= 1`. – lumbric Jan 31 '13 at 13:04
  • 5
    I gave you +1 before I got to your eels example. Wish I could give another for `raise HovercraftFull` – keithjgrant Jan 31 '13 at 13:23
  • I don't see how args and kwargs are passing from __new__ to __init__. – PaulDong Jan 06 '17 at 12:25
  • So, Basically, in short `__new__` is a class method which gives direct access(read,write,modify and delete) to class attribute and `__init__` is a instance method which gives direct access(read,write,modify and delete) to instance attribute. Am I right? Please Clarify. – CrazyPythonProgrammer May 27 '20 at 07:55
  • No answer for `When I create a new instance such as a = MyClass("hello","world"), how these arguments are passed?`? – Muhammad Yasirroni Mar 26 '22 at 04:25