# Implicit initialisation of inherited attributes

**URL:** <https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849>\
**Category:** Ideas\
**Created:** [July 26, 2020, 8:12pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849 "2020-07-26T20:12:25Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![maggyero](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/maggyero/32/1505_2.png) [@maggyero](https://discuss.python.org/u/maggyero)\
**Post date:** [July 26, 2020, 8:12pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/1 "2020-07-26T20:12:25Z")

</div>

In Python, I have noticed that the ` __init__ ` constructor of a class does not _implicitly_ call the ` __init__ ` constructor of its base class. Indeed, this program:

```auto
class A:
    def __init__ (self): print("A")

class B(A):
    def __init__ (self): print("B")

b = B()

```

outputs:

```auto
B

```

This contrasts with C++ for which the constructor of a class _implicitly_ calls the default constructor of its base class. Indeed, this program:

```auto
#include <iostream>

struct A {
    A() { std::cout << "A" << std::endl; }
};

struct B: A {
    B() { std::cout << "B" << std::endl; }
};

int main() {
    B b {};
    return 0;
}

```

outputs:

```auto
A
B

```

I see two benefits with the behaviour of Python: ` __init__ ` behaves like any other methods and everything is explicit.

But I see also one drawback: we can get runtime errors (`AttributeError`) when a class calls inherited methods that use uninitialised attributes, because the constructor of the class did not explicitly call the constructor of its base class. Indeed, this program:

```auto
class A:
    def __init__ (self): self.x = 0
    def f(self): return self.x

class B(A):
    def __init__ (self): pass

b = B()
b.f()

```

outputs:

```auto
[…]
AttributeError: 'B' object has no attribute 'x'

```

The problem is obvious in this program since we can see the implementation of the base class, but when we subclass a base class defined in another module I imagine it could be more pernicious.

For _explicitly_ calling the ` __init__ ` constructor of a base class, instead of using the base class directly we can use the `super` class in order to allow _cooperative multiple inheritance_ at the same time. Like explained in Raymond Hettinger’s excellent article [Python’s super() considered super!](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/), this requires:

- calling `super(). __init__ ` in each ` __init__ ` constructor of the inheritance graph;
- matching caller arguments and callee parameters (by having each ` __init__ ` constructor take the keyword parameters that it uses and a variadic keyword argument that it forwards to the `super(). __init__ ` base constructor).

Indeed, this program:

```auto
class A:
    def __init__ (self, a, **kwargs):
        print("A", a, kwargs)
        super(). __init__ (**kwargs)

class B(A):
    def __init__ (self, b, **kwargs):
        print("B", b, kwargs)
        super(). __init__ (**kwargs)

class C(A):
    def __init__ (self, c, **kwargs):
        print("C", c, kwargs)
        super(). __init__ (**kwargs)

class D(B, C):
    def __init__ (self, d, **kwargs):
        print("D", d, kwargs)
        super(). __init__ (**kwargs)

D(a=1, b=2, c=3, d=4)

```

outputs:

```auto
D 4 {'a': 1, 'b': 2, 'c': 3}
B 2 {'a': 1, 'c': 3}
C 3 {'a': 1}
A 1 {}

```

If Python was designed in such a way that the ` __init__ ` constructor of a class _implicitly_ called the ` __init__ ` constructor of its base class, the previous program would look like this:

```auto
class A:
    def __init__ (self, a):
        print("A", a)

class B(A):
    def __init__ (self, b):
        print("B", b)

class C(A):
    def __init__ (self, c):
        print("C", c)

class D(B, C):
    def __init__ (self, d):
        print("D", d)

D(a=1, b=2, c=3, d=4)

```

Finally here are my questions:

1. Would such a language design be _possible_?
2. Would such a language design be _desirable_?

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [July 26, 2020, 9:53pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/2 "2020-07-26T21:53:55Z")

</div>

Not only is it possible, but you can do it right now in your own classes: you could create your own meta-class which builds the instantiation parameters (in ` __new__ `) by inspecting and passing them to each base classes’ ` __init__ `. To stay explicit, you might want to call the meta-class `ImplicitParentInit`, and have it only pass to base classes which have this meta-class (or a specific attribute). I’ve implemented an example [below](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/4).

This wouldn’t be incorporated into standard Python, firstly because it’s breaking (duh), but also because it’s not explicit; try `import this`.

The way I would implement your example above is to use dataclasses and inheritance (although optical parameters will get you: I have [a PR](https://github.com/python/cpython/pull/17322) fixing that). You can still put methods on dataclasses, but you get ` __init__ ` and ` __repr__ ` for free.

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [July 27, 2020, 12:50am UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/3 "2020-07-27T00:50:18Z")

</div>

Hi Géry,

As you point out, the same applies for every method. ` __init__ ` and

` __new__ ` are not special.

You give some example code using `super()`:

```auto

class A:

    def __init__ (self, a, **kwargs):

        print("A", a, kwargs)

        super(). __init__ (**kwargs)

 

class B(A):

    def __init__ (self, b, **kwargs):

        print("B", b, kwargs)

        super(). __init__ (**kwargs)

```

(For brevity I have ignored C and D classes.)

You say:

"If Python was designed in such a way that the ` __init__ ` constructor

of a class _implicitly_ called the ` __init__ ` constructor of its base

class, the previous program would look like this:"

```auto

class A:

    def __init__ (self, a):

        print("A", a)

class B(A):

    def __init__ (self, b):

        print("B", b)

```

and then ask if such a design was possible and desirable.

Possible, I guess so, but it would be expensive.

Desirable, no.

Even in this simplified, shallow class hierarchy, we can see why this

would be undesirable.

Where should the implicit call to super() go? Before or after running

the current method? In the above example, that makes a difference

between printing “B” first or “A” first.

Whichever choice you choose, some subclasses will want the opposite.

And what about subclasses that _intentionally_ don’t call super, because

they want to completely override the superclass constructor? Failing to

call super is not necessarily a bug, sometimes it is intentional. You

will break them. Likewise for classes that already call super,

explicitly.

This would also be a useability nightmare. As the caller of this class,

how am I supposed to know that I cannot call `B(b=2)` but have to supply

an undocumented parameter that doesn’t show up in the constructor

signature?

"I am trying to instantiate a class that takes a single parameter, but

whenever I call it I get a TypeError about missing arguments."

What happens if the caller passes arguments positionally rather than as

keyword? `B(1, 2)` rather than `B(a=1, b=2)`?

It would also slow down class instantiation. The interpreter would have

to try to allocate arguments in the normal way:

```
B(a=1, b=2)

```

which would fail. It would then have to search the MRO of B looking for

a class that takes a parameter `a`. Having found one, it then has to

remove `a` from the arguments, and try calling `B. __init__ ` again.

Repeat each time it fails until there are no extra parameters.

Once all the parameters are allocated for B, then it would have

to call `B. __init__ `, implicitly calling super() with those extra

arguments that had been removed, and repeat the process for every

superclass in the MRO.

And this whole process has to be repeated on _every_ call to B() because

the constructor methods are resolved at runtime, not compile time. It’s

not like statically typed languages where the compiler can resolve the

calls once, at compile time. It has to be done on every call.

What about alternative constructors? It would be very odd, and

confusing, if this magical behaviour applied to `B()` but not alternate

constructors like `B.from_items()`.

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [July 27, 2020, 2:18am UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/4 "2020-07-27T02:18:56Z")

</div>

I’ve implemented a metaclass that achieves your syntax:

```python
import inspect

class ImplicitParentInitMeta(type):
    def __call__ (cls, **kwargs):
        self = object. __new__ (cls)
        for cls_ in cls. __mro__ :
            if isinstance(cls_, ImplicitParentInitMeta):
                sig = inspect.signature(cls_. __init__ )
                kw = {n: kwargs[n] for n in sig.parameters if n in kwargs}
                cls_. __init__ (self, **kw)
        return self

class A(metaclass=ImplicitParentInitMeta):
    def __init__ (self, a):
        print("A", a)

class B(A, metaclass=ImplicitParentInitMeta):
    def __init__ (self, b):
        print("B", b)

...

```

---

<div class="post-metadata">

**Author:** ![maggyero](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/maggyero/32/1505_2.png) [@maggyero](https://discuss.python.org/u/maggyero)\
**Post date:** [July 28, 2020, 7:19pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/5 "2020-07-28T19:19:25Z")

</div>

Thanks a lot for this neat implementation @EpicWink! Metaclasses are indeed very powerful.

Your implementation follows the C++ design which allows each constructor to forward the arguments that it uses, and therefore allows multiple classes in the inheritance graph to use the same arguments. Indeed, this C++ program:

```auto
#include <iostream>

struct A {
    A(int x) { std::cout << "A(" << x << ")" << std::endl; }
};

struct B: A {
    // Constructor B forwards the argument x that it uses
    B(int x): A(x) { std::cout << "B(" << x << ")" << std::endl; }
};

int main() {
    B b {3};
    return 0;
}

```

outputs:

```auto
A(3)
B(3)

```

On the other hand, in @rhettinger’s implementation of cooperative multiple inheritance, each ` __init__ ` constructor strips off the arguments that it uses from the keyword argument dictionary to forward, and therefore prevents multiple classes in the inheritance graph from using the same arguments.

Here is a slight variation on your implementation @EpicWink, but following @rhettinger’s design:

```auto
import inspect

class BaseInitializer(type):
    def __call__ (cls, **kwargs):
        instance = cls. __new__ (cls, **kwargs)
        if isinstance(instance, cls):
            for cls_ in cls. __mro__ :
                if isinstance(cls_, BaseInitializer):
                    sig = inspect.signature(cls_. __init__ )
                    kwargs_ = {
                        p: kwargs.pop(p)
                        for p in sig.parameters if p in kwargs
                    }
                    cls_. __init__ (instance, **kwargs_)
        return instance

class A(metaclass=BaseInitializer):
    def __init__ (self, a):
        print("A", a)

class B(A, metaclass=BaseInitializer):
    def __init__ (self, b):
        print("B", b)

class C(A, metaclass=BaseInitializer):
    def __init__ (self, c):
        print("C", c)

class D(B, C, metaclass=BaseInitializer):
    def __init__ (self, d):
        print("D", d)

D(a=1, b=2, c=3, d=4)

```

It would be interesting to have @rhettinger’s motivations for preventing multiple classes in the inheritance graph from using the same arguments. I guess that most of the time we do not want an object to have different copies of the same attribute (the one owned and the ones inherited), but should it be the rule?

---

<div class="post-metadata">

**Author:** ![maggyero](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/maggyero/32/1505_2.png) [@maggyero](https://discuss.python.org/u/maggyero)\
**Post date:** [July 28, 2020, 7:30pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/6 "2020-07-28T19:30:23Z")

</div>

Thank you @steven.daprano for the very sensible remarks. I think @EpicWink’s metaclass approach is a very nice solution to implement implicit base initialization now while remaining optional.

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [July 28, 2020, 11:32pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/7 "2020-07-28T23:32:00Z")

</div>

> [@maggyero](#):
>
> I guess that most of the time we do not want an object to have different copies of the same attribute

Remember that assignment doesn’t copy in Python, rather just creates a new reference to the assigned object. Seeing as the majority of the time you assign the argument to the same name in the unit, you’ll likely just overwrite with the same thing.

```python
def __init__ (self, a):
    self.a = a

```

---

<div class="post-metadata">

**Author:** ![maggyero](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/maggyero/32/1505_2.png) [@maggyero](https://discuss.python.org/u/maggyero)\
**Post date:** [July 29, 2020, 1:53pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/8 "2020-07-29T13:53:06Z")

</div>

You are right @EpicWink, it is an assignment. Which makes things worse, because now you get _aliasing issues_ with inherited attributes sharing values. For instance, your `ImplicitParentInitMeta` metaclass allows this kind of Python program:

```auto
class A(metaclass=ImplicitParentInitMeta):
    def __init__ (self, a):
        self.__x = a
    def f(self):
        self.__x[0] += 1

class B(A, metaclass=ImplicitParentInitMeta):
    def __init__ (self, a):
        self.__y = a
    def g(self):
        return self.__y

b = B(a=[1])
print(b.g())
b.f()
print(b.g())

```

which outputs:

```auto
[1]
[2]

```

This might be surprising. But C++ allows the same kind of vertical attribute interaction anyway. Indeed this C++ program:

```auto
#include <iostream>

struct A {
    A(int& a): x{a} {}
    void f() { ++x; }
  private:
    int& x;
};

struct B: A {
    B(int& a): y{a}, A{a} {}
    int g() { return y; }
  private:
    int& y;
};

int main() {
    int a{1};
    B b{a};
    std::cout << b.g();
    b.f();
    std::cout << b.g();
    return 0;
}

```

outputs:

```auto
12

```

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [July 29, 2020, 3:42pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/9 "2020-07-29T15:42:08Z")

</div>

Was thinking in my example you have the same name for the instance attribute as the instantiation argument:

```python
class A(metaclass=ImplicitParentInitMeta):
    def __init__ (self, x):
        self.x = x

class B(A, metaclass=ImplicitParentInitMeta):
    def __init__ (self, x):
        self.x = x

b = B(3)
print(b.x) # 3

```

---

<div class="post-metadata">

**Author:** ![maggyero](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/maggyero/32/1505_2.png) [@maggyero](https://discuss.python.org/u/maggyero)\
**Post date:** [July 31, 2020, 6:16pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/10 "2020-07-31T18:16:35Z")

</div>

Oh I see, we were talking about two different issues that could result from passing the same argument list to each class’s ` __init__ ` in the inheritance graph: I was talking about instance attribute _aliasing_, while you were talking about instance attribute _overriding_. But contrary to the former, instance attribute overriding can occur even when you don’t pass the same argument list to each class’s ` __init__ ` in the inheritance graph, like prescribed by @rhettinger’s collaborative multiple inheritance (so that cannot be his motivation):

```auto
class A:
    def __init__ (self):
        self.a = 1
        self._b = 2
        self.__c = 3

class B(A):
    def __init__ (self):
        self.d = 1
        self._e = 2
        self.__f = 3

class C(A):
    def __init__ (self):
        super(). __init__ ()
        self.d = 1
        self._e = 2
        self.__f = 3

class D(A):
    def __init__ (self):
        self.d = 1
        self._e = 2
        self.__f = 3
        super(). __init__ ()

class E(A):
    def __init__ (self):
        self.a = 4
        self._b = 5
        self.__c = 6

class F(A):
    def __init__ (self):
        super(). __init__ ()
        self.a = 4
        self._b = 5
        self.__c = 6

class G(A):
    def __init__ (self):
        self.a = 4
        self._b = 5
        self.__c = 6
        super(). __init__ ()

b = B()
c = C()
d = D()
e = E()
f = F()
g = G()
assert vars(b) == {'d': 1, '_e': 2, '_B__f': 3}
assert vars(c) == {'a': 1, '_b': 2, '_A __c': 3, 'd': 1, '_e': 2, '_C__ f': 3}
assert vars(d) == {'a': 1, '_b': 2, '_A __c': 3, 'd': 1, '_e': 2, '_D__ f': 3}
assert vars(e) == {'a': 4, '_b': 5, '_E__c': 6}
assert vars(f) == {'a': 4, '_b': 5, '_F __c': 6, '_A__ c': 3}
assert vars(g) == {'a': 1, '_b': 2, '_G __c': 6, '_A__ c': 3}

```

As it feels more natural to me that a derived class overrides an inherited instance attribute than the contrary (in other words that `super(). __init__ ` be called before rather than after), here is a new implementation of your metaclass (the only difference is the `reversed` call):

```auto
class ImplicitParentInitMeta(type):

    def __call__ (cls, **kwargs):
        instance = cls. __new__ (cls, **kwargs)
        if isinstance(instance, cls):
            for cls_ in reversed(cls. __mro__ ):
                if isinstance(cls_, ImplicitParentInitMeta):
                    sig = inspect.signature(cls_. __init__ )
                    kwargs_ = {
                        p: kwargs[p]
                        for p in sig.parameters if p in kwargs
                    }
                    cls_. __init__ (instance, **kwargs_)
        return instance

```

---

<div class="post-metadata">

**Author:** ![jriddy](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jriddy/32/902_2.png) [@jriddy](https://discuss.python.org/u/jriddy)\
**Post date:** [July 31, 2020, 6:40pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/11 "2020-07-31T18:40:24Z")

</div>

I have to wonder _why_ you’d want something like this to be in the language. Although you can make this kinda thing happen with metaclasses and such like mentioned here, this is a complex behavior that would be difficult to explain to someone that wasn’t intimately familiar with the details.

I have to wonder…why not just try to simplify how you do your ` __init__ ` methods and make them more independent? This isn’t C++; you don’t have to allocate a ton of resources just to make an object. And as mentioned above, Python style typically prefers a more explicit way of handing those things when you need it. What problem are you trying to solve (at a higher level than “make the language act like I want it to”)? Perhaps there’s a more idiomatic way of approaching it

---

<div class="post-metadata">

**Author:** ![maggyero](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/maggyero/32/1505_2.png) [@maggyero](https://discuss.python.org/u/maggyero)\
**Post date:** [July 31, 2020, 11:30pm UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/12 "2020-07-31T23:30:19Z")

</div>

Like explained in the original post, my motivation for exploring implicit initialisation of inherited attributes was:

1. Avoiding runtime errors ( `AttributeError` ) when a class calls inherited methods that use uninitialised attributes.
2. Making cooperative multiple inheritance automatic.

But I did not face any real problem. I just wanted to better understand the Python inheritance model (its strengths and weaknesses) by discussing this idea with other developers to see where it leads and at least learn a few things on the way. @steven.daprano did the intellectual exercise very well and @EpicWink even provided an implementation.

---

<div class="post-metadata">

**Author:** ![maggyero](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/maggyero/32/1505_2.png) [@maggyero](https://discuss.python.org/u/maggyero)\
**Post date:** [August 1, 2020, 12:16am UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/13 "2020-08-01T00:16:13Z")

</div>

> [@steven.daprano](#):
>
> Where should the implicit call to super() go? Before or after running the current method? In the above example, that makes a difference between printing “B” first or “A” first. Whichever choice you choose, some subclasses will want the opposite. And what about subclasses that _intentionally_ don’t call super, because they want to completely override the superclass constructor?

With @EpicWink’s metaclass, we have the choice to use implicit initialisation or not, and it could support a keyword boolean argument `init_after` in the base class list to change the order (I think before should be the default as it is more natural to override inherited attributes in derived classes than the opposite).

> [@steven.daprano](#):
>
> This would also be a useability nightmare. As the caller of this class, how am I supposed to know that I cannot call `B(b=2)` but have to supply an undocumented parameter that doesn’t show up in the constructor signature?

That is a real issue, I agree.

> [@steven.daprano](#):
>
> What happens if the caller passes arguments positionally rather than as keyword? `B(1, 2)` rather than `B(a=1, b=2)`?

Yes that is an issue. Since cooperative multiple inheritance is not practical with positional arguments, they should be forbidden for instantiating classes. This is the case with @EpicWink’s metaclass.

> [@steven.daprano](#):
>
> It would also slow down class instantiation. The interpreter would have to try to allocate arguments in the normal way:
> 
> ```auto
> B(a=1, b=2)
> 
> ```
> 
> which would fail. It would then have to search the MRO of B looking for a class that takes a parameter `a` . Having found one, it then has to remove `a` from the arguments, and try calling `B. __init__ ` again. Repeat each time it fails until there are no extra parameters. Once all the parameters are allocated for B, then it would have to call `B. __init__ ` , implicitly calling super() with those extra arguments that had been removed, and repeat the process for every superclass in the MRO.

The MRO is actually traversed once in @EpicWink’s metaclass, with ` __init__ ` called for each class in the MRO. The popping or not of keyword arguments is independent of the traversal.

> [@steven.daprano](#):
>
> What about alternative constructors? It would be very odd, and confusing, if this magical behaviour applied to `B()` but not alternate constructors like `B.from_items()`.

I am not sure that I follow you here since `B.from_items()` delegates to `B()`, right?

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [August 1, 2020, 3:56am UTC](https://discuss.python.org/t/implicit-initialisation-of-inherited-attributes/4849/14 "2020-08-01T03:56:29Z")

</div>

> [@steven.daprano](#):
>
> What happens if the caller passes arguments positionally rather than as
> 
> keyword?

Géry Ogam replied:

"Yes that is an issue. Since cooperative multiple inheritance is not

practical with positional arguments, they should be forbidden for

instantiating classes. This is the case with @EpicWink’s metaclass."

That’s a bold claim. I’ve written MI classes that accept positional

arguments. Perhaps that’s true in the most general sense of MI with

arbitrary keyword only arguments being added by subclasses, but I don’t

think its true of any use of positional arguments.

> [@steven.daprano](#):
>
> What about alternative constructors? It would be very odd, and confusing, if this magical behaviour applied to `B()` but not alternate constructors like `B.from_items()`.

Géry:

"I am not sure that I follow you here since `B.from_items()` delegates

to `B()`, right?"

You can’t make that assumption. `B.from_items()` may be completely

independent, or perhaps `B()` delegates to `from_items`, or both

delegate to some other method. I’ve written classes which do all of

those things.

Remember that in Python there’s very little special about either

` __new__ ` or ` __init__ `, and you can construct a new instance in any

method. So while it might be common for alternative constructors to

delegate to ` __new__ `, it isn’t necessary or mandatory.
