4

I have a class where I would like to instantiate the object in several ways depending on the arguments. When I pass a filepath to the function __init__ it should restore all parameters from the object saved as a pickle file. Is there any smart way to do so, something like self=load(...)

martineau
  • 119,623
  • 25
  • 170
  • 301
freude
  • 3,632
  • 3
  • 32
  • 51

3 Answers3

6

Doing self=load(...) in your __init__ only masks the local self variable in the __init__, it does not change the instance.

You can instead control the creation of the new instance in the __new__ method of the class.

import pickle

class Pretty(object):
    def __new__(cls, filepath=None, *args, **kwargs):
        if filepath:
            with open(filepath) as f:
               inst = pickle.load(f)
            if not isinstance(inst, cls):
               raise TypeError('Unpickled object is not of type {}'.format(cls))
        else:
            inst = super(Pretty, cls).__new__(cls, *args, **kwargs)
        return inst

You can do a quick instance check of the unpickled object to ensure it is actually an instance of your class, otherwise you can expect bad behavior such as the __init__ method of your class not being called.

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
3

As stated in another answer, self = load(...) only replaces the local variable self. The same answer recommends resorting to the __new__ method of the class which works but isn't a good practice in python, as overriding this method should be used for immutable types only.

Instead, you should be using a factory, which can in this case be a simple function :

def foo_factory(filename=None, *args, **kwargs):
    if filename:
        foo_factory.foo = pickle.load(filename)
    else:
        foo_factory.foo = Foo(*args, **kwargs)
    return foo_factory.foo

where we supposed that class Foo is the class of interest.

(see Why is __init__() always called after __new__()? for more details and references)

rdbs
  • 31
  • 4
1

This is probably not a "smart way" but if you prefer a third option (besides new and factory):

class myclass:

  def __init__(self, filename):

    with open(filename, 'rb') as f:
      myinstance = pickle.load(f)

    for k in myinstance.__dict__.keys():
      setattr(self, k, getattr(myinstance, k))

This loops through all properties of the loaded instance (myinstance) and sets the corresponding ones in the instance that is initialized (self).

ChrisMM
  • 8,448
  • 13
  • 29
  • 48
Peter
  • 11
  • 1