如何找出从哪个模块导入名称?

时间:2017-06-30 17:04:06

标签: python python-3.x debugging module namespaces

在Python程序中,如果程序的命名空间中存在名称,是否可以查明该名称是否从某个模块导入,如果是,则从哪个模块导入?

5 个答案:

答案 0 :(得分:21)

您可以通过__module__属性查看已定义函数的模块。 From the Python Data model documentation on __module__

  

定义函数的模块的名称,如果不可用,则为None。

示例:

>>> from re import compile
>>> compile.__module__
're'
>>> def foo():
...     pass
... 
>>> foo.__module__
'__main__'
>>>

The Data model later mentions类具有相同的属性:

  

__module__是定义类的模块名称。

>>> from datetime import datetime
>>> datetime.__module__
'datetime'
>>> class Foo:
...     pass
... 
>>> Foo.__module__
'__main__'
>>> 

您也可以使用intlist等内置名称执行此操作。您可以从builtins模块访问它们。

>>> int.__module__
'builtins'
>>> list.__module__
'builtins'
>>> 
  

我可以使用intlist而不使用from builtins import int, list。那么intlist如何在我的计划中出现?

这是因为intlist是内置名称。您不必为Python显式导入它们,以便能够在当前命名空间中找到它们。您可以在CPython virtual machine source code中自行查看。正如@ 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

您可以使用sys._getframe()

从Python级别观察此映射
>>> import sys
>>> frame = sys._getframe()
>>> 
>>> frame.f_builtins['int']
<class 'int'>
>>> frame.f_builtins['list']
<class 'list'>
>>> 

sys._getframe()返回调用堆栈顶部的帧。在这种情况下,它是模块范围的框架。从上面可以看出,框架的f_builtins映射包含intlist类,因此Python自动为您提供这些名称。换句话说,它将它们“建立”到范围内;因此术语“builtins”

答案 1 :(得分:6)

如果由于某些原因导致源不可用,您可以使用inspect中的getmodule__module__尽可能通过抓取__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'

但不会总是产生正确的结果:

String

答案 2 :(得分:5)

某些对象(但远非所有)具有属性__module__

答案 3 :(得分:5)

除非代码执行某些异常操作,例如直接更新 globals ,否则源代码应指明每个变量的来源:

x = 10                         # Assigned in the current module
from random import randrange   # Loaded from random
from functools import *        # Loads everything listed in functools.__all__

答案 4 :(得分:4)

您还可以查看globals(),它将输出一个包含所有名称的dict,python使用BUT以及您在命名空间内声明的变量,模块和函数。

>>> 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