简体   繁体   English

如何解析其类存在于不同命名空间(python)中的对象?

[英]How to unpickle an object whose class exists in a different namespace (python)?

If I have a script that defines a class: 如果我有一个定义类的脚本:

script = """

class myClass:
    def __init__(self):
        self.name = 'apple'
        self.color = 'green'

"""

and then exec this script in its own namespace dict: 然后在自己的命名空间dict中执行此脚本:

NS = {}
exec script in NS

and then create an instance of the class and pickle it: 然后创建一个类的实例并将其pickle:

a = NS['myClass']()

import pickle

save = pickle.dumps(a)

Now if I try to unpickle it: 现在,如果我试图取消它:

load = pickle.loads(save)

I get the error 我收到了错误

AttributeError: 'module' object has no attribute 'myClass'

I gather that this doesn't work because python doesn't know where to find myClass in order to rebuild the object. 我认为这不起作用,因为python不知道在哪里找到myClass以重建对象。 But myClass does exist in the NS dict. 但myClass确实存在于NS dict中。 Is there a way to tell pickle where to find the class for the object it is loading? 有没有办法告诉pickle在哪里找到它正在加载的对象的类?

You can actually go one step further, and have the object reconstruct itself into whatever type you want. 您实际上可以更进一步,让对象重建为您想要的任何类型。

import pickle
import copy_reg

class myClass(object):
    def __init__(self):
        self.apple = 'banana'

class otherclass(object):
    def __init__(self):
        self.apple = 'existential woe'

def pickle_an_object(o):
    print "pickling %s" % str(o)
    return otherclass, (o.apple,)

copy_reg.pickle(myClass, pickle_an_object)

foo = myClass()

s = pickle.dumps(foo)

del myClass
del otherclass

class otherclass(object):
    def __init__(self, appletype):
        self.apple = 'not %s' % appletype

o2 = pickle.loads(s)

print o2.apple

The basic idea is that you pack your class into a "trojan horse" of sorts, where its reconstruction causes an instantiation of a different class from what it originally was. 基本的想法是,你把你的班级打包成各种各样的“特洛伊木马”,它的重建导致了与原来不同的类的实例化。

It does not matter what the otherclass on the pickling side contains. 不要紧 ,什么otherclass在酸洗面包含的内容。 All that matters is that it exist at the same module path as the "destination" class - pickle is just putting a string representation of the module name into the serialized stream. 重要的是它存在于与“目标”类相同的模块路径中 - pickle只是将模块名称的字符串表示放入序列化流中。

So, to break down what's happening in the above code in detail: 那么,要详细分解上面代码中发生的事情:

  • We register a custom pickler for myClass . 我们为myClass注册了一个自定义pickler。 This can be done via copy_reg or the __reduce_ex__ function. 这可以通过copy_reg__reduce_ex__函数完成。
  • Our custom pickler says "pickle this as an instance of otherclass " (which is a dummy. You do not need the "real" contents of otherclass on the pickling side, because all that goes into the pickle is the module/class name). 我们的定制Pickler会说“泡菜将此作为一个实例otherclass ”(这是一个虚拟的。你并不需要的“真正的”内容otherclass在酸洗面,因为所有进入泡菜是模块/类名)。
  • We pickle the object and "send it across the wire", to where the real version of otherclass exists. 我们挑选对象并“通过网络发送”到其他类的真实版本所在的otherclass
  • On the remote side, otherclass is instantiated with the data from the tuple returned by the custom pickling function. 在远程端,使用自定义otherclass函数返回的元组中的数据实例化otherclass

Python can be pretty powerful! Python可以非常强大!

I discovered a solution this. 我发现了一个解决方案。 It seems the problem is executing code in a dict prevents python from figuring out where the class is defined. 似乎问题是在dict中执行代码会阻止python找出定义类的位置。 The solution is to create an empty module, execute the code in the module, and then add the module to sys.modules so python knows about it. 解决方案是创建一个空模块,执行模块中的代码,然后将模块添加到sys.modules,以便python知道它。

script = """
class myClass:
    def __init__(self):
        self.name = 'apple'
        self.color = 'green'
"""

import imp, sys

moduleName = 'custom'

module = imp.new_module(moduleName)

exec script in module.__dict__

sys.modules[moduleName] = module

Now it is possible to pickle and unpickle an instance of the class: 现在可以pickle和unpickle类的实例:

import pickle
a = module.myClass()
s = pickle.dumps(a)
b = pickle.loads(s)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM