Python手冊!

編程語言 Python 文本編輯器 腳本語言 問世間情是何物 2017-06-17

Python手冊!如果你從Python解釋器中退出然後重新進入,之前定義的名字(函數和變量)都丟失了。因此,如果你想寫長一點的程序,使用文本編輯器來準備解釋器的輸入會更好,使用文件作為替代的輸入。這也被稱作創建腳本。當程序越來越長時,出於易於維護的原因,你可能會將程序分割為幾個文件。你也可能想要在多個程序中使用很好用的一個函數,而不用將其定義拷貝到每一個程序中。

為了支持這些需求,Python提供了將定義放入一個文件的方式,並且在腳本或者解釋器交互式實例中使用它們。這樣的文件稱為模塊;模塊中的定義可以導入到其他模塊或者主模塊中(在頂層執行的腳本和計算模式中可訪問到的變量集合)。

模塊就是一個包含Python定義和語句的文件。文件名是模塊名並且帶有

.py

後綴。在模塊中,模塊的名字(作為字符串),作為全局變量

__name__

的值,是可用的。例如,使用你最喜歡的文本編輯器在當前目錄創建

fibo.py

文件,內容如下:
# Fibonacci numbers moduledef fib(n): # write Fibonacci series up to n

進入Python解釋器,使用下列命令導入這個模塊:

>>> import fibo

這個操作並不會講

fibo

中定義的函數的名字導入到當前符號表中;只是導入模塊的名字

fibo

。使用模塊名字可訪問到函數:
>>> fibo.fib(1000)1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987>>> fibo.fib2(100)

如果想要頻繁使用函數,可以將其賦值給局部名字:

>>> fib = fibo.fib>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.1 More on Modules

模塊可以同時包含可執行語句和函數定義。可執行語句用來初始化模塊。當模塊名字出現在導入語句中時,這些可執行語句只執行一次[1]。(如果文件作為腳本,這些可執行也會執行)

每一個模塊都有自己私有的符號表,這個符號表被所有定義在模塊中的函數作為全局符號表使用。因此,模塊的作者可以使用這些全局變量,而不用擔心和用於全局變量偶然的名字衝突。另一方面,如果你知道自己在做什麼,你可以使用與引用函數相同的方法來引用模塊的全局變量,

modname.itemname

模塊可以引用其他模塊。將所有

import

語句放置到模塊的開始處(或者腳本)是一個很好的習慣,但並不是強制的。被導入的模塊名字將會被放置到當前模塊的全局符號表中。

有一種導入語句的變種方法,將模塊的名字直接導入到當前模塊的符號表中。例如:

>>> from fibo import fib, fib2>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

以上並不會引入模塊的名字(在上面的例子中

fibo

不會被定義)。

甚至有一種方法可以導入模塊中定義的所有名字:

>>> from fibo import *>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

這種方法導入所有不以下劃線

_

開頭的名字。大多數情況下,Python程序員不會使用這種方法,因為這會導入未知的名字集合到解釋器中,也許還會屏蔽已經定義的一些名字。

需要注意,通常在實踐中從模塊或者包中導入所以名字是不鼓勵使用的,因為會降低程序的易讀性。然而,在交互式環境中使用它來減少打字輸入是可行的。

注意: 出於性能原因,一個解釋器會話中每個模塊只導入一次。因此,如果模塊被改變了,必須重啟解釋器;如果只想要交互式測試一個模塊,使用

importlib.reload()

,例如:

import importlib; importlin.reload(modulename)

6.1.1 Executing modules as scripts

當使用以下命令運行Python模塊:

python fibo.py <arguments>

模塊中的代碼會被執行,就像導入該模塊一樣,但是這時

__name__

被設置為

__main__

。這意味著以下代碼會加入到模塊末尾:
if __name__ == "__main__": import sys

模塊即作為腳本執行,也可以作為模塊導入,因為只有當模塊作為

mian

文件執行時候,解析命令行的代碼才會執行:
$ python fibo.py 501 1 2 3 5 8 13 21 34

如果模塊被導入,代碼不會執行:

>>> import fibo>>>

這可以用來為使用者提供一個模塊用戶接口的使用約定,也可以用作測試(模塊作為腳本時執行測試用例)

6.1.2 The Module Search Path

當模塊

spam

被導入時,解釋器首先搜索

built-in

模塊。如果沒有找到,解釋器在變量

sys.path

提供的路徑列表中搜索名為

spam.py

的文件。

sys.path

從下列位置初始化:
  • 包含輸入腳本的目錄(或者沒有指定文件時的當前目錄)

  • PYTHONPATH

    (目錄名字集合,與shell環境變量

    PATH

    相似,也是環境變量)
  • 安裝默認目錄

注意: 在支持符號鏈接的文件系統,包含輸入腳本的目錄是符號鏈接指向的目錄。也就是說包含符號鏈接的目錄不會被加入到搜索路徑中。

