繁体   English   中英

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

[英]How can I find out which module a name is imported from?

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

您可以通过__module__属性查看函数在哪个模块中定义。 来自__module__上的 Python 数据模型文档

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

例子:

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

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

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

我可以在没有from builtins import int, list list 的情况下使用intlist 那么intlist如何在我的程序中可用?

那是因为intlist是内置名称。 您不必为 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是什么。

您可以使用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 自动为您提供了这些名称。 换句话说,它将它们“构建”到范围中; 因此术语“内置”

如果由于某种原因源不可用,您可以使用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'

一些对象(但远非全部)具有属性__module__

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

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

您还可以查看globals() ,它将输出一个字典,其中包含 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM