【发布时间】:2017-12-04 17:18:35
【问题描述】:
在Python程序中,如果名称存在于程序的命名空间中,是否有可能找出该名称是否是从某个模块导入的,如果是,它是从哪个模块导入的?
【问题讨论】:
标签: python python-3.x debugging module namespaces
在Python程序中,如果名称存在于程序的命名空间中,是否有可能找出该名称是否是从某个模块导入的,如果是,它是从哪个模块导入的?
【问题讨论】:
标签: python python-3.x debugging module namespaces
您可以通过__module__ 属性查看函数在哪个模块中定义。 From the Python Data model documentation on __module__:
定义函数的模块的名称,如果不可用,则为无。
例子:
>>> 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__'
>>>
您也可以使用诸如int 和list 之类的内置名称来执行此操作。您可以从builtins 模块访问它们。
>>> int.__module__
'builtins'
>>> list.__module__
'builtins'
>>>
我可以在没有
from builtins import int, list的情况下使用int和list。那么int和list如何在我的程序中可用?
这是因为 int 和 list 是内置名称。您不必为 Python 显式导入它们就可以在当前命名空间中找到它们。您可以在CPython virtual machine source code 中亲自查看。正如@user2357112 提到的,当全局查找失败时会访问内置名称。这是相关的sn-p:
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 映射包含int 和list 类,因此Python 自动为您提供了这些名称。换句话说,它将它们“构建”到范围中;因此术语“内置”。
【讨论】:
int和list等内置类型是从哪个模块导入的?
int.__module__ 和 list.__module__。在builtins 模块中有定义。
from builtins import int,list 的情况下使用int 和list。那么int 和list 如何在我的程序中可用?
int 和list 是Python 中的内置名称。您不必显式导入它们。
__builtin__ 或 builtins (依赖于版本)模块使用内置命名空间作为其模块命名空间,但您可以使用内置名称的原因不是通过任何形式的导入,并且说 builtins 是定义这些类型的地方是有问题的。
如果由于某种原因源不可用,您可以使用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'
【讨论】:
您也可以查看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
【讨论】:
print(globals())而不是globals()?
print 的原因,但如果它便于阅读,我可以删除它。
除非代码做一些不寻常的事情,比如直接更新 globals,否则源代码应该指出每个变量的来源:
x = 10 # Assigned in the current module
from random import randrange # Loaded from random
from functools import * # Loads everything listed in functools.__all__
【讨论】:
from ... import *,,你怎么知道一个变量来自哪个模块?
一些对象(但远非全部)具有属性__module__。
【讨论】: