e2nIEE / pandapower

Convenient Power System Modelling and Analysis based on PYPOWER and pandas
https://www.pandapower.org
Other
889 stars 484 forks source link

Possible adaptation of JSONSerializableClass #528

Closed dlohmeier closed 4 years ago

dlohmeier commented 5 years ago

There is something I wanted to document with respect to the JSONSerializableClass. Currently the way it is restored from json is that a dictionary is read from the json file and then handed over to the from_dict method. This dictionary has to contain the '_init' parameters saved by calling update_initialized and the '_state' parameters just extracted from the __dict__ right before saving to json. The object is then restored by calling the constructor with the '_init' parameters and updating the __dict__ with the '_state' parameters.

As far as I found out in a small research it should be possible to create an object of the inheriting class with the __new__() operator and then just updating the __dict__ with the '_state' parameters. I see two advantages in this:

  1. It is not necessary to always call update_initialized which makes it simpler for anyone who wants to create his own new class.
  2. If an object of JSONSerializable contains another object of JSONSerializable, this one is contained in both the '_init' and the '_state' parameters. Therefore it will be initialized twice and once just thrown away once the __dict__ is updated.

What we probably still need is a workaround for monkey-patched methods as they are not contained in the __dict__ of the object. But I think this is also not yet considered in the current version, as far as I know.

Unfortunately, this all doesn't seem to be very trivial, so it would take some time that I don't have at the moment. I will see if I can look into this soon, but if someone else will work on this class, maybe that person can have a look at my proposal and implement it as well or get in contact with me.

dlohmeier commented 5 years ago

One additional aspect is how to save and initialize monkey patched methods. The following would be possible:

import marshal, base64

def func1(x):
    return x + 1

a = lambda x: x + 2

f1_string = base64.b64encode(marshal.dumps(func1.__code__))
f1_copy = types.FunctionType(marshal.loads(base64.b64decode(f1_string)), globals())

a_string = base64.b64encode(marshal.dumps(a.__code__))
a_copy = types.LambdaType(marshal.loads(base64.b64decode(a_string)), globals())

print(func1(1000), f1_copy(1000), a(1000), a_copy(1000))

The main problems with this implementation are:

Alternatively, if functions are defined within a certain namespace, it should be possible to load them from this namespace, as it is done with basically all parts of a pandapower network. It should be worth discussing which way to go (maybe both ways should be implemented, so that only anonymous functions directly monkey patched into the class will be saved as byte strings, the rest will be tried to retrieve from the namespace)...

rbolgaryn commented 5 years ago

I have implemented the approach with new on my branch. There are some points to think about:

At the moment, further todos are:

rbolgaryn commented 5 years ago

Tests are running through now -> #559

rbolgaryn commented 5 years ago