globals()
немного неверно. Получает вызывающий модуль __dict__
. (Истинное «глобальное» пространство имен Python на самом деле builtins
.)
Как globals()
может получить __dict_
своего вызывающего, когда оно определено в модуле builtins
? Вот подсказка:
PyObject *
PyEval_GetGlobals(void)
{
PyThreadState *tstate = _PyThreadState_GET();
PyFrameObject *current_frame = _PyEval_GetFrame(tstate);
if (current_frame == NULL) {
return NULL;
}
assert(current_frame->f_globals != NULL);
return current_frame->f_globals;
}
globals()
- это одна из тех встроенных функций, которые реализованы в C (в CPython), но вы получите суть. Он читает глобальные значения фреймов из текущего стекового фрейма, поэтому в Python,
import inspect
inspect.currentframe().f_globals
будет делать то же самое, что и globals()
. Но вы не можете просто поместить это в функцию и ожидать, что она будет работать так же, потому что ее вызов добавит кадр стека, и глобальные переменные этого кадра зависят от атрибута .__globals__
функции, который установлен в .__dict__
модуля, который его определил. Вам нужен кадр вызывающего абонента .
def myglobals():
"""Behaves like the builtin globals(), but written in Python!"""
return inspect.currentframe().f_back.f_globals
Вы можете сделать то же самое в test_typematching
. Но ходить по стеку к предыдущему кадру, как это, странная вещь. Это может быть удивительно и хрупко. Это равносильно передаче фрейма вызывающего в качестве неявного скрытого аргумента, что обычно не имеет значения. Подумайте, что произойдет, если вы поместите его в декоратор. Теперь, из какого фрейма стека вы получаете глобальные переменные?
Итак, на самом деле вы должны передавать globals()
в качестве явного аргумента test_typematching()
, например test_typematching(globals())
. Определенный и задокументированный параметр будет намного менее запутанным, чем неявный самоанализ. «Явное лучше, чем неявное».
Тем не менее, стандартная библиотека Python иногда делает подобные вещи, и сам globals()
является ярким примером. И exec()
может использовать текущее пространство имен, если вы не зададите ему другое. Также super()
теперь может работать без аргументов в Python 3. Таким образом, проверка стекового фрейма имеет прецедент для этого вида использования.