unpickle python 对象时如何控制导入的内容?

How to control what gets imported when you unpickle python object?

我有以下设置:

a.py:

class A(object):
    def __init__(self, name):
        self.name = name
    def a(self):
        print('yow {}!'.format(self.name))

b.py:

class B(object):
    def __init__(self, obj):
        self.obj = obj

sender.py:

from a import A
from b import B
message = pickle.dumps(B(A('Martin')))

receiver.py:

my_b = pickle.loads(message)
my_a = my_b.obj
my_a.a()

输出:yow Martin!

sender.py 中,我 pickle 对象 b 作为对象 a 的载体。然后我通过 RabbitMQ 将那个腌制对象 b 发送到 另一个进程 。在 receiver.py(这是另一个进程)中,我通过 RabbitMQ 收到一条消息,unpickle 对象 b 并通过魔法 BA 自动导入。我可以控制导入的内容吗?我希望 worker receiver.py 消耗尽可能少的内存。但是,如果模块在我无法控制的情况下被导入,它会很快变得臃肿。

有人可以解释 pickle 如何导入东西以及如何处理它吗?

它使用AB__module__属性:

>>> A.__module__
'a'
>>> __import__(A.__module__)
<module 'a' from 'a.py'>

如果您想控制导入的内容,您可以构造您的 python 包,这样 from a import A 就不会加载太多对象。

需要什么样的控制?正如您从源代码中看到的那样,当您 运行 pickle.loads(content) 它实际上是:

def loads(str):
    file = StringIO(str)
    return Unpickler(file).load()

然后有一些魔法。它将字符串作为文件读取,并根据特定键分派其内容:

GLOBAL          = 'c'   # push self.find_class(modname, name); 2 string args
INST            = 'i'   # build & push class instance

加载函数本身:

def load(self):
    """Read a pickled object representation from the open file.
    Return the reconstituted object hierarchy specified in the file.
    """
    ...
    read = self.read  # self.read = file.read, which is StringIO's read()
    dispatch = self.dispatch
    try:
        while 1:
            key = read(1)
            dispatch[key](self) # this function call makes a future import.
   except _Stop, stopinst:
       return stopinst.value

您对方法 find_class() 感兴趣,该方法用于其他几个 load functionsload_inst()load_global()):

def find_class(self, module, name):
    # Subclasses may override this:
    __import__(module)  # straight-forward import, you can ovveride it.
    mod = sys.modules[module]
    klass = getattr(mod, name)
    return klass

例如,load_inst()函数:

def load_inst(self):
    module = self.readline()[:-1]
    name = self.readline()[:-1]
    klass = self.find_class(module, name)
    # Now module is imported and ready to be used:
    self._instantiate(klass, self.marker())
dispatch[INST] = load_inst

因此,如果您想要控制可以导入的名称空间或模块,您将需要子类化 Unpickler 并覆盖 find_class() 以符合您的目标。我的回答对您有帮助吗?

pickle 需要导入模块 ab 以加载 类 AB,它们是需要重建你的对象。我以你的例子为例,只是将 a.py 重命名为 aaaa.py,将 b.py 重命名为 bbbb.py。现在,如果我们打印 sender.py 实际发送的消息(腌制对象),您会看到:

ccopy_reg
_reconstructor
p0
(cbbbb
B
p1
c__builtin__
object
p2
Ntp3
Rp4
(dp5
S'obj'
p6
g0
(caaaa
A
p7
g2
Ntp8
Rp9
(dp10
S'name'
p11
S'Martin'
p12
sbsb.

您不需要理解所有这些,但请注意 bbbb 后跟 Baaaa 后跟 A .那是告诉 pickle 如何重建你的 pickled 对象。为了加载 类,它必须导入定义了 类 的模块。如果你试图扰乱 pickle 的机器并阻止模块被加载,就不会有重建对象的方法。