pickle:它如何pickle函数?

2024-06-11 09:15:09 发布

您现在位置:Python中文网/ 问答频道 /正文

在我昨天发布的post中,我意外地发现更改函数的__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)

通过运行代码段可以看到,hahahaabcabc都无法被pickle,因为出现了异常:

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

我真的被这个例外弄糊涂了

  1. pickle对函数进行pickle时,它寻找什么?虽然hahaha__qualname__已更改为“sdfsdf”,但函数hahaha及其副本abcabc仍然可以在会话中调用(就像在dir(sys.modules['__main__'])中一样),那么为什么pickle无法对它们进行pickle呢

  2. 更改函数的__qualname__的实际效果是什么?我理解,将hahaha__qualname__更改为'sdfsdf'不会使sdfsdf可调用,因为它不会出现在dir(sys.modules['__main__'])中。但是,通过运行代码段可以看到,在将hahaha__qualname__更改为“sdfsdf”之后,对象hahaha及其副本abcabc已更改为类似<function sdfsdf at 'some_address'>的内容。sys.modules['__main__']<function sdfsdf at 'some_address'>中的对象之间有什么区别


Tags: of函数modulesmainsysfunctionpickleprint
1条回答
网友
1楼 · 发布于 2024-06-11 09:15:09

函数对象的pickle在^{} method in pickle.py中定义:

首先,通过__qualname__检索函数名:

name = getattr(obj, '__qualname__', None)

之后,在检索模块后,将重新导入该模块:

__import__(module_name, level=0)
module = sys.modules[module_name]

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

obj2, parent = _getattribute(module, name)

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


你可以做到这一点,但你必须始终如一:

>>> 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.'

相关问题 更多 >