【问题标题】:pickle: how does it pickle a function?pickle:它如何腌制一个函数?
【发布时间】:2020-09-28 02:50:24
【问题描述】:

在我昨天发的一个post中,无意中发现改变一个函数的__qualname__pickle有意想不到的效果。通过运行更多的测试,我发现在 pickling 一个函数时,pickle 并没有像我想象的那样工作,而更改函数的 __qualname__pickle 的行为方式有真正的影响。

下面的sn-ps是我跑的测试,

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)

运行 sn-ps 可以看到,hahahaabcabc 都不能因为异常而被腌制:

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

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

  1. pickle 在腌制函数时会寻找什么?尽管hahaha__qualname__ 已更改为'sdfsdf',但函数hahaha 及其副本abcabc 仍然可以在会话中调用(因为它们在dir(sys.modules['__main__']) 中),那么为什么pickle不能腌制吗?

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

【问题讨论】:

    标签: python python-3.x pickle


    【解决方案1】:

    函数对象的酸洗定义在save_global 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,因此酸洗在此处失败。


    你可以做到这一点,但你必须保持一致:

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

    【讨论】:

      猜你喜欢
      • 2018-10-30
      • 2012-04-20
      • 1970-01-01
      • 2011-05-08
      • 1970-01-01
      • 1970-01-01
      • 2011-09-17
      • 1970-01-01
      • 2018-09-24
      相关资源
      最近更新 更多