On Tue, Aug 8, 2017 at 6:08 PM, Ian Pilcher <arequip...@gmail.com> 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?

It's initialized by the superclass call to __new__. frozenset is
immutable, and __init__ methods of immutable types generally don't do
anything (if they could, then they wouldn't be immutable), which is
why it doesn't really matter that you didn't call it. At the same
time, it generally doesn't hurt to call it, and you probably shouldn't
even have an override of __init__ here if it doesn't do anything.

It seems a bit inefficient that you create *two* sets in __new__ and
then map one of them to the other in your registry. Why not just
create the UniqueSet and then map it to itself if it's not already
registered? Something like this (untested):

def __new__(cls, *args, **kwargs):
    self = super(UniqueSet, cls).__new__(cls, *args, **kwargs)
    return UniqueSet._registry.setdefault(self, self)

Reply via email to