pickle: Wie wird eine Funktion gebeizt?
In einem Beitrag, den ich gestern gepostet habe, habe ich versehentlich festgestellt, dass das Ändern __qualname__einer Funktion unerwartete Auswirkungen auf hat pickle. Durch das Ausführen weiterer Tests stellte ich fest, dass das Beizen einer Funktion picklenicht so funktioniert, wie ich es mir vorgestellt habe, und dass das Ändern __qualname__der Funktion einen echten Einfluss auf das Verhalten hat pickle.
Die folgenden Ausschnitte sind Tests, die ich durchgeführt habe.
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)
Wie Sie durch Ausführen der Snippets sehen können, können beide hahahaund abcabcaufgrund der Ausnahme nicht eingelegt werden:
Can't pickle <function sdfsdf at 0x7fda36dc5f28>: attribute lookup sdfsdf on __main__ failed.
Ich bin wirklich verwirrt von dieser Ausnahme,
Was sucht es
pickle, wenn es eine Funktion auswählt? Obwohl das__qualname__ofhahahain 'sdfsdf' geändert wurde, können sowohl die Funktionhahahaals auch die Kopieabcabcin der Sitzung (wie sie sich gerade befindendir(sys.modules['__main__'])) aufgerufen werden. Warumpicklekönnen sie dann nicht ausgewählt werden?Was ist der wirkliche Effekt einer Änderung der
__qualname__Funktion? Ich verstehe, dass das Ändern von__qualname__vonhahahain 'sdfsdf' nichtsdfsdfaufrufbar macht, da es nicht in angezeigt wirddir(sys.modules['__main__']). Wie Sie jedoch sehen können, indem Sie die Snippets ausführen , haben sich nach dem Ändern von__qualname__ofhahahain 'sdfsdf' sowohl das Objekthahahaals auch seine Kopieabcabcin etwas wie geändert<function sdfsdf at 'some_address'>. Was ist der Unterschied zwischen den Objekten insys.modules['__main__']und<function sdfsdf at 'some_address'>?
Antworten
Das Beizen von Funktionsobjekten wird in der save_globalMethode in pickle.py definiert :
Zunächst wird der Name der Funktion abgerufen über __qualname__:
name = getattr(obj, '__qualname__', None)
Nach dem Abrufen des Moduls wird es erneut importiert:
__import__(module_name, level=0)
module = sys.modules[module_name]
Dieser frisch importierte modulewird dann verwendet, um die Funktion als Attribut nachzuschlagen:
obj2, parent = _getattribute(module, name)
obj2wäre jetzt eine neue Kopie der Funktion, aber da sdfsdfin diesem Modul keine vorhanden ist, schlägt das Beizen hier fehl.
Sie können diese Arbeit machen, aber Sie müssen konsequent sein:
>>> 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.'