В программе Python, если имя существует в пространстве имен программы, можно ли узнать, импортировано ли имя из некоторого модуля, и если да, какой модуль он импортируется из?
Как узнать, из какого модуля импортируется имя?
Ответ 1
Вы можете видеть, какой модуль определен функцией через атрибут __module__
. Из документации модели данных Python на __module__
:
Название модуля, в котором была определена функция, или Нет, если недоступно.
Пример:
>>> from re import compile
>>> compile.__module__
're'
>>> def foo():
... pass
...
>>> foo.__module__
'__main__'
>>>
В модели данных позже упоминается, что классы имеют одинаковый атрибут:
__module__
- это имя модуля, в котором был определен класс.
>>> from datetime import datetime
>>> datetime.__module__
'datetime'
>>> class Foo:
... pass
...
>>> Foo.__module__
'__main__'
>>>
Вы также можете сделать это со встроенными именами, такими как int
и list
. Вы можете получить доступ к ним из модуля builtins
.
>>> int.__module__
'builtins'
>>> list.__module__
'builtins'
>>>
Я могу использовать
int
иlist
безfrom builtins import int, list
. Итак, какint
иlist
становятся доступными в моей программе?
Это потому, что int
и list
являются встроенными именами. Вам не нужно явно импортировать их для Python, чтобы иметь возможность находить их в текущем пространстве имен. Вы можете убедиться в этом в исходный код виртуальной машины CPython. Как упоминалось в @user2357112, встроенные имена доступны, когда глобальный поиск завершается с ошибкой. Вот соответствующий фрагмент:
if (v == NULL) {
v = PyDict_GetItem(f->f_globals, name);
Py_XINCREF(v);
if (v == NULL) {
if (PyDict_CheckExact(f->f_builtins)) {
v = PyDict_GetItem(f->f_builtins, name);
if (v == NULL) {
format_exc_check_arg(
PyExc_NameError,
NAME_ERROR_MSG, name);
goto error;
}
Py_INCREF(v);
}
else {
v = PyObject_GetItem(f->f_builtins, name);
if (v == NULL) {
if (PyErr_ExceptionMatches(PyExc_KeyError))
format_exc_check_arg(
PyExc_NameError,
NAME_ERROR_MSG, name);
goto error;
}
}
}
}
В приведенном выше коде CPython сначала ищет имя в глобальной области. Если это не удается, оно возвращается и пытается получить имя от сопоставления встроенных имен в текущем объекте фрейма. Это то, что f->f_builtins
.
Вы можете наблюдать это сопоставление с уровня Python, используя sys._getframe()
:
>>> import sys
>>> frame = sys._getframe()
>>>
>>> frame.f_builtins['int']
<class 'int'>
>>> frame.f_builtins['list']
<class 'list'>
>>>
sys._getframe()
возвращает фрейм в верхней части стека вызовов. В этом случае это рамка для области модуля. Как вы можете видеть выше, отображение f_builtins
для фрейма содержит классы int
и list
, поэтому Python автоматически предоставил эти имена. Другими словами, он "встроил" их в сферу охвата; следовательно, термин "встроенные".
Ответ 2
Если по какой-либо причине источник недоступен, вы можете использовать getmodule
из inspect
, который пытается найти этот модуль захватив __module__
, если он существует, а затем возвращается к другим альтернативам.
Если все идет o.k, вы возвращаете объект модуля. Из этого вы можете получить __name__
, чтобы получить фактическое имя модуля:
from inspect import getmodule
from collections import OrderedDict
from math import sin
getmodule(getmodule).__name__
'inspect'
getmodule(OrderedDict).__name__
'collections'
getmodule(sin).__name__
'math'
Если он ничего не найдет, он возвращает None
, поэтому вам придется в этом случае использовать специальный случай. В общем, это инкапсулирует логику для вас, поэтому вам не нужно самостоятельно писать функцию, чтобы фактически захватить __module__
, если она существует.
Это не работает для объектов, которые не имеют этой информации. Вы можете, как спад, попробовать и пройти в типе, чтобы обойти его:
o = OrderedDict()
getmodule(o) # None
getmodule(type(0)).__name__ # 'collections'
но это не всегда даст правильный результат:
from math import pi
getmodule(type(pi)).__name__
'builtins'
Ответ 3
Некоторые объекты (но далеко не все) имеют атрибут __ module__.
Ответ 4
Если код не делает что-то необычное, как прямое обновление глобалов, исходный код должен указывать, откуда появилась каждая переменная:
x = 10 # Assigned in the current module
from random import randrange # Loaded from random
from functools import * # Loads everything listed in functools.__all__
Ответ 5
Вы также можете посмотреть globals()
, он выведет dict со всеми именами, которые использует python, но также переменными, модулями и функции, которые вы объявили внутри пространства имен.
>>> x = 10
>>> import os
>>> globals() # to big to display here but finish with
# {... 'x': 10, 'os': <module 'os' from '/usr/local/lib/python2.7/os.py'>}
Поэтому вы можете проверить, были ли переменные объявлены следующим образом:
if globals()['x']:
print('x exist')
try:
print(globals()['y'])
except KeyError:
print('but y does not')
# x exist
# but y does not
Работает также с модулями:
print(globals()['os']) # <module 'os' from '/usr/local/lib/python2.7/os.py'>
try:
print(globals()['math'])
except KeyError:
print('but math is not imported')
# <module 'os' from '/usr/local/lib/python2.7/os.py'>
# but math is not imported