31

In Python, __new__ is used to initialize immutable types and __init__ typically initializes mutable types. If __init__ were removed from the language, what could no longer be done (easily)?

For example,

class A:

    def __init__(self, *, x, **kwargs):
        super().__init__(**kwargs)
        self.x = x


class B(A):

    def __init__(self, y=2, **kwargs):
        super().__init__(**kwargs)
        self.y = y

Could be rewritten using __new__ like this:

class A_N:

    def __new__(cls, *, x, **kwargs):
        obj = super().__new__(cls, **kwargs)
        obj.x = x
        return obj


class B_N(A_N):

    def __new__(cls, y=2, **kwargs):
        obj = super().__new__(cls, **kwargs)
        obj.y = y
        return obj

Clarification for scope of question: This is not a question about how __init__ and __new__ are used or what is the difference between them. This is a question about what would happen if __init__ were removed from the language. Would anything break? Would anything become a lot harder or impossible to do?

Neil G
  • 32,138
  • 39
  • 156
  • 257
  • 1
    @matino *were* is the subjunctive; *was* is the indicative. – Neil G Feb 17 '16 at 09:22
  • Not quite. `__new__` is the constructor, `__init__` is the initializer. However, (from [Python's use of `__new__` and `__init__`?](http://stackoverflow.com/a/674369/4014959) ) "In general, you shouldn't need to override `__new__` unless you're subclassing an immutable type like `str`, `int`, `unicode` or `tuple`". Sure, you _can_ initialize stuff via `__new__`, but that's not the conventional Python style, and most experienced Python coders will assume you're doing something special if you override `__new__`. IOW, it's really a matter of style and language design. – PM 2Ring Feb 17 '16 at 09:23
  • You can do everything with `__new__`, but this might introduce many bugs for many reasons. – Maroun Feb 17 '16 at 09:25
  • 2
    @MokonaModoki It's not a duplicate. Did you read my clarification? – Neil G Feb 17 '16 at 09:48
  • 2
    http://stackoverflow.com/questions/3131488/python-always-use-new-instead-of-init – Padraic Cunningham Feb 17 '16 at 10:31
  • 3
    @PM2Ring: "`__new__` is the constructor, `__init__` is the initializer" - why do people keep saying this? In what bizarre definition is `__init__` not a constructor? It takes an allocated, uninitialized object and brings it into an initialized, usable state. This is what constructors do in pretty much every language that has constructors. I'm not aware of any definition under which the constructor is responsible for allocation like `__new__`. Python has its weird allocator/secondary constructor thing in `__new__` because it turned out the first constructor design wasn't quite flexible enough. – user2357112 Feb 17 '16 at 17:37
  • 1
    @user2357112: I guess people keep saying that because `__new__` returns an instance and `__init__` initializes it, and it's convenient to have a clear way to distinguish between these two processes. True, `__new__` can do initialization, and it may return an existing instance rather than allocating a fresh one (but when that happens `__init__` isn't automatically invoked). But `__init__` shouldn't create an instance: it expects to be passed an instance that it can initialize. If you wish to refer to both of these methods as constructors, feel free, but that's not the usual Python convention. – PM 2Ring Feb 17 '16 at 18:02
  • @PM2Ring Do you have a source that it is the usual Python convention to call one the constructor and the other the initializer? – Neil G Feb 17 '16 at 18:12
  • @NeilG: How about this quote from [Dive into Python](http://stackoverflow.com/q/6578487/4014959)? – PM 2Ring Feb 17 '16 at 18:27

3 Answers3

20

Note about difference between __new__ and __init__

Before explaining missing functionality let's get back to definition of __new__ and __init__:

__new__ is the first step of instance creation. It's called first, and is responsible for returning a new instance of your class.

However, __init__ doesn't return anything; it's only responsible for initializing the instance after it's been created.

Consequences of replacing __init__ with __new__

Mainly you would lose out on flexibility. You would get a lot of semantics headaches and loose separation of initializatin and construction (by joining __new__ andinit we are to joining construction and initialization into one step...). Let's take a look on snippet below:

class A(object):
    some_property = 'some_value'

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls, *args, **kwargs)
        obj.some_property = cls.some_property
        return obj


class B(A):
    some_property = 2

    def __new__(cls, *args, **kwargs):
        obj = super(B, cls).__new__(cls)
        return obj

Consequences of moving __init__ actions into __new__:

  1. Initialize B before A: When you are using __new__ method instead of __init__ your first step of creating new instance of B is calling A.__new__ as side effect you cannot initialize B before A is initialized ( access and assign some properties to new B instance). Using of __init__ gives you such flexability.

  2. Loose control on initializing order: let's imagine that you have B_N inherited from two classes (A_N1, A_N2), now you would miss controlling of order of initializing new instance of B_N(what is the order you are going to initialize instances ? it could be matter... what is weird.)

  3. Properties and methods mess: you would miss access to A.some_property (cls would be equal to B while instantiating new instance of B. However directly accessing of A.some_property is possible, but my guess it's at least weird to access properties within class throught class name and not by using classmethods).

  4. You cannot re-initialize an existed instance without creating new one or implementation special logic for this ( thanks to @platinhom for idea )

What can __init__ do that __new__ cannot?

There are no actions that cannot be done in __new__ and can in __init__, because actions that __init__ performs is a subset of the actions that can be performed by __new__.

An interesting moment from Python Docs, Pickling and unpickling normal class instances#object.getinitargs regarding when __init__ could be usefull:

When a pickled class instance is unpickled, its init() method is normally not invoked.

pfabri
  • 885
  • 1
  • 9
  • 25
Andriy Ivaneyko
  • 20,639
  • 6
  • 60
  • 82
  • 7
    I know what they do, but this doesn't anwer my question at all. – Neil G Feb 17 '16 at 09:14
  • Did you see my updated question? That's exactly what I do. – Neil G Feb 17 '16 at 09:25
  • Yes, but you can manipulate the instance of `B`. I show you how to do that in my updated question. You must call `super` by the way. You can't call `object.__new__` from `B` and you probably should get into the habit of calling it from `A`'s `__new__` as well. – Neil G Feb 17 '16 at 09:42
  • 2
    1. A's `__new__` will create an instance of B if `cls=B`. 2. The order is the inheritance order just like with `__init__`. 3. You can access the properties through the created object that is passed. – Neil G Feb 17 '16 at 09:59
  • 1
    I will also use `__init__` as a `reset` function. Can `__new__` do it without creating a new method ? – platinhom Feb 17 '16 at 10:04
  • @platinhom thanks for idea, yes that's not possible until you add some special login to `\__new__` – Andriy Ivaneyko Feb 17 '16 at 10:07
  • Good answer! You could probably focus the points 1 and 3 some more. - You simply cannot initialize B (or set any value on the object) before A is initialized if you have only `__new__`. And it is a bit troublesome to call member functions inside of `__new__` – Falco Feb 17 '16 at 10:29
  • 3
    Why would you not be able to access `A.some_property`? – Padraic Cunningham Feb 17 '16 at 10:51
  • 3
    @PadraicCunningham You can. This answer is almost totally wrong. – Neil G Feb 17 '16 at 17:07
  • 3
    @NeilG, I have to say I was thinking the same, I am amazed at how many upvotes this has gotten. – Padraic Cunningham Feb 17 '16 at 17:10
  • @PadraicCunningham and NeilG. My guess is that answer looks weird for you due to concentrating on opinion that "it's not a problem of mixing construction and initialization of new instance, we would always find workaround" while my answer attempts to highlight inconveniences,confusing moments and obscure commands execution which became routine part of development after mixing init and new due to loosing flow controlling ( separation of construction and initialization of new instances) . – Andriy Ivaneyko Feb 18 '16 at 08:23
  • @AndriyIvaneyko Your answer is not "weird" and you still refuse to address my responses. Your points #2 (you can't control it with `__init__` either) and #3 (see my question) are wrong, and point #4 is a bad idea (nowhere in Python do you use `__init__` as a substitute for `clear` and I think this is a bad idea in general.) Point #1 is a valid, if limited, point. It should be your entire answer in my opinion. – Neil G Feb 19 '16 at 09:34
  • In fact, your point #3 shows a total misunderstanding of how inheritance works in Python. Even if `cls` is `B`, you can still access the properties of `A` since the `__mro__` will already be initialized. – Neil G Feb 19 '16 at 09:37
  • #2 you cannot control initializing order: how would you initialize B then A_N2 and then A_N1 within `__new__` of B ? ( what wrong here.. ?! ) #3: I've pointed to moment that you can access class via A attributes, which is bad idea, because you would face moment when you need to access some props with `cls` and some with `A`, so how such code would be supported in further development by other devs (they would be confused), ? #4 that's you opinion regarding re-initialization of class. – Andriy Ivaneyko Feb 19 '16 at 09:45
  • You cannot control initialization order with `__init__` either. Note that it's wrong to call superclass `__init__` directly — you must call them through `super()`. – Neil G Feb 19 '16 at 09:47
  • "You cannot control initialization ord" .... sure that should be called with super, each time when i mention about initialization order i mean calling init with super() – Andriy Ivaneyko Feb 19 '16 at 09:48
  • #3 is just wrong. You can access parent class members just fine and it's very common to do so. You can find subclasses that do that in the standard library itself. – Neil G Feb 19 '16 at 09:49
  • #4 is not "just my opinion". You can see evidence of that for yourself in all of the library classes that define their own `clear` method. You don't call `__init__` to clear a list or set or a dict. You call `clear`. That's pretty standard. Suggesting that it's a good idea to manually call init is a bad idea for another reason besides just convention: Calling magic methods breaks `__getattribute__`. – Neil G Feb 19 '16 at 09:51
  • #3 just rephrase: "It's ok to access class within classmethod not as parameter because class method could be called with another context", my guess is that working flow of classmethods assume work with `cls` and accessing props via `A.prop` is ok for `staticmethods` – Andriy Ivaneyko Feb 19 '16 at 09:52
  • 1
    I'm sorry, but I have no idea what you're saying. If you really believe in this answer, why don't you erase the intro and produce some examples for each of your points? – Neil G Feb 19 '16 at 09:54
  • That's list mainly rely on semantic difference and obscur actions after joining of init and highlight moments which would be weird at least from my point of view. I don't mind to write use cases but i think they would be obscure, because there no use cases when we face wall due to not using init, there only semantic difference + not desired initialization. – Andriy Ivaneyko Feb 19 '16 at 10:04
4

Everything you can do in __init__ can also be done in __new__.

Then, why use __init__?
Because you don't have to store instance in variable (obj in your example code), and later bother returning it. You can focus on what you realy want to do – initializing mutable object.

GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
3

Per When to use __new__ vs. __init__

__new__ is the first step of instance creation. It's called first, and is responsible for returning a new instance of your class. In contrast, __init__ doesn't return anything; it's only responsible for initializing the instance after it's been created.

Also the class of class is type, and type.__call__() is implemented something like below refer to the above description:

def __call__(cls, *args, **kwargs):
    obj = cls.__new__(cls, *args, **kwargs)
    if isinstance(obj, cls):
        obj.__init__(*args, **kwargs)
    return obj

We know __init__() just do a part of __new__() can do anything to that object before the object is returned.

In general, you shouldn't need to override __new__ unless you're subclassing an immutable type like str, int, unicode or tuple.

So it is not good to remove __init__ from the language, and it is better to always use __init__() better than using __new__().

Here is one history of Low-level constructors and __new__().

zangw
  • 43,869
  • 19
  • 177
  • 214