在開發過程當中,咱們沒法把全部代碼、資源都放在同一個文件中。所以,模塊導入在編碼中是很常見的。不管是C++、Java,仍是Python、Go。html
能夠把不一樣功能、不一樣模塊進行分離,當使用的時候,能夠經過import
關鍵字在一個模塊中使用另一個模塊提供的能力,這可以大大提高代碼的開發效率。python
尤爲是對於Python這種對於模塊、工具包依賴較強的編程語言,這一點更爲突出。web
模塊導入,這一點在Python中最爲常見的東西對於不少人來講都不屑一顧,可是,你真的完全理解Python中的import
嗎?編程
我能夠斷言,絕大多數Python開發人員只是使用,殊不知因此然。緩存
本文,就來詳細、完全的介紹一下Python模塊導入的使用。微信
模塊與工具包
模塊(modules)和工具包(packages)這2個概念首當其衝,常常被Python開發者混爲一談。編程語言
雖然,兩者有不少相同之處,可是仍是存在必定差別。所以,要想完全理解import
,首先就須要理解模塊與工具包的異同點。編輯器
模塊ide
Python官網對於模塊的定義以下:函數
模塊具備一個包含任意Python對象的命名空間,一般用做Python代碼組織單位的對象。
實際上,一個模塊一般對應一個.py
文件,模塊的真正功能是能夠將其導入到其餘代碼中,並重複使用,例如:
>>> import math
>>> math.pi
3.141592653589793
第一行代碼,經過import
把math模塊導入到代碼中,經過math.pi來調用pi這一屬性。
這裏須要注意,這裏寫的math.pi
不只是單純的pi
,它還把math
充當全部屬性保持統一的命名空間。命名空間對於保持代碼的可讀性和組織性很是有用。
你能夠利用 dir
來查看命名空間的內容:
>>> import math
>>> dir()
['__annotations__', '__builtins__', ..., 'math']
>>> dir(math)
['__doc__', ..., 'nan', 'pi', 'pow', ...]
除了上述直接導入,咱們還能夠導入模塊下特定的部分:
>>> from math import pi
>>> pi
3.141592653589793
>>> math.pi
NameError: name 'math' is not defined
請注意,這裏對比於前一種方式已經發生了一些轉變。這裏的pi
是放置在全局命名空間內,而不是math
的命名空間內。
包
一樣,首先看一下Python官網對工具包的定義:
一個Python模塊,能夠包含子模塊或遞歸地包含子包。從技術上講,包是具備
__path__
屬性的Python模塊。
從定義上能夠看出,包仍然是模塊。可是,它們仍是有必定的區別。
從編碼上來說,Python包須要在目錄下建立一個名爲__init__.py
的文件。
導入模塊時,一般不會導入子模塊和子包,可是,你能夠經過添加__init__.py
來將須要導入的子模塊和子包囊括進去。
絕對導入與相對導入
from ... import ...
這種導入當時在代碼中常常會遇到,假如,咱們有以下工程:
world/
│
├── africa/
│ ├── __init__.py
│ └── zimbabwe.py
│
├── europe/
│ ├── __init__.py
│ ├── greece.py
│ ├── norway.py
│ └── spain.py
│
└── __init__.py
當想要導入africa時能夠這樣:
from world import africa
也能夠這樣:
from . import africa
那麼這裏面的**點(.)**表明什麼含義?
這裏的點(.)就是一種相對導入,你能夠理解爲從當前包中導入africa。
相反,絕對導入語句中,須要明確命名當前包:
from world import africa
在編碼過程當中,你能夠選擇絕對導入,也能夠選擇相對導入。只不過,PEP 8風格指南中,建議使用絕對導入。
Python導入路徑
這是一個須要重點理解的問題,不少開發者從接觸Python開始就是用PyCharm,它對於導入路徑已經進行了默認的配置,所以,開發者很難遇到沒法導入的問題。
可是,當切換到VS Code、Sublime這些須要較多自行配置的開發工具以後,會發現沒法導入,或者由於導入工具包帶來的調用錯誤問題。
Python是如何找到它要導入的模塊和包的?
你能夠試着輸出sys.path
,你會發現輸出列表中主要包含以下3個部分的位置:
-
當前腳本目錄 -
PYTHON_PATH環境變量 -
其餘與安裝相關的目錄
一般狀況下,Python將對列表進行從頭開發遍歷,從每一個位置中尋找給定的模塊,直到第一個匹配爲止。
因爲腳本所在目錄始終被排在列表的第一位,所以,導入模塊時它會首先從當前目錄下進行尋找。
因此,必定不要把本身的代碼文件名稱與工具包重名。
例如,當前目錄有一個名爲math.py
的文件:
# math.py
def double(number):
return 2 * number
這時候,你導入能夠按照預期工做:
>>> import math
>>> math.double(3.14)
6.28
可是,它已經覆蓋了Python自帶的math標準庫。若是咱們誤認爲導入的是標準math模塊,去調用pi
、sqrt
這些方法,則會報錯:
>>> math.pi
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'math' has no attribute 'pi'
>>> math
<module 'math' from 'math.py'>
所以,爲了不這個問題,必定當心本身開發代碼文件的命名。
建立並安裝本地工具包
在Python開發過程當中,常常會用到pip
安裝來自PyPI倉庫的工具包,它能夠用於全局的工程項目。
除了從倉庫下載安裝工具包,還能夠自行在本地建立工具包,並完成安裝。
建立本地安裝包只須要建立setup.cfg
和setup.py
兩個項目就行:
# setup.cfg
[metadata]
name = local_structure
version = 0.1.0
[options]
packages = structure
# setup.py
import setuptools
setuptools.setup()
這裏的版本名稱和版本號能夠自行選擇。
而後,能夠執行下方命令,把建立的安裝包安裝到本地:
$ python -m pip install -e .
導入樣式
爲了保持代碼的可讀性和可維護性,PEP 8提出了一些針對模塊導入的規則:
-
將導入放在文件的頂部 -
每一個導入要分行 -
將導入分組:首先是標準庫導入,而後是第三方導入,最後是本地應用程序或庫導入 -
在每一個組中按字母順序排序導入 -
絕對導入優先於相對導入 -
避免使用通配符導入 from module import *
# Standard library imports
import sys
from typing import Dict, List
# Third party imports
import feedparser
import html2text
# Reader imports
from reader import URL
資源導入
除了模塊和工具包,代碼開發過程當中,還會常常用到外部資源包,針對外部資源包的導入,可使用importlib.resources
。
它是Python 3.7中的標準模塊,使用它有2點好處:
-
使得導入方式更加一致 -
能夠更輕鬆地訪問其餘包中的資源文件
例如,
>>> from importlib import resources
>>> with resources.open_text("books", "alice_in_wonderland.txt") as fid:
... alice = fid.readlines()
動態導入
Python是一門動態語言,這也是它的主要特色之一。
動態語言使得你能夠在程序運行的時候作不少事情,能夠添加屬性、從新定義方法、更改模塊的文檔字符串。
例如,經過修改print()函數,使它不作任何操做:
>>> print("Hello dynamic world!")
Hello dynamic world!
>>> # Redefine the built-in print()
>>> print = lambda *args, **kwargs: None
>>> print("Hush, everybody!")
>>> # Nothing is printed
在上述示例中,print函數就被匿名函數從新定義了。
除了這種方法,還有更爲易用的動態導入方式,就是利用importlib
。
先來看一段示例,
# docreader.py
import importlib
module_name = input("Name of module? ")
module = importlib.import_module(module_name)
print(module.__doc__)
import_module()
返回能夠綁定到任何變量的模塊對象。而後,您能夠將該變量視爲常規導入的模塊。
$ python docreader.py
Name of module? math
This module is always available. It provides access to the
mathematical functions defined by the C standard.
$ python docreader.py
Name of module? csv
CSV parsing and writing.
在每種狀況下,該模塊都是經過動態導入的import_module()
。
週期性導入
若是兩個或者多個模塊互相導入時,就會發生週期性導入。
例如,有兩個模塊yin.py
和yang.py
:
# yin.py
print(f"Hello from yin")
import yang
print(f"Goodbye from yin")
# yang.py
print(f"Hello from yang")
import yin
print(f"Goodbye from yang")
嘗試在交互式命令行下導入yin
時會發生下面狀況:
>>> import yin
Hello from yin
Hello from yang
Goodbye from yang
Goodbye from yin
有些同窗會疑惑,這樣互相導入,難道不會無限循環下去嗎?
這得益於Python的模塊緩存機制,在導入yin
以後,會首先把它加入到緩存中,後續再導入,會先去參考緩存區域,避免無限循環。
推薦閱讀
福利
最近我花費了半個月的時間,整理了1份理論+實踐的計算機視覺入門教程,這或許是你見過最好的一份CV教程之一。獨家打造、徹底免費,須要的同窗能夠掃碼添加個人我的微信,發送「CV」獲取~
本文分享自微信公衆號 - 七步編程(CodeSteps)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。