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