Question

I was browsing some of the code from the serge pygame engine and I cam across something in serge.serialize that I couldn't wrap my head around:

def __setstate__(self, state=None):
        """Initialize the object to the given state for unpickling"""
        self.initial_properties = Bag()
        #
        # Initialize first from the defaults and then from the live state
        for this_state in (self.__class__._getProperties(), state):
            if this_state:
                for name, value in this_state:
                    setattr(self, name, value)
                    setattr(self.initial_properties, name, value)

This method was retrieved from the Serializable object. What I'm confused about is why the state parameter in __setstate__ has a default value of None. Why wouldn't a state be sent to __setstate__ upon unpickling an object, and in what situation would this be useful?

Was it helpful?

Solution

This isn't useful if your class relies entirely on __getstate__/__setstate__ for pickling.

As the docs on __setstate__ explain:

Upon unpickling, if the class defines __setstate__(), it is called with the unpicked state… If __getstate__() returns a false value, the __setstate__() method will not be called upon unpicking.

So, if your __getstate__ returns None, it will not be passed back to you at __setstate__ time; you just won't get called.

However, note that in the 2.x version, this is not true for classic classes. With a classic class, "If a class defines both __getstate__() and __setstate__(), the state object needn’t be a dictionary and these methods can do what they want." (In fact, I believe there are cases where any falsey value turns into {}, which isn't very well documented, but the same if statement handles that…)

Still, that doesn't explain why you'd need a default value… If you can get None, sure, you need to write code that deals with None… but you don't need to write code that doesn't get a parameter at all, right?

But, there are reasons you might do this.

First, note that this particular class's __setstate__ does more than the usual: it initializes "first from the defaults and then from the live state". So, it could very easily be useful to unit test that initializing from the defaults is working.

On top of that, if you define a custom __reduce__ method and unreducer, there's no reason that it has to follow exactly the same rules as the default unreducer. Or course there's no reason it has to call __setstate__ at all—but if you're, say, building a base class that you expect your users to subclass, making your unreducer work as much like the default as possible, so your users can just override __setstate__ instead of adding their own whole __reduce__ implementation.

Similarly, if you're building your own serializer on top of pickle/copyreg, rather than just using it as-is, your code doesn't have to use the exact same rules as pickle. Again, there's no reason it has to use even similar rules, but doing so might make it much easier for your users to extend your classes.

Whether any of these applies to serge, I have no idea, but they're both things that could easily apply to a wide range of types you might want to build.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top