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

Posted

技术标签:

【中文标题】如何解开其类存在于不同命名空间(python)中的对象?【英文标题】:How to unpickle an object whose class exists in a different namespace (python)? 【发布时间】:2013-01-09 15:36:50 【问题描述】:

如果我有一个定义类的脚本:

script = """

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

"""

然后在它自己的命名空间字典中执行这个脚本:

NS = 
exec script in NS

然后创建一个类的实例并腌制它:

a = NS['myClass']()

import pickle

save = pickle.dumps(a)

现在如果我尝试解开它:

load = pickle.loads(save)

我得到了错误

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

我认为这不起作用,因为 python 不知道在哪里可以找到 myClass 以重建对象。但是 myClass 确实存在于 NS 字典中。有没有办法告诉 pickle 在哪里可以找到它正在加载的对象的类?

【问题讨论】:

【参考方案1】:

我发现了一个解决方案。似乎问题是在 dict 中执行代码会阻止 python 找出类的定义位置。解决方法是创建一个空模块,执行模块中的代码,然后将该模块添加到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

现在可以腌制和取消腌制类的实例:

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

【讨论】:

这个解决方案有点 hackish IMO。但它有效!所以谢谢。就我而言,我无法控制泡菜的接收端。否则可以使用 Borealid 的答案【参考方案2】:

您实际上可以更进一步,让对象将自身重构为您想要的任何类型。

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

基本思想是将您的类打包成某种“特洛伊木马”,在这种情况下,它的重建会导致实例化与原来不同的类。

酸洗侧的otherclass 包含什么无关紧要。重要的是它与“目标”类存在于同一模块路径中 - pickle 只是将模块名称的字符串表示形式放入序列化流中。

所以,详细分解上面代码中发生的事情:

我们为myClass 注册了一个自定义pickler。这可以通过copy_reg__reduce_ex__ 函数来完成。 我们的自定义pickler 说“pickle this as an instance of otherclass”(这是一个假人。您不需要在酸洗方面需要otherclass 的“真实”内容,因为所有进入泡菜的是模块/类名)。 我们腌制对象并“将其通过网络发送”到otherclass 的真实版本所在的位置。 在远程端,otherclass 使用自定义酸洗函数返回的元组中的数据进行实例化。

Python 可以非常强大!

【讨论】:

这一切都很整洁,但它如何回答最初的问题? @JohnY 您可以将类从 unpickler 的命名空间中 unpickle 到任意类的实例中。您将myClass 设置为foomodule.myClass,然后在调用pickle.loads 之前说foomodule.myClass = NS['myClass']

以上是关于如何解开其类存在于不同命名空间(python)中的对象?的主要内容,如果未能解决你的问题,请参考以下文章

删除存在于另一个命名空间 rails 中的模型

列出存在于另一个模型中的所有关联模型记录,该模型存在于 rails 中的另一个命名空间中

python高阶函数

python基础 13 类命名空间于对象实例的命名空间,组合方法

如何解决 .net 标准中的命名空间“System.Configuration”错误中不存在类型或命名空间名称“ConfigurationManager”?

解读Python的命名空间