0

I have a float subclass defined as follows:

class NewFloat(float):
    def __new__(cls, value, value2):
        r = super().__new__(cls, value)
        r.value = value2
        return r

When I create an instance of this class, all works well -- a = NewFloat(1, 2) gives an object a such that a.value returns 2. However, when I pickle the object (pickle.dump(a, open('file.p', 'wb'))) and try to load it (pickle.load(open('file.p', 'rb'))), it throws the following error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-75-ff08f7f793b8> in <module>
----> 1 b = pickle.load(open('test_pickle.p', 'rb'))

TypeError: __new__() missing 1 required positional argument: 'value2'

What is going on during the pickling process that causes this error? Is __new__ called again, and why?

I've found that adding the attribute in a function definition works fine, but I can't see why this should be any different:

class NewFloat2(float):
    def save_value(self, value):
        r.value = value

A NewFloat2 instance can be pickled and loaded, no problem. Any insight would be greatly appreciated!

Robert Yi
  • 1,553
  • 1
  • 14
  • 18

1 Answers1

0

Typically, upon pickling, obj.__class and obj.__dict__ are saved, and upon unpickleing cls.__new__(cls) and cls.__dict__.update(attrs) are executed.

That is to say, pickle won't pass extra args to your __new__() method while unpickling by default. To solve this, you must define the __getnewargs__() method in your class to tell pickle to pass those args, such as:

class NewFloat(float):
    def __new__(cls, value, value2):
        r = super().__new__(cls, value)
        r.value = value2
        r.original_value = value
        return r
    def __getnewargs__(self):
        return (self.original_value,self.value)

Then the pickle will appear as you want. You define __getnewargs_ex__() method if you want to pass keyword args too,

Note that the value argument is useless and unsaved in the instance in such an example. When it goes to normal application, you either save it in a property of the instances or remove it.

C.K.
  • 1,409
  • 10
  • 20