In a post I posted yesterday, I accidentally found changing the __qualname__
of a function has an unexpected effect on pickle
. By running more tests, I found that when pickling a function, pickle
does not work in the way I thought, and changing the __qualname__
of the function has a real effect on how pickle
behaves.
The snippets below are tests I ran,
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)
As you can see by running the snippets, both hahaha
and abcabc
cannot be pickled because of the exception:
Can't pickle <function sdfsdf at 0x7fda36dc5f28>: attribute lookup sdfsdf on __main__ failed
.
I'm really confused by this exception,
What does
pickle
look for when it pickles a function? Although the__qualname__
ofhahaha
was changed to 'sdfsdf', the functionhahaha
as well as its copyabcabc
is still callable in the session (as they are indir(sys.modules['__main__'])
), then whypickle
cannot pickle them?What is the real effect of changing the
__qualname__
of a function? I understand by changing the__qualname__
ofhahaha
to 'sdfsdf' won't makesdfsdf
callable, as it won't show up indir(sys.modules['__main__'])
. However, as you can see by running the snippets, after changing the__qualname__
ofhahaha
to 'sdfsdf', the objecthahaha
as well as its copyabcabc
has changed to something like<function sdfsdf at 'some_address'>
. What is the difference between the objects insys.modules['__main__']
and<function sdfsdf at 'some_address'>
?