[英]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__'
>>>
您也可以使用int
和list
等內置名稱來執行此操作。 您可以從builtins
模塊訪問它們。
>>> int.__module__
'builtins'
>>> list.__module__
'builtins'
>>>
我可以在沒有
from builtins import int, list
list 的情況下使用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
是什么。
您可以使用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 自動為您提供了這些名稱。 換句話說,它將它們“構建”到范圍中; 因此術語“內置” 。
如果由於某種原因源不可用,您可以使用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.