你真的會用Python模塊與工具包嗎?

在開發過程當中,咱們沒法把全部代碼、資源都放在同一個文件中。所以,模塊導入在編碼中是很常見的。不管是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模塊,去調用pisqrt這些方法,則會報錯:

>>> 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.cfgsetup.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.pyyang.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以後,會首先把它加入到緩存中,後續再導入,會先去參考緩存區域,避免無限循環。


推薦閱讀

或許,這是最強大的一款Python GUI工具

8個小技巧教你提高Python代碼質量

5款最強且免費的Python IDE

福利

最近我花費了半個月的時間,整理了1份理論+實踐的計算機視覺入門教程,這或許是你見過最好的一份CV教程之一。獨家打造、徹底免費,須要的同窗能夠掃碼添加個人我的微信,發送「CV」獲取~


本文分享自微信公衆號 - 七步編程(CodeSteps)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索