Python的import vs from ... import

文章作者:Tyan
博客:noahsnail.com  |  CSDN  |  簡書

0. 測試環境

Python 3.6.9

1. 引言

Python代碼中,既可以導入模塊,也可以導入模塊中的對象,導入方式主要分爲兩種:import <module_name>from <module_name> import <name(s)>。本文主要對二者進行比較。爲了之後測試,定義了一個module模塊,module.py文件定義如下:

a = [1, 2, 3]
b = 'abc'

class Test():
    def __init__(self):
        self.desc = 'Test'

    def print_desc(self):
        print(self.desc)

2. 兩種方式對比

2.1 import <module_name>

Python模塊導入的命令之一是import <module_name>,執行了import <module_name>命令之後,Python的運行過程如下:

  1. 首先在sys.modules中查找module_namesys.modules中包含所有之前導入模塊的緩存。
  2. 如果在模塊緩存中沒找到module_name,Python會繼續查找內置模塊列表,這些是Python預先安裝的模塊,可以在Python標準庫中找到。
  3. 如果還沒找到,Python會在sys.path定義的目錄列表中查找。這個列表中通常包含當前目錄,會首先查找當前目錄。
  4. 如果找到了module_name,會將其綁定到局部命名空間中,後面可以使用。如果沒找到,則會拋出ModuleNotFoundError

注: 導入模塊之後,可以通過模塊的__file__屬性來獲取模塊所在的目錄,其是sys.path中的目錄之一。sys.path[0]爲空,表示當前目錄。示例如下:

>>> import module
>>> module.__file__
'/workspace/heatmap/module.py'
>>> import re
>>> re.__file__
'/usr/lib/python3.6/re.py'
>>> sys.path
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/local/lib/python3.6/dist-packages/warpctc_pytorch-0.1-py3.6-linux-x86_64.egg', '/usr/lib/python3/dist-packages']
>>> import os
>>> os.getcwd()
'/workspace/heatmap'
>>> import mod
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'mod'

import <module_name>導入模塊之後,並不可以直接訪問模塊中的內容,每個模塊都有自己的私有符號表,其是模塊中定義的所有對象的全局符號表,模塊創建了一個單獨的命名空間。執行import <module_name>之後,<module_name>放到了調用者的局部符號表裏,但模塊中定義的對象仍在模塊的私有符號表裏。訪問模塊中定義的對象需要使用.符號。示例如下:

>>> import module
>>> module
<module 'module' from '/workspace/heatmap/module.py'>
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
>>> module.a
[1, 2, 3]
>>> module.b
'abc'

import導入模塊時,有時候模塊的名字非常長,每次訪問模塊的內容都很不方便,因此可以使用import ... as ...的方式爲模塊重命名,便於使用,示例代碼如下:

>>> import module as mod
>>> module
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'module' is not defined
>>> mod
<module 'module' from '/workspace/heatmap/module.py'>
>>> module.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'module' is not defined
>>> mod.a
[1, 2, 3]

2.2 from <module_name> import <name(s)>

from <module_name> import <name(s)>命令可以直接導入模塊中的對象,命令執行之後,模塊中的對象被引用到調用者的環境中,可以直接對其訪問,而不需要添加模塊前綴。這是方式的導入會將模塊中的對象直接添加到調用者的符合表裏,並會覆蓋調用者符號表裏的同名對象。

>>> a = [1, 2]
>>> a
[1, 2]
>>> from module import a
>>> a
[1, 2, 3]
>>> from module import abc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'abc'
>>> from module import b
>>> b
'abc'
>>> module
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'module' is not defined
>>> from module import Test
>>> t = Test()
>>> t.print_desc()
Test

針對導入對象名稱較長問題,也可以使用from ... import ... as ...對導入的對象進行重命名,這種方式也可以用來解決導入對象帶來的名稱衝突。

2.3 from <module_name> import *

from <module_name> import *命令可以無差別導入模塊中的大部分對象(下劃線開頭的部分除外),風險較高且代碼閱讀不直觀,因此這裏不對其進行詳細介紹,也不推薦這種使用方式。

2.4 dir()

Python內置函數dir()會返回命名空間定義的名稱列表,通過dir()函數可以查看導入聲明前後局部符號表的變化情況,也可以用來查看模塊中定義的對象,示例如下:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> a = [1, 2]
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a']
>>> from module import b
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b']
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b', 'module']
>>> from module import Test as t
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b', 'module', 't']
>>> dir(module)
['Test', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b']
>>> dir(module.Test)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'print_desc']

References

  1. https://realpython.com/absolute-vs-relative-python-imports/

  2. https://realpython.com/python-modules-packages/

  3. https://docs.python.org/3/reference/import.html

  4. https://stackoverflow.com/questions/710551/use-import-module-or-from-module-import

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章