初始化後,Python程序可以修改

sys.path

。包含執行腳本的目錄被放置到搜索路徑的開始,在標準庫路徑之前。這意味著該目錄中的腳本會被加載,而標準庫目錄中的同名模塊不會被加載。這會引發錯誤,除非是有意替換標準庫的模塊。閱讀Standard Modules獲取更多信息。 (譯註:自定義的模塊不應與標準模塊重名,否則標準模塊會被覆蓋。)

6.1.3 “Compiled” Python files

為加速模塊加載,Python會在

__pycache__

目錄中緩存每個模塊的編譯版本,緩存文件名為

module.version.pyc

version

編碼了被編譯文件的版本;通常包含了Python的版本號。例如,在

CPython release 3.3

中,文件

spam.py

的編譯版本會被緩存為

__pycache__/spam.cpython-33.pyc

。這種命名約定允許來自不同Python發行版本的模塊得以共存。

Python檢查源文件的修改日期與編譯版本,來確定編譯版本是否過期,是否需要重新編譯。這是一個完全自動化的過程。另外,編譯模塊是平臺獨立的,因此異構系統可以共享相同的庫。

Python不會檢查在兩個環境中的緩存。首先,Python總是重新編譯,並且不會存儲直接從命令行加載的模塊的結果。其次,如果沒有源模塊,Python不檢查緩存。若要支持無源文件(只有編譯版本)分佈,那麼編譯的模塊必須放在源文件目錄中,並且源模塊必需不存在。

給專家的建議:

  • 可以在命令行使用

    -O

    或者

    -OO

    開關來減少編譯模塊的大小。

    -O

    參數移除

    assert

    語句,

    -OO

    參數同時移除

    assert

    語句和

    __doc__

    字符串。由於一些程序依賴這些變量,那麼只有當你確認你要這麼做時,才能使用這兩個參數。“優化的”模塊有一個

    opt-

    標籤並且通常更小。未來的髮型版本可能改變優化的影響。
  • .pyc

    文件中讀取的程序不會比從

    .py

    文件讀取的程序跑得快;

    .pyc

    文件快的地方在於加載。
  • compileall

    模塊可以為目錄中的所有模塊創建

    .pyc

    文件。
  • PEP 3147中有關係這點的更多信息,包括一個決策流程

6.2 Standard Modules

Python提供了標準模塊庫,在獨立文檔中描述,名為

Python Library Reference

(以後叫做

Library Reference

)。有一些模塊內嵌入解釋器中,這些模塊不是語言核心的一部分,但是它們是內嵌的,這既是為性能考慮,也提供了訪問如系統調用般的操作系統原生接口的方式。這些模塊集合依賴底層平臺的配置選項。例如

winreg

模塊只在Windows系統中提供。一個特殊的模塊值得注意:

sys

,這個模塊內嵌在所有Python解釋器中。變量

sys.ps1

sys.ps2

定義了主提示符和輔助提示符的字符串:
>>> import sys>>> sys.ps1'>>> '>>> sys.ps2'... '>>> sys.ps1 = 'C> 'C> print('Yuck!')

只有當解釋器以交互模式運行才會定義這兩個變量。

變量

sys.path

是決定解釋器模塊搜索路徑的字符列表。該變量從環境變量

PYTHONPATH

,或者內置默認路徑(

PYTHONPATH

未指定時)初始化。可以使用標準

list

操作修改它的值:
>>> import sys>>> sys.path.append('/ufs/guido/lib/python')

6.3 The dir() Function

內嵌函數

dir()

用於搜索模塊定義的名字。返回一個有序字符串列表:
>>> import fibo, sys>>> dir(fibo)

不帶參數使用

dir()

函數,會列出當前作用域的全部名字:
>>> a = [1, 2, 3, 4, 5]>>> import fibo>>> fib = fibo.fib>>> dir()

需要注意該函數列出所有類型的名字:變量,模塊,函數,等等。

dir()

不會列出內嵌函數和變量的名字。如果希望列出,這些名字定義在標準模塊

builtins

中:
>>> import builtins>>> dir(builtins)

6.4 Packages

包是使用“圓點模塊名”結構化Python模塊名字空間的方式。例如,模塊名

A.B

表示包

A

中的子模塊

B

。就像模塊使得不同模塊的作者免於擔憂每個模塊的全局名字一樣,圓點模塊名的使用使得多模塊包(如

NumPy

或者Python圖像庫)的作者免於擔憂每個模塊的名字。

假設需要為統一音頻文件和音頻數據的處理設計一個模塊的集合(包)。有許多不同的音頻文件格式(通常通過擴展名辨認,如

.wav, .aiff, .au

),因此需要為不同文件格式的轉換創建和維護一個持續增長的模塊集合。也存在許多對音頻數據不同的操作(例如混合,增加回聲,增加均衡器函數,創建人造立體效果),因此需要額外編寫執行這些操作的大量模塊。以下是包的可能結構(以層級文件結構來表示):
sound/ Top-level package

