Dieser Artikel bietet Ihnen eine Analyse der Fallstricke der dynamischen Zuweisung. Ich hoffe, dass er Ihnen als Referenz dienen wird.
Das Thema Namespace und Geltungsbereich mag trivial erscheinen, aber tatsächlich steckt viel dahinter.
Aus Platzgründen gibt es immer noch einen wichtigen Wissensinhalt, der nicht besprochen wurde, nämlich „die Lese- und Schreibprobleme von Locals() und Globals()“. Der Grund, warum dieses Problem wichtig ist, besteht darin, dass es einige flexible dynamische Zuweisungsfunktionen implementieren kann.
Es handelt sich bei allen um Wörterbuchtypen, und ihre Verwendung ist selbsterklärend. Allerdings gibt es bei der Verwendung eine Falle zu beachten: globals() ist lesbar und beschreibbar, während locals() nur lesbar, aber nicht beschreibbar ist. Der Artikel, den ich heute geteilt habe, dient der Untersuchung dieses Themas. Er ist sehr ausführlich und ich möchte ihn gerne mit Ihnen teilen.
Bei der Arbeit stoßen wir manchmal auf eine Situation: dynamische Variablenzuweisung, egal ob es sich um eine lokale Variable oder eine globale Variable handelt. Wenn wir uns den Kopf zerbrechen, hat Python dieses Problem für uns gelöst > Der Namespace von Python wird in Form eines Wörterbuchs widergespiegelt, und die spezifischen Funktionen sind locals () und globals (), die dem lokalen Namespace bzw. dem globalen Namespace entsprechen. Daher können wir durch diese Methoden unsere „Dynamik“ realisieren Zuweisung" benötigt.
Zum Beispiel:
def test(): globals()['a2'] = 4 test() print a2 # 输出 4
Da Globals den globalen Namespace ändern können, ist es natürlich, dass Einheimische natürlich auch in der Lage sein sollten, den lokalen Namespace zu ändern. Ändern Sie lokale Variablen darin die Funktion.
Aber ist das wirklich der Fall? Nein!
def aaaa(): print locals() for i in ['a', 'b', 'c']: locals()[i] = 1 print locals() print a aaaa()
Ausgabe:
{} {'i': 'c', 'a': 1, 'c': 1, 'b': 1} Traceback (most recent call last): File "5.py", line 17, in <module> aaaa() File "5.py", line 16, in aaaa print a NameError: global name 'a' is not defined
Das Programm hat beim Ausführen einen Fehler gemeldet!
Aber in Wenn Sie locals() zum zweiten Mal drucken, können Sie deutlich erkennen, dass diese Variablen bereits im lokalen Bereich vorhanden sind und dass auch die Variable a mit dem Wert 1 vorhanden ist. Aber warum wird beim Drucken von a eine NameError-Ausnahme angezeigt?
Schauen Sie sich noch einmal ein Beispiel an:
def aaaa(): print locals() s = 'test' # 加入显示赋值 s for i in ['a', 'b', 'c']: locals()[i] = 1 print locals() print s # 打印局部变量 s print a aaaa()
Ausgabe:
{} {'i': 'c', 'a': 1, 's': 'test', 'b': 1, 'c': 1} test Traceback (most recent call last): File "5.py", line 19, in <module> aaaa() File "5.py", line 18, in aaaa print a NameError: global name 'a' is not defined
Der Unterschied besteht darin, dass der folgende Code die Zuweisung zeigt Ausgelöst wird der Wert der lokalen Variablen s.
Dies lässt uns fragen, ob sich die Änderung lokaler Variablen durch locals() von der direkten Zuweisung unterscheidet. Um dieses Problem zu lösen, können wir uns nur die Wahrheit ansehen Programmbetrieb und Der große Killer dis~
Erforschung der Grundursache
Analysieren Sie direkt den zweiten Teil des Codes:
13 0 LOAD_GLOBAL 0 (locals) 3 CALL_FUNCTION 0 6 PRINT_ITEM 7 PRINT_NEWLINE 14 8 LOAD_CONST 1 ('test') 11 STORE_FAST 0 (s) 15 14 SETUP_LOOP 36 (to 53) 17 LOAD_CONST 2 ('a') 20 LOAD_CONST 3 ('b') 23 LOAD_CONST 4 ('c') 26 BUILD_LIST 3 29 GET_ITER >> 30 FOR_ITER 19 (to 52) 33 STORE_FAST 1 (i) 16 36 LOAD_CONST 5 (1) 39 LOAD_GLOBAL 0 (locals) 42 CALL_FUNCTION 0 45 LOAD_FAST 1 (i) 48 STORE_SUBSCR 49 JUMP_ABSOLUTE 30 >> 52 POP_BLOCK 17 >> 53 LOAD_GLOBAL 0 (locals) 56 CALL_FUNCTION 0 59 PRINT_ITEM 60 PRINT_NEWLINE 18 61 LOAD_FAST 0 (s) 64 PRINT_ITEM 65 PRINT_NEWLINE 19 66 LOAD_GLOBAL 1 (a) 69 PRINT_ITEM 70 PRINT_NEWLINE 71 LOAD_CONST 0 (None) 74 RETURN_VALUE None
Sie können es im obigen Bytecode sehen:
< Der Bytecode, der 🎜>locals() entspricht, ist: LOAD_GLOBALs='test'. Der Bytecode, der 'test' entspricht, ist: LOAD_CONST und STORE_FASTprint s entsprechend ist: LOAD_FAST Der Bytecode, der print a entspricht, ist: LOAD_GLOBALWie aus den Bytecodes mehrerer oben aufgeführter Schlüsselanweisungen ersichtlich ist, sind direkte Zuweisung/Lesung und Zuweisung durch Einheimische( ) /Die Art des Lesens ist sehr unterschiedlich. Wenn also die Ausnahme „NameError“ ausgelöst wird, beweist dies, dass der über locals()[i] = 1 gespeicherte Wert und der tatsächliche lokale Namespace zwei verschiedene Orte sind? Um diese Frage zu beantworten, müssen wir zunächst eines bestimmen: Wie erhält man den echten lokalen Namespace? Tatsächlich wurde die Standardantwort auf diese Frage im obigen Bytecode gegeben Der Namespace existiert tatsächlich in der entsprechenden Datenstruktur STORE_FAST. Dies erfordert Quellcode, um zu antworten:// ceval.c 从上往下, 依次是相应函数或者变量的定义 // 指令源码 TARGET(STORE_FAST) { v = POP(); SETLOCAL(oparg, v); FAST_DISPATCH(); } -------------------- // SETLOCAL 宏定义 #define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \ GETLOCAL(i) = value; \ Py_XDECREF(tmp); } while (0) -------------------- // GETLOCAL 宏定义 #define GETLOCAL(i) (fastlocals[i]) -------------------- // fastlocals 真面目 PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag){ // 省略其他无关代码 fastlocals = f->f_localsplus; .... }
// bltinmodule.c static PyMethodDef builtin_methods[] = { ... // 找到 locals 函数对应的内置函数是 builtin_locals {"locals", (PyCFunction)builtin_locals, METH_NOARGS, locals_doc}, ... } ----------------------------- // builtin_locals 的定义 static PyObject * builtin_locals(PyObject *self) { PyObject *d; d = PyEval_GetLocals(); Py_XINCREF(d); return d; } ----------------------------- PyObject * PyEval_GetLocals(void) { PyFrameObject *current_frame = PyEval_GetFrame(); // 获取当前堆栈对象 if (current_frame == NULL) return NULL; PyFrame_FastToLocals(current_frame); // 初始化和填充 f_locals return current_frame->f_locals; } ----------------------------- // 初始化和填充 f_locals 的具体实现 void PyFrame_FastToLocals(PyFrameObject *f) { /* Merge fast locals into f->f_locals */ PyObject *locals, *map; PyObject **fast; PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; Py_ssize_t j; int ncells, nfreevars; if (f == NULL) return; locals = f->f_locals; // 如果locals为空, 就新建一个字典对象 if (locals == NULL) { locals = f->f_locals = PyDict_New(); if (locals == NULL) { PyErr_Clear(); /* Can't report it :-( */ return; } } co = f->f_code; map = co->co_varnames; if (!PyTuple_Check(map)) return; PyErr_Fetch(&error_type, &error_value, &error_traceback); fast = f->f_localsplus; j = PyTuple_GET_SIZE(map); if (j > co->co_nlocals) j = co->co_nlocals; // 将 f_localsplus 写入 locals if (co->co_nlocals) map_to_dict(map, j, locals, fast, 0); ncells = PyTuple_GET_SIZE(co->co_cellvars); nfreevars = PyTuple_GET_SIZE(co->co_freevars); if (ncells || nfreevars) { // 将 co_cellvars 写入 locals map_to_dict(co->co_cellvars, ncells, locals, fast + co->co_nlocals, 1); if (co->co_flags & CO_OPTIMIZED) { // 将 co_freevars 写入 locals map_to_dict(co->co_freevars, nfreevars, locals, fast + co->co_nlocals + ncells, 1); } } PyErr_Restore(error_type, error_value, error_traceback); }
Über die obige Quelle Im Code wissen wir bereits klar, dass das, was locals() sieht, tatsächlich der Inhalt des lokalen Namespace der Funktion ist, aber es selbst kann den lokalen Namespace nicht darstellen. Es ist wie ein Proxy, der A sammelt. Die Dinge von B und C sind gezeigt, aber ich kann nicht einfach die Dinge ändern, die A, B und C wirklich besitzen, indem ich diesen Proxy ändere!Aus diesem Grund übergeben wir locals( )[i] = 1, um dynamisch Werte zuzuweisen , print a löst eine NameError-Ausnahme aus, im Gegenteil, globals() ist tatsächlich der eigentliche globale Namespace, daher wird im Allgemeinen gesagt: locals() ist schreibgeschützt, globals() kann
lesen und schreiben
Dieser Artikel ist hier zu Ende. Weitere spannende Inhalte finden Sie in der Spalte Python-Video-Tutorial auf der chinesischen PHP-Website!
Das obige ist der detaillierte Inhalt vonTrap-Analyse der dynamischen Zuweisung von Python. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!