在尝试使用How to create a copy of a python function技术在python中克隆函数时遇到一个非常奇怪的问题
显示问题的最小代码:
import dill
import pickle
import types
def foo():
print ('a')
fooCopy=types.FunctionType(foo.__code__, foo.__globals__, 'IAmFooCopied',foo.__defaults__ , foo.__closure__)
print ( 'printing foo and the copy', fooCopy, foo )
print ( 'dill output: ', dill.dumps(fooCopy ))
print ( 'pickle Output: ', pickle.dumps (fooCopy) )
输出:
printing foo and the copy <function foo at 0x7fb6ec6349d8> <function foo at 0x7fb6ed41a268>
dill output: b'\x80\x03cdill._dill\n_create_function\nq\x00(cdill._dill\n_load_type\nq\x01X\x08\x00\x00\x00CodeTypeq\x02\x85q\x03Rq\x04(K\x00K\x00K\x00K\x02KCC\x0ct\x00d\x01\x83\x01\x01\x00d\x00S\x00q\x05NX\x01\x00\x00\x00aq\x06\x86q\x07X\x05\x00\x00\x00printq\x08\x85q\t)X\x10\x00\x00\x00testCloneFunc.pyq\nX\x03\x00\x00\x00fooq\x0bK\x05C\x02\x00\x01q\x0c))tq\rRq\x0ec__builtin__\n__main__\nX\x0c\x00\x00\x00IAmFooCopiedq\x0fNN}q\x10tq\x11Rq\x12.'
Traceback (most recent call last):
File "testCloneFunc.py", line 12, in <module>
print ( 'pickle Output: ', pickle.dumps (fooCopy) )
_pickle.PicklingError: Can't pickle <function foo at 0x7fb6ec6349d8>: it's not the same object as __main__.foo
我发现的第一件事很奇怪,如果打印出副本,您将获得与原始名称相同的名称,我希望它是'IAmFooCopied'。
然后我为泡菜的错误而欺骗,认为这两个对象是相同的。
一些有关此泡菜错误的文档:https://code.google.com/archive/p/modwsgi/wikis/IssuesWithPickleModule.wiki
但是我真的不明白为什么泡菜看不到这两个功能不一样。我可以使用任何快速修复程序吗?
编辑:FunctionType的name参数似乎没有设置函数的co_name,而是设置了 qualname 。因此,通过重新创建代码对象,我修复了旧错误,只是遇到了这个错误:
import dill
import pickle
import types
def foo():
print ('a')
oldCode=foo.__code__
name='IAmFooCopied'
newCode= types.CodeType(
oldCode.co_argcount, # integer
oldCode.co_kwonlyargcount, # integer
oldCode.co_nlocals, # integer
oldCode.co_stacksize, # integer
oldCode.co_flags, # integer
oldCode.co_code, # bytes
oldCode.co_consts, # tuple
oldCode.co_names, # tuple
oldCode.co_varnames, # tuple
oldCode.co_filename, # string
name, # string
oldCode.co_firstlineno, # integer
oldCode.co_lnotab, # bytes
oldCode.co_freevars, # tuple
oldCode.co_cellvars # tuple
)
fooCopy=types.FunctionType(newCode, foo.__globals__, name,foo.__defaults__ , foo.__closure__)
fooCopy.__qualname__= name
print ( 'printing foo and the copy', fooCopy, foo )
print ( 'dill output: ', dill.dumps(fooCopy ))
print ( 'pickle Output: ', pickle.dumps (fooCopy) )
新输出:
printing foo and the copy <function IAmFooCopied at 0x7fee8ebb19d8> <function foo at 0x7fee8f996268>
dill output: b'\x80\x03cdill._dill\n_create_function\nq\x00(cdill._dill\n_load_type\nq\x01X\x08\x00\x00\x00CodeTypeq\x02\x85q\x03Rq\x04(K\x00K\x00K\x00K\x02KCC\x0ct\x00d\x01\x83\x01\x01\x00d\x00S\x00q\x05NX\x01\x00\x00\x00aq\x06\x86q\x07X\x05\x00\x00\x00printq\x08\x85q\t)X\x10\x00\x00\x00testCloneFunc.pyq\nX\x0c\x00\x00\x00IAmFooCopiedq\x0bK\x05C\x02\x00\x01q\x0c))tq\rRq\x0ec__builtin__\n__main__\nh\x0bNN}q\x0ftq\x10Rq\x11.'
Traceback (most recent call last):
File "testCloneFunc.py", line 38, in <module>
print ( 'pickle Output: ', pickle.dumps (fooCopy) )
_pickle.PicklingError: Can't pickle <function IAmFooCopied at 0x7fee8ebb19d8>: attribute lookup IAmFooCopied on __main__ failed
此外,dill.detect无法检测到任何问题。
答案 0 :(得分:0)
这是有效的代码:
import pickle
import dill
import types
def foo():
print ('a')
oldCode=foo.__code__
name='IAmFooCopied'
newCode= types.CodeType(
oldCode.co_argcount, # integer
oldCode.co_kwonlyargcount, # integer
oldCode.co_nlocals, # integer
oldCode.co_stacksize, # integer
oldCode.co_flags, # integer
oldCode.co_code, # bytes
oldCode.co_consts, # tuple
oldCode.co_names, # tuple
oldCode.co_varnames, # tuple
oldCode.co_filename, # string
name, # string
oldCode.co_firstlineno, # integer
oldCode.co_lnotab, # bytes
oldCode.co_freevars, # tuple
oldCode.co_cellvars # tuple
)
IAmFooCopied=types.FunctionType(newCode, foo.__globals__, name,foo.__defaults__ , foo.__closure__)
IAmFooCopied.__qualname__= name
print ( 'printing foo and the copy', IAmFooCopied, foo )
print ( 'dill output: ', dill.dumps(IAmFooCopied ))
print ( 'pickle Output: ', pickle.dumps (IAmFooCopied) )
如果函数的定义(IAmFooCopied=types.FunctionType(newCode, foo.__globals__, name,foo.__defaults__ , foo.__closure__
和namename ='IAmFooCopied'enter code here
不匹配,则pickle找不到要序列化的函数。
输出:
printing foo and the copy <function IAmFooCopied at 0x7f8a6a8159d8> <function foo at 0x7f8a6b5f5268>
dill output: b'\x80\x03cdill._dill\n_create_function\nq\x00(cdill._dill\n_load_type\nq\x01X\x08\x00\x00\x00CodeTypeq\x02\x85q\x03Rq\x04(K\x00K\x00K\x00K\x02KCC\x0ct\x00d\x01\x83\x01\x01\x00d\x00S\x00q\x05NX\x01\x00\x00\x00aq\x06\x86q\x07X\x05\x00\x00\x00printq\x08\x85q\t)X\x10\x00\x00\x00testCloneFunc.pyq\nX\x0c\x00\x00\x00IAmFooCopiedq\x0bK\x05C\x02\x00\x01q\x0c))tq\rRq\x0ec__builtin__\n__main__\nh\x0bNN}q\x0ftq\x10Rq\x11.'
pickle Output: b'\x80\x03c__main__\nIAmFooCopied\nq\x00.'
答案 1 :(得分:0)
我不确定您要在这里做什么...但要明确一点-dill
可以正常工作。
>>> import dill
>>> import pickle
>>> import types
>>>
>>> def foo():
... print ('a')
...
>>> fooCopy=types.FunctionType(foo.__code__, foo.__globals__, 'IAmFooCopied',foo.__defaults__ , foo.__closure__)
>>>
>>> dill.loads(dill.dumps(foo))
<function foo at 0x1058172a8>
>>> dill.loads(dill.dumps(fooCopy))
<function IAmFooCopied at 0x105817320>
>>>
pickle
之所以失败,是因为它通常在许多情况下无法序列化用户构建的函数,因为它会通过引用(即,对其内置模块的引用)对函数进行序列化。您可以在转储的字符串中看到pickle
基本上存储了一个字符串,该字符串等于要使用的序列化版本的前缀,然后是模块的名称(__main__
),然后是函数的名称( 'IAmFooCopied')。另一方面,dill
确实可以手动完成您的操作。请参见https://github.com/uqfoundation/dill/blob/master/dill/_dill.py,请参见save_function
和_create_function
。