pickle:它如何腌制一个函数?

meT*_*sky 8 python pickle python-3.x

帖子昨天我给,我无意中发现改变__qualname__功能的对意想不到的效果pickle。通过运行更多的测试,我发现在腌制一个函数时,pickle并没有像我想象的那样工作,并且改变__qualname__函数的 对pickle行为方式有实际影响。

下面的片段是我运行的测试,

import pickle
from sys import modules

# a simple function to pickle 
def hahaha(): return 1

print('hahaha',hahaha,'\n')

# change the __qualname__ of function hahaha
hahaha.__qualname__ = 'sdfsdf'
print('set hahaha __qualname__ to sdfsdf',hahaha,'\n')

# make a copy of hahaha
setattr(modules['__main__'],'abcabc',hahaha)
print('create abcabc which is just hahaha',abcabc,'\n')

try:
    pickle.dumps(hahaha)
except Exception as e:
    print('pickle hahaha')
    print(e,'\n')

try:
    pickle.dumps(abcabc)
except Exception as e:
    print('pickle abcabc, a copy of hahaha')
    print(e,'\n')

try:
    pickle.dumps(sdfsdf)
except Exception as e:
    print('pickle sdfsdf')
    print(e)
Run Code Online (Sandbox Code Playgroud)

你可以通过运行片断看到的,hahahaabcabc不能因为异常的腌制:

Can't pickle <function sdfsdf at 0x7fda36dc5f28>: attribute lookup sdfsdf on __main__ failed.

我真的被这个异常搞糊涂了,

  1. pickle当它腌制一个函数时寻找什么?尽管__qualname__ofhahaha已更改为 'sdfsdf',但该函数hahaha及其副本abcabc仍可在会话中调用(就像它们在 中一样dir(sys.modules['__main__'])),那为什么pickle不能pickle 呢?

  2. 改变__qualname__函数的实际效果是什么?我知道通过将__qualname__of更改hahaha为 'sdfsdf' 不会成为sdfsdf可调用的,因为它不会出现在dir(sys.modules['__main__']). 但是,正如您通过运行片段所看到的,在将__qualname__of更改hahaha为“sdfsdf”后,对象hahaha及其副本abcabc已更改为类似<function sdfsdf at 'some_address'>. sys.modules['__main__']和 中的对象有什么区别<function sdfsdf at 'some_address'>

L3v*_*han 5

函数对象的酸洗save_global在 pickle.py中的方法中定义

首先,通过__qualname__以下方式检索函数的名称:

name = getattr(obj, '__qualname__', None)
Run Code Online (Sandbox Code Playgroud)

之后,在检索模块后,它被重新导入:

__import__(module_name, level=0)
module = sys.modules[module_name]
Run Code Online (Sandbox Code Playgroud)

module然后使用这个新导入的函数来查找作为属性的函数:

obj2, parent = _getattribute(module, name)
Run Code Online (Sandbox Code Playgroud)

obj2现在将是该函数的新副本,但由于sdfsdf此模块中不存在,因此此处酸洗失败。


您可以完成这项工作,但您必须保持一致:

>>> import sys
>>> import pickle
>>> def hahaha(): return 1
>>> hahaha.__qualname__ = "sdfsdf"
>>> setattr(sys.modules["__main__"], "sdfsdf", hahaha)
>>> pickle.dumps(hahaha)
b'\x80\x04\x95\x17\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06sdfsdf\x94\x93\x94.'
Run Code Online (Sandbox Code Playgroud)