__new__ and __init__ - why does this work?

Peter Otten __peter__ at web.de
Wed Aug 9 04:14:34 EDT 2017


Ian Pilcher wrote:

> I have created a class to provide a "hash consing"[1] set.
> 
>    class UniqueSet(frozenset):
> 
>        _registry = dict()
> 
>        def __new__(cls, *args, **kwargs):
>            set = frozenset(*args, **kwargs)
>            try:
>                return UniqueSet._registry[set]
>            except KeyError:
>                self = super(UniqueSet, cls).__new__(cls, *args, **kwargs)
>                UniqueSet._registry[set] = self
>                return self
> 
>        def __init__(self, *args, **kwargs):
>            pass
> 
> I can't figure out how it works, though.  In particular, I can't figure
> out how the call to __new__ actually initializes the set (since my
> __init__ never calls the superclass __init__).
> 
> Is this a particular behavior of frozenset, or am I missing something
> about the way that __new__ and __init__ interact?

I think __init__() is called to initialise the object after it has been 
created with __new__(), roughly the following, run by the metaclass:

obj = UniqueSet_.__new__(UniqueSet, ...)
if isinstance(obj, UniqueSet):
    obj.__init__(...)

As Ian says, for immutable classes __init__() usually does nothing, as by 
definition the instance cannot be changed once created:

>>> t = tuple.__new__(tuple, "ab")
>>> t
('a', 'b')
>>> t.__init__(None)
>>> t.__init__(x=42)
>>> t.__init__("whatever", "you", "like")
>>> t
('a', 'b')

I'm a bit surprised that the signature isn't restricted to a single 
positional argument...




More information about the Python-list mailing list