如何控制在取消python对象时导入的内容?

时间:2015-05-22 11:29:18

标签: python multiprocessing pickle

我有以下设置:

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中,我挑选了作为对象b的载体的对象a。然后我通过RabbitMQ将腌制对象b发送到另一个进程。在receiver.py(这是另一个进程)中,我通过RabbitMQ收到消息,unpickle对象b和魔术BA自动导入。我能控制进口的东西吗?我希望工作者receiver.py尽可能少地使用内存。但是如果模块在没有我控制的情况下导入,它会很快膨胀。

有人可以解释一下pickle如何进口东西以及如何处理它?<​​/ p>

3 个答案:

答案 0 :(得分:2)

它使用__module__A的{​​{1}}属性:

B

如果要控制导入的内容,可以构建python包,以便>>> A.__module__ 'a' >>> __import__(A.__module__) <module 'a' from 'a.py'> 不会加载太多对象。

答案 1 :(得分:2)

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

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

然后有一些魔力。它将一个字符串作为一个文件读出并发送它的&#39;内容基于特定密钥:

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()以符合您的目标。我的回答对你有帮助吗?

答案 2 :(得分:1)

pickle需要导入模块ab才能加载类AB,这些类需要重新构建您的宾语。我拿了你的例子,只是将a.py重命名为aaaa.py,将b.py重命名为bbbb.py.现在,如果我们打印sender.py实际发送的消息(pickled对象),你会看到:

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如何重建你的腌制对象。为了加载类,它必须导入定义类的模块。如果你试图搞乱pickle的机器并阻止模块被加载,那么就没有办法重建你的对象