Search code examples
pythonself-referencenamedtuplepython-dataclasses

How to construct circular referencing instances of a frozen class in python


I have instances of a dataclass that reference each other.

from dataclasses import dataclass

@dataclass()
class Foo:
    id: int
    neighbor: 'Foo'
        
foo = Foo(1, None)
bar = Foo(2, foo)
foo.neighbor = bar

I really want a frozen class, since these objects must not be manipulated in a multi threaded run. But if I declare frozen=True, the last line will obviously raise an error. I cannot think of a way how to deal with this. I read this thread but the solution does not work for me, since foo.neighbor should point at another frozen instance.

Is there any way to achieve this? I am not bound to dataclasses. But I encounter the same problem using namedtuples.


Solution

  • frozen works by overriding __setattr__. You can by pass __setattr__ altogether by accessing your instance's __dict__ attribute directly.

    foo.__dict__['neighbor'] = bar
    

    I don't know if this will have any unintended side effects in general (and certainly won't work if you use __slots__ to prevent __dict__ from being created), but it might be sufficient for your use case.

    (This may also fail in future versions of dataclass, if the implementation changes to use a C extension rather than the current approach of dynamically generating source code to pass to exec. namedtuple evolved along those lines, I believe.)


    Or, you can use the same trick that dataclasses itself uses to initialize the attributes of a frozen class: use object.__setattr__ explicitly.

    object.__setattr__(foo, 'neighbor', bar)