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

Tim*_*Tim 20 python debugging namespaces module python-3.x

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

Chr*_*ean 21

您可以通过__module__属性查看已定义函数的模块.从Python Data模型文档__module__:

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

例:

>>> from re import compile
>>> compile.__module__
're'
>>> def foo():
...     pass
... 
>>> foo.__module__
'__main__'
>>>
Run Code Online (Sandbox Code Playgroud)

Data模型后来提到类也具有相同的属性:

__module__ 是定义类的模块名称.

>>> from datetime import datetime
>>> datetime.__module__
'datetime'
>>> class Foo:
...     pass
... 
>>> Foo.__module__
'__main__'
>>> 
Run Code Online (Sandbox Code Playgroud)

您也可以使用内置名称(例如int和)执行此操作list.您可以从builtins模块访问它们.

>>> int.__module__
'builtins'
>>> list.__module__
'builtins'
>>> 
Run Code Online (Sandbox Code Playgroud)

我可以使用intlist不使用from builtins import int, list.那么,如何intlist我的程序可用?

那是因为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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,CPython首先在全局范围内搜索名称.如果失败,则它会回退并尝试从当前帧对象中的内置名称映射中获取名称.那是什么f->f_builtins.

您可以使用sys._getframe()以下方法从Python级别观察此映射:

>>> import sys
>>> frame = sys._getframe()
>>> 
>>> frame.f_builtins['int']
<class 'int'>
>>> frame.f_builtins['list']
<class 'list'>
>>> 
Run Code Online (Sandbox Code Playgroud)

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


Jim*_*ard 6

如果由于某种原因源不可用,您可以使用getmodule从中inspect尝试找到该模块,__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'
Run Code Online (Sandbox Code Playgroud)

如果它没有找到任何东西,它会返回,None所以你需要特殊情况.通常,这会为您封装逻辑,因此您不需要自己编写函数来实际获取__module__它是否存在.

这不适用于未附加此信息的对象.作为一个后备,你可以尝试并传递类型来规避它:

o = OrderedDict()
getmodule(o)                # None
getmodule(type(0)).__name__ # 'collections'
Run Code Online (Sandbox Code Playgroud)

但这并不总能产生正确的结果:

from math import pi
getmodule(type(pi)).__name__ 
'builtins'
Run Code Online (Sandbox Code Playgroud)


phd*_*phd 5

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


Ray*_*ger 5

除非代码执行一些不寻常的事情,比如直接更新全局变量,否则源代码应指出每个变量的来源:

x = 10                         # Assigned in the current module
from random import randrange   # Loaded from random
from functools import *        # Loads everything listed in functools.__all__
Run Code Online (Sandbox Code Playgroud)