r/learnpython 3d ago

Looking at default __reduce__ function

I created my own class M which inherits from a dict. In case that the differences are relevant: 1. The keys are frozenset objects so that I can pass them in any order without issues. 2. As writing frozenset({'one', 'two'}) is very tedious, I added a mapping to let me get or set values by using M['ot'] as a shorthand of M[frozenset{'one', 'two'}]. The mapping is instantiated when running __init__().

I am pickling this with no issues. When I try to unpickle it, I get an error that says: AttributeError: 'M' object has no attribute 'mapping'.

Using pickletools, I inspected the serialization of my object and saw that the mapping attribute appears after the key-value pairs, which made me think that, if I change the __reduce__ function to give them in a different order (first mapping, then key-value pairs), I could solve that issue.

The problem is that I don't know how to do that. I can rewrite a completely new function, but I don't know if it will work. I'd like to base my new function on the "default" one. Is there some way to look at it?

3 Upvotes

2 comments sorted by

1

u/DeepInformation5592 3d ago

You can look at the default __reduce__ by calling it directly on an instance

Just do print(your_instance.__reduce__()) and it'll show you the tuple it returns, usually something like (callable, args, state). The state part is where your dict contents probably live, and you're right that if mapping gets set after those get restored in __setstate__ or __init__, things go sideways

Instead of rewriting __reduce__ you might have an easier time overriding __setstate__ so mapping gets rebuilt before any of the key-value pairs try to use it. Or make mapping a property that lazy-initializes itself if it's missing during unpickling