導入包時,Python搜索

sys.path

提供的路徑尋找包子目錄。

為使Python將普通目錄看做包,目錄中必須包含

__init__.py

文件。這樣做是為了避免普通的目錄名(如

string

)將以後會出現在模塊搜索路徑中的有效模塊無意識的隱藏掉。最簡單的情況是,

__init__.py

可以是一個空文件,但是它也可以包含可執行的初始化包的代碼或者設置

__all__

變量,後面講述。

包的用戶可以從包中導入獨立的模塊:

import sound.effects.echo

這將加在子模塊

sound.effects.echo

。必須使用全名引用:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

導入子模塊可選方式:

from sound.effects import echo

以上也加載子模塊

echo

,不使用包前綴引用模塊,因此可以像下面一樣使用:
echo.echofilter(input, output, delay=0.7, atten=4)

另外的方式是直接導入需要的函數或者變量:

from sound.effects.echo import echofilter

同樣的,這將加在子模塊echo,但使函數

echofilter()

直接可用:
echofilter(input, output, delay=0.7, atten=4)

注意當使用

from package import item

時,

item

可以使子模塊(子包),也可以是包內定義的其他名字,如函數,類或者變量。

import

語句首先測試要導入的項是否在包中存在;如果不存在,Python假設這個項是模塊並嘗試加載它。如果最後尋找失敗,

ImportError

異常拋出。

相對的,使用

import item.subitem.subsubitem

時,除了最後一項,每一項都必須是包;最後一項可以是模塊或者包但是不能是前面的項中定義的類,函數或者變量。

6.4.1 Importing * From a Package

使用

from sound.effects import *

會發生什麼?理想情況下,總是期望在文件系統中找出所有子模塊,並全部導入。全部導入會耗費很長時間,並且導入子模塊可能會有不期待的副作用,這些副作用應該在顯式導入時發生。

唯一的解決方案是包作者提供一個包的顯式索引。

import

語句遵循以下約定:如果包的

__init__.py

代碼中定義了名為

__all__

的變量,那麼使用

from package import *

時會導入該變量指定的所有模塊。當包的新版本發佈時,由包作者負責更新列表

__all__

。如果包作者不希望可以使用

from package import *

導入包中的模塊,也可以不支持

__all__

。 例如,文件

sound/effects/__init__.py

可能包含以下代碼:
__all__ = ["echo", "surround", "reverse"]

這意味著

from sound.effects import *

會導入

sound

包中指定的三個模塊。

如果

__all__

沒有定義,語句

from sound.effects import *

不會將包

sound.effects

中的子模塊全部倒入到當前名字空間中,只保證包

sound.effects

被導入了(可能會運行

__init__.py

中的初始化代碼)並且導入任意在包中定義的名字。包括在

__init__.py

中定義的任意名字(以及顯式加載的子模塊)。也會包括前面的

import

語句顯式加載的任意包子模塊。考慮如下代碼:
import sound.effects.echoimport sound.effects.surroundfrom sound.effects import *

這個例子中,

echo

surround

模塊被導入到當前名字空間中,因為執行

from... import

時,它們已經定義在

sound.effects

包中定義了。(定義了

__all__

時同樣有效)

儘管使用

import *

時只有符合特定模式的名字會被導出,但仍然不建議在生產代碼中使用。

記住,使用

form Package import specific_submodle

沒有錯誤。實際上,這是推薦的方法,除非當前模塊需要使用其他包中的同名模塊。

6.4.2 Intra-package References

當包中包含了子包結構(就如例子中的

sound

包),可以使用絕對導入的方式引用兄弟包中的子模塊。例如,如果模塊

sound.filters.vocoder

需要使用包

sound.effects

中的

echo

模塊,可以使用

from sound.effects import echo

也可以使用

from modul import name

語句來相對導入模塊。這種方式使用點

.

指示當前包和相對導入中涉及的父包。以

surround

模塊為例:
from . import echofrom .. import formatsfrom ..filters import equalizer

相對導入基於當前模塊的名字。由於主模塊的名字總是

__main__

,要當做Python應用主模塊使用的模塊必須總是使用絕對導入的方式。

6.4.3 Packages in Multiple Directories

包還支持一個特殊屬性,

__path__

。在

__init__.py

中的代碼執行之前,屬性

__path__

被初始化為一個列表,這個列表包含了持有該

__init__.py

文件的目錄的路徑。該變量可修改,這樣做會影響將來的對包內模塊和子包的搜索。

然而這個特性不總是需要的,它可以用來擴展包的模塊集合

Footnotes

[1] 實際上,函數定義也是可“執行”的“語句”;模塊級別的函數執行將函數的名字放置到模塊的全局符號表中

相關推薦

推薦中...