__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