Подтвердить что ты не робот

Как узнать, из какого модуля импортируется имя?

В программе Python, если имя существует в пространстве имен программы, можно ли узнать, импортировано ли имя из некоторого модуля, и если да, какой модуль он импортируется из?

4b9b3361

Ответ 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