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
picklelook for when it pickles a function? Although the__qualname__ofhahahawas changed to 'sdfsdf', the functionhahahaas well as its copyabcabcis still callable in the session (as they are indir(sys.modules['__main__'])), then whypicklecannot pickle them?What is the real effect of changing the
__qualname__of a function? I understand by changing the__qualname__ofhahahato 'sdfsdf' won't makesdfsdfcallable, as it won't show up indir(sys.modules['__main__']). However, as you can see by running the snippets, after changing the__qualname__ofhahahato 'sdfsdf', the objecthahahaas well as its copyabcabchas 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'>?