__new__ and __init__ - why does this work?

Steve D'Aprano steve+python at pearwood.info
Wed Aug 9 08:54:00 EDT 2017


On Wed, 9 Aug 2017 10:08 am, Ian Pilcher wrote:

> I have created a class to provide a "hash consing"[1] set.

Your footnote for [1] appears to be missing. What's a hash consing set? It
appears to be nothing more than frozen sets which you put in a cache so as to
confuse identity and value *wink*

I doubt very much you're actually saving any time, since you create a temporary
frozen set before returning the one in the cache. You might save some memory
though.


>    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__).

Since frozensets are immutable, they have to be initialised in the __new__
constructor, because by the time __init__ is called the instance is immutable
and cannot be updated.

Your call to super() above creates a new instance. Its effectively a frozenset,
except that the class of it is set to your subclass ("cls", in this case). So
all the initalisation of the frozenset happens inside frozenset.__new__, which
you call via super().

Your __init__ method does nothing. Get rid of it and save two lines of code :-)

Also, there is at least theoretically the vague possibility that
frozenset.__init__ does something (phones home to Guido?) so you shouldn't
block it if you don't need to.

> Is this a particular behavior of frozenset, or am I missing something
> about the way that __new__ and __init__ interact?

Its not just frozenset. Any mutable class must initialise its instances in
__new__. Immutable classes can use either __new__ or __init__, but for
historical reasons typically use __init__.

The way __new__ and __init__ are called is:

(1) __new__ is called;

(2) if it returns an instance of cls, then instance.__init__ is called


(It is not mandatory for __new__ to return a new instance of its class; it can
return whatever you like. That is a feature, and there are occasional uses for
it.)



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list