Python3語法詳解

一.下載安裝

1.1Python下載

Python官網:https://www.python.org/html

1.2Python安裝

1.2.1 Linux 平臺安裝

如下爲在Unix & Linux 平臺上安裝 Python 的簡單步驟:node

  • 打開WEB瀏覽器訪問https://www.python.org/downloads/source/
  • 選擇適用於Unix/Linux的源碼壓縮包。
  • 下載及解壓壓縮包。
  • 若是你須要自定義一些選項修改Modules/Setup
  • 執行 ./configure 腳本
  • make
  • make install

執行以上操做後,Python會安裝在 /usr/local/bin 目錄中,Python庫安裝在/usr/local/lib/pythonXX,XX爲你使用的Python的版本號。python

1.2.2 Window 平臺安裝 

如下爲在 Window 平臺上安裝 Python 的簡單步驟:linux

  • 打開 WEB 瀏覽器訪問https://www.python.org/downloads/windows/
  • 在下載列表中選擇Window平臺安裝包,包格式爲:python-XYZ.msi 文件 , XYZ 爲你要安裝的版本號。
  • 要使用安裝程序 python-XYZ.msi, Windows系統必須支持Microsoft Installer 2.0搭配使用。只要保存安裝文件到本地計算機,而後運行它,看看你的機器支持MSI。Windows XP和更高版本已經有MSI,不少老機器也能夠安裝MSI。
  • 下載後,雙擊下載包,進入Python安裝嚮導,安裝很是簡單,你只須要使用默認的設置一直點擊"下一步"直到安裝完成便可。

1.2.3 環境變量配置

在 Unix/Linux 設置環境變量git

  • 在 csh shell: 輸入 
    setenv PATH "$PATH:/usr/local/bin/python"
    , 按下"Enter"。
  • 在 bash shell (Linux): 輸入 
    export PATH="$PATH:/usr/local/bin/python"
    ,按下"Enter"。
  • 在 sh 或者 ksh shell: 輸入 
    PATH="$PATH:/usr/local/bin/python"
    , 按下"Enter"。

注意: /usr/local/bin/python 是 Python 的安裝目錄。程序員

在 Windows 設置環境變量正則表達式

在環境變量中添加Python目錄:算法

在命令提示框中(cmd) : 輸入 sql

path=%path%;C:\Python 

按下"Enter"。shell

注意: C:\Python 是Python的安裝目錄。

也能夠經過如下方式設置:

  • 右鍵點擊"計算機",而後點擊"屬性"
  • 而後點擊"高級系統設置"
  • 選擇"系統變量"窗口下面的"Path",雙擊便可!
  • 而後在"Path"行,添加python安裝路徑便可(個人D:\Python32),因此在後面,添加該路徑便可。 ps:記住,路徑直接用分號";"隔開!
  • 最後設置成功之後,在cmd命令行,輸入命令"python",就能夠有相關顯示。


Python 環境變量

下面幾個重要的環境變量,它應用於Python:

變量名 描述
PYTHONPATH PYTHONPATH是Python搜索路徑,默認咱們import的模塊都會從PYTHONPATH裏面尋找。
PYTHONSTARTUP Python啓動後,先尋找PYTHONSTARTUP環境變量,而後執行此變量指定的文件中的代碼。
PYTHONCASEOK 加入PYTHONCASEOK的環境變量, 就會使python導入模塊的時候不區分大小寫.
PYTHONHOME 另外一種模塊搜索路徑。它一般內嵌於的PYTHONSTARTUP或PYTHONPATH目錄中,使得兩個模塊庫更容易切換。

1.2.4 運行Python

有三種方式能夠運行Python:

一、交互式解釋器:

你能夠經過命令行窗口進入python並開在交互式解釋器中開始編寫Python代碼。

你能夠在Unix,DOS或任何其餘提供了命令行或者shell的系統進行python編碼工做。

$ python # Unix/Linux 

或者 

C:>python # Windows/DOS

如下爲Python命令行參數:

選項 描述
-d 在解析時顯示調試信息
-O 生成優化代碼 ( .pyo 文件 )
-S 啓動時不引入查找Python路徑的位置
-V 輸出Python版本號
-X 從 1.6版本以後基於內建的異常(僅僅用於字符串)已過期。
-c cmd 執行 Python 腳本,並將運行結果做爲 cmd 字符串。
file 在給定的python文件執行python腳本。

二、命令行腳本

在你的應用程序中經過引入解釋器能夠在命令行中執行Python腳本,以下所示:

$ python script.py # Unix/Linux 
或者 
C:>python script.py # Windows/DOS

注意:在執行腳本時,請檢查腳本是否有可執行權限。

三、集成開發環境(IDE:Integrated Development Environment): PyCharm

PyCharm 是由 JetBrains 打造的一款 Python IDE,支持 macOS、 Windows、 Linux 系統。

PyCharm 功能 : 調試、語法高亮、Project管理、代碼跳轉、智能提示、自動完成、單元測試、版本控制……

PyCharm 下載地址 : https://www.jetbrains.com/pycharm/download/

PyCharm 安裝地址:http://www.runoob.com/w3cnote/pycharm-windows-install.html

繼續下一章以前,請確保您的環境已搭建成功。若是你不可以創建正確的環境,那麼你就能夠從您的系統管理員的幫助。

二.使用Python解釋器

2.1調用解釋器

Python解釋器一般安裝/usr/local/bin/python3.6 在那些可用的機器上; 放入/usr/local/binUnix shell的搜索路徑能夠經過輸入命令來啓動它:

python3.6

到了shell。[1]因爲選擇解釋器所在的目錄是一個安裝選項,其餘地方也是可能的; 請諮詢您當地的Python大師或系統管理員。(例如,/usr/local/python是一個受歡迎的替代位置。)

在Windows機器上,一般會放置Python安裝 C:\Python36,可是在運行安裝程序時能夠更改此設置。要將此目錄添加到路徑,能夠在DOS框中的命令提示符中鍵入如下命令:

set path=%path%;C:\python36 

在主提示符下鍵入文件結束字符(Control-D在Unix上,Control-Z在Windows上)會致使解釋器以零退出狀態退出。若是這不起做用,您能夠經過鍵入如下命令退出解釋器:quit()

解釋器的行編輯功能包括支持readline的系統上的交互式編輯,歷史替換和代碼完成。也許最快的檢查是否支持命令行編輯是輸入 Control-P你獲得的第一個Python提示。若是發出嗶嗶聲,則能夠進行命令行編輯; 有關鍵的介紹,請參閱附錄交互式輸入編輯和歷史替換若是沒有發生任何事情,或者是否^P回顯,則命令行編輯不可用; 你只能使用退格鍵從當前行中刪除字符。

解釋器的操做有點像Unix shell:當使用鏈接到tty設備的標準輸入調用時,它以交互方式讀取和執行命令; 當使用文件名參數或文件做爲標準輸入調用時,它會從該文件中讀取並執行腳本

啓動解釋器的第二種方法是執行命令中的語句,相似於shell的 選項。因爲Python語句一般包含空格或shell特有的其餘字符,所以一般建議使用單引號引用 命令python -c command [arg] ...-c

一些Python模塊也可用做腳本。能夠使用這些來調用它們 ,它執行模塊的源文件,就像在命令行中拼寫出它的全名同樣。python -m module [arg] ...

使用腳本文件時,有時能夠運行腳本並在以後進入交互模式。這能夠經過-i 在腳本以前傳遞來完成

命令行和環境中描述了全部命令行選項

2.1.1 參數傳遞

當解釋器知道時,腳本名稱和其後的附加參數將變爲字符串列表並分配給模塊中argv 變量sys您能夠經過執行來訪問此列表清單的長度至少爲一; 當沒有給出腳本和參數時,是一個空字符串。當腳本名稱爲 (表示標準輸入)時,設置爲使用 命令時設置爲使用 模塊時 將其設置爲所定位模塊的全名。命令模塊以後找到 的選項不會被Python解釋器的選項處理使用,而是留在import syssys.argv[0]'-'sys.argv[0]'-'-c sys.argv[0]'-c'-m sys.argv[0]-c -m sys.argv 用於處理的命令或模塊。

2.1.2 交互模式

當從tty讀取命令時,解釋器被稱爲處於交互模式在這種模式下,它會提示下一個帶有主要提示的命令,一般是三個大於號(>>>); 對於連續行,它會提示輔助提示,默認爲三個點(...)。在打印第一個提示以前,解釋程序會打印一條歡迎消息,說明其版本號和版權聲明:

$ python3.6
Python 3.6 (default, Sep 16 2015, 09:25:04) [GCC 4.8.2] on linux Type "help", "copyright", "credits" or "license" for more information. >>> 

進入多線構造時須要延續線。舉個例子,看看這個if聲明:

>>>
>>> the_world_is_flat = True >>> if the_world_is_flat: ... print("Be careful not to fall off!") ... Be careful not to fall off! 

有關交互模式的更多信息,請參閱交互模式

2.2 解釋器及其環境

2.2.1 源代碼編碼

默認狀況下,Python源文件被視爲以UTF-8編碼。在該編碼中,世界上大多數語言的字符能夠在字符串文字,標識符和註釋中同時使用 - 儘管標準庫僅使用ASCII字符做爲標識符,這是任何可移植代碼都應遵循的約定。要正確顯示全部這些字符,編輯器必須識別文件是UTF-8,而且必須使用支持文件中全部字符的字體。

要聲明非默認編碼,應添加一個特殊註釋行做爲文件第一行。語法以下:

# -*- coding: encoding -*-

其中encodingcodecsPython支持的有效編碼之一

例如,要聲明要使用Windows-1252編碼,源代碼文件的第一行應爲:

# -*- coding: cp1252 -*-

第一行規則的一個例外是源代碼以UNIX「shebang」行開頭 在這種狀況下,應將編碼聲明添加爲文件的第二行。例如:

#!/usr/bin/env python3
# -*- coding: cp1252 -*-

三. Python的非正式介紹

在如下示例中,輸入和輸出經過是否存在提示來區分(>>>...):要重複示例,必須在提示符後出現提示時出現提示; 從解釋器輸出不以提示開頭的行。請注意,示例中一行上的輔助提示意味着您必須鍵入一個空行; 這用於結束多行命令。

本手冊中的許多示例,即便是在交互式提示符下輸入的示例,都包含註釋。Python中的註釋以哈希字符開頭 #,並延伸到物理行的末尾。註釋可能出如今行的開頭或跟隨空格或代碼,但不在字符串文字中。字符串文字中的哈希字符只是一個哈希字符。因爲註釋是爲了澄清代碼而不是由Python解釋,所以在鍵入示例時可能會省略它們。

一些例子:

# this is the first comment
spam = 1 # and this is the second comment # ... and now a third! text = "# This is not a comment because it's inside quotes." 

3.1 使用Python做爲計算器

讓咱們嘗試一些簡單的Python命令。啓動解釋器並等待主要提示符>>>(不該該花很長時間。)

3.1.1 數字

解釋器充當一個簡單的計算器:您能夠在其上鍵入表達式,它將寫入值。表達式語法是直接的:運營商+-*/工做就像在大多數其餘語言(例如,C或Pascal); 括號(())可用於分組。例如:

>>>
>>> 2 + 2 4 >>> 50 - 5*6 20 >>> (50 - 5*6) / 4 5.0 >>> 8 / 5 # division always returns a floating point number 1.6 

的整數(例如2420)具備類型int,具備小數部分(例如,那些5.01.6)具備類型 float咱們將在本教程後面看到有關數值類型的更多信息。

Division(/)老是返回一個浮點數。要進行分區並得到整數結果(丟棄任何小數結果),您能夠使用// 運算符; 計算你能夠使用的餘數%

>>>
>>> 17 / 3 # classic division returns a float 5.666666666666667 >>> >>> 17 // 3 # floor division discards the fractional part 5 >>> 17 % 3 # the % operator returns the remainder of the division 2 >>> 5 * 3 + 2 # result * divisor + remainder 17 

使用Python,能夠使用**運算符來計算冪[1]

>>>
>>> 5 ** 2 # 5 squared 25 >>> 2 ** 7 # 2 to the power of 7 128 

等號(=)用於爲變量賦值。以後,在下一個交互式提示以前不會顯示任何結果:

>>>
>>> width = 20 >>> height = 5 * 9 >>> width * height 900 

若是變量沒有「定義」(賦值),嘗試使用它會給你一個錯誤:

>>>
>>> n # try to access an undefined variable Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'n' is not defined 

浮點數徹底支持; 具備混合類型操做數的運算符將整數操做數轉換爲浮點數:

>>>
>>> 4 * 3.75 - 1 14.0 

在交互模式下,最後打印的表達式將分配給變量 _這意味着當您使用Python做爲桌面計算器時,繼續計算會更容易一些,例如:

>>>
>>> tax = 12.5 / 100 >>> price = 100.50 >>> price * tax 12.5625 >>> price + _ 113.0625 >>> round(_, 2) 113.06 

該變量應被用戶視爲只讀。不要爲其顯式賦值 - 您將建立一個具備相同名稱的獨立局部變量,以使用其魔術行爲屏蔽內置變量。

除了int和以外float,Python還支持其餘類型的數字,例如DecimalFractionPython還內置了對複數的支持,並使用jJ後綴來表示虛部(例如3+5j)。

3.1.2 字符串

除了數字以外,Python還能夠操做字符串,這能夠經過多種方式表達。它們能夠用單引號('...')或雙引號("..."括起來,結果相同[2]。 \可用於轉義引號:

>>>
>>> 'spam eggs' # single quotes 'spam eggs' >>> 'doesn\'t' # use \' to escape the single quote... "doesn't" >>> "doesn't" # ...or use double quotes instead "doesn't" >>> '"Yes," they said.' '"Yes," they said.' >>> "\"Yes,\" they said." '"Yes," they said.' >>> '"Isn\'t," they said.' '"Isn\'t," they said.' 

在交互式解釋器中,輸出字符串用引號括起來,特殊字符用反斜槓轉義。雖然這可能有時看起來與輸入不一樣(封閉的引號可能會改變),但這兩個字符串是等價的。若是字符串包含單引號而沒有雙引號,則該字符串用雙引號括起來,不然用單引號括起來。print()函數經過省略括號引號並打印轉義字符和特殊字符,產生更可讀的輸出:

>>>
>>> '"Isn\'t," they said.' '"Isn\'t," they said.' >>> print('"Isn\'t," they said.') "Isn't," they said. >>> s = 'First line.\nSecond line.' # \n means newline >>> s # without print(), \n is included in the output 'First line.\nSecond line.' >>> print(s) # with print(), \n produces a new line First line. Second line. 

若是您不但願將前面提到的字符\解釋爲特殊字符,則能夠經過在第一個引號以前添加原始字符串來使用原始字符串r

>>>
>>> print('C:\some\name') # here \n means newline! C:\some ame >>> print(r'C:\some\name') # note the r before the quote C:\some\name 

字符串文字能夠跨越多行。一種方法是使用三引號: """..."""'''...'''行尾自動包含在字符串中,但能夠經過\在行尾添加a來防止這種狀況如下示例:

print("""\ Usage: thingy [OPTIONS]  -h Display this usage message  -H hostname Hostname to connect to """) 

產生如下輸出(請注意,不包括初始換行符):

Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to

字符串能夠與+操做符鏈接(粘合在一塊兒),並重復*

>>>
>>> # 3 times 'un', followed by 'ium' >>> 3 * 'un' + 'ium' 'unununium' 

兩個或多個彼此相鄰的字符串文字(即引號之間的字符串)會自動鏈接。

>>>
>>> 'Py' 'thon' 'Python' 

當您想要斷開長字符串時,此功能特別有用:

>>>
>>> text = ('Put several strings within parentheses ' ... 'to have them joined together.') >>> text 'Put several strings within parentheses to have them joined together.' 

這僅適用於兩個文字,而不是變量或表達式:

>>>
>>> prefix = 'Py' >>> prefix 'thon' # can't concatenate a variable and a string literal  ... SyntaxError: invalid syntax >>> ('un' * 3) 'ium'  ... SyntaxError: invalid syntax 

若是要鏈接變量或變量和文字,請使用+

>>>
>>> prefix + 'thon' 'Python' 

字符串能夠被索引(下標),第一個字符具備索引0.沒有單獨的字符類型; 一個字符只是一個大小爲1的字符串:

>>>
>>> word = 'Python' >>> word[0] # character in position 0 'P' >>> word[5] # character in position 5 'n' 

指數也多是負數,從右邊開始計算:

>>>
>>> word[-1] # last character 'n' >>> word[-2] # second-last character 'o' >>> word[-6] 'P' 

請注意,因爲-0與0相同,所以負索引從-1開始。

除索引外,還支持切片雖然索引用於獲取單個字符,但切片容許您獲取子字符串:

>>>
>>> word[0:2] # characters from position 0 (included) to 2 (excluded) 'Py' >>> word[2:5] # characters from position 2 (included) to 5 (excluded) 'tho' 

請注意如何始終包含開始,並始終排除結束。這能夠確保始終等於s[:i] s[i:]s

>>>
>>> word[:2] + word[2:] 'Python' >>> word[:4] + word[4:] 'Python' 

切片索引具備有用的默認值; 省略的第一個索引默認爲零,省略的第二個索引默認爲要切片的字符串的大小。

>>>
>>> word[:2] # character from the beginning to position 2 (excluded) 'Py' >>> word[4:] # characters from position 4 (included) to the end 'on' >>> word[-2:] # characters from the second-last (included) to the end 'on' 

記住切片如何工做的一種方法是將索引視爲指向 字符之間,第一個字符的左邊緣編號爲0.而後,n個字符串的最後一個字符的右邊緣具備索引n,例如:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n | +---+---+---+---+---+---+ 0 1 2 3 4 5 6 -6 -5 -4 -3 -2 -1 

第一行數字給出了字符串中索引0 ... 6的位置; 第二行給出相應的負指數。i到 j的切片分別由標記爲ij的邊之間的全部字符組成

對於非負索引,切片的長度是索引的差別,若是二者都在邊界內。例如,長度word[1:3]爲2。

嘗試使用過大的索引將致使錯誤:

>>>
>>> word[42] # the word only has 6 characters Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: string index out of range 

可是,在用於切片時,優雅地處理超出範圍的切片索引:

>>>
>>> word[4:42] 'on' >>> word[42:] '' 

Python字符串沒法更改 - 它們是不可變的所以,分配給字符串中的索引位置會致使錯誤:

>>>
>>> word[0] = 'J'  ... TypeError: 'str' object does not support item assignment >>> word[2:] = 'py'  ... TypeError: 'str' object does not support item assignment 

若是您須要不一樣的字符串,則應建立一個新字符串:

>>>
>>> 'J' + word[1:] 'Jython' >>> word[:2] + 'py' 'Pypy' 

內置函數len()返回字符串的長度:

>>>
>>> s = 'supercalifragilisticexpialidocious' >>> len(s) 34 

也能夠看看

文本序列類型 - str
字符串是 序列類型的 示例,並支持此類型支持的常見操做。
字符串方法
字符串支持大量基本轉換和搜索方法。
格式化的字符串文字
具備嵌入式表達式的字符串文字。
格式字符串語法
有關字符串格式的信息 str.format()
printf-style字符串格式
當字符串是運算 % 的左操做數時調用的舊格式化操做在此處更詳細地描述。

3.1.3 列表

Python知道許多複合數據類型,用於將其餘值組合在一塊兒。最通用的是列表,它能夠寫成方括號之間的逗號分隔值(項)列表。列表可能包含不一樣類型的項目,但一般項目都具備相同的類型。

>>>
>>> squares = [1, 4, 9, 16, 25] >>> squares [1, 4, 9, 16, 25] 

像字符串(以及全部其餘內置序列類型)同樣,列表能夠被索引和切片:

>>>
>>> squares[0] # indexing returns the item 1 >>> squares[-1] 25 >>> squares[-3:] # slicing returns a new list [9, 16, 25] 

全部切片操做都返回包含所請求元素的新列表。這意味着如下切片返回列表的新(淺)副本:

>>>
>>> squares[:] [1, 4, 9, 16, 25] 

列表還支持串聯等操做:

>>>
>>> squares + [36, 49, 64, 81, 100] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 

不可變的字符串不一樣,列表是可變 類型,便可以更改其內容:

>>>
>>> cubes = [1, 8, 27, 65, 125] # something's wrong here >>> 4 ** 3 # the cube of 4 is 64, not 65! 64 >>> cubes[3] = 64 # replace the wrong value >>> cubes [1, 8, 27, 64, 125] 

您還能夠使用該append() 方法在列表末尾添加新項目(咱們稍後會看到有關方法的更多信息):

>>>
>>> cubes.append(216) # add the cube of 6 >>> cubes.append(7 ** 3) # and the cube of 7 >>> cubes [1, 8, 27, 64, 125, 216, 343] 

也能夠分配給切片,這甚至能夠改變列表的大小或徹底清除它:

>>>
>>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] >>> letters ['a', 'b', 'c', 'd', 'e', 'f', 'g'] >>> # replace some values >>> letters[2:5] = ['C', 'D', 'E'] >>> letters ['a', 'b', 'C', 'D', 'E', 'f', 'g'] >>> # now remove them >>> letters[2:5] = [] >>> letters ['a', 'b', 'f', 'g'] >>> # clear the list by replacing all the elements with an empty list >>> letters[:] = [] >>> letters [] 

內置函數len()也適用於列表:

>>>
>>> letters = ['a', 'b', 'c', 'd'] >>> len(letters) 4 

能夠嵌套列表(建立包含其餘列表的列表),例如:

>>>
>>> a = ['a', 'b', 'c'] >>> n = [1, 2, 3] >>> x = [a, n] >>> x [['a', 'b', 'c'], [1, 2, 3]] >>> x[0] ['a', 'b', 'c'] >>> x[0][1] 'b' 

3.2 邁向編程的第一步

固然,咱們能夠將Python用於更復雜的任務,而不是將兩個和兩個一塊兒添加。例如,咱們能夠編寫Fibonacci 系列的初始子序列,以下所示:

>>>
>>> # Fibonacci series: ... # the sum of two elements defines the next ... a, b = 0, 1 >>> while b < 10: ... print(b) ... a, b = b, a+b ... 1 1 2 3 5 8 

此示例介紹了幾個新功能。

  • 第一行包含多個賦值:變量ab 同時獲取新值0和1.在最後一行再次使用它,證實在任何賦值發生以前,右邊的表達式都是先評估的。右側表達式從左到右進行評估。

  • while只要條件(此處爲:)保持爲真循環就會執行在Python中,就像在C中同樣,任何非零整數值都是真的; 零是假的。條件也能夠是字符串或列表值,其實是任何序列; 任何長度非零的東西都是真的,空序列都是假的。該示例中使用的測試是簡單的比較。標準比較運算符的編寫方式與C中的相同:( 小於),(大於), (等於),(小於或等於),(大於或等於)和(不等於)。10<>==<=>=!=

  • 循環是縮進:縮進是Python對語句分組的方法。在交互式提示符下,您必須爲每一個縮進行鍵入一個選項卡或空格。在實踐中,您將使用文本編輯器爲Python準備更復雜的輸入; 全部體面的文本編輯都有自動縮進功能。當以交互方式輸入複合語句時,必須後跟一個空行以指示完成(由於解析器在您鍵入最後一行時沒法猜出)。請注意,基本塊中的每一行必須縮進相同的數量。

  • print()函數寫入給定參數的值。它與僅處理多個參數,浮點數量和字符串的方式不一樣,只是編寫您想要編寫的表達式(正如咱們以前在計算器示例中所作的那樣)。字符串打印時不帶引號,並在項目之間插入空格,所以您能夠很好地格式化事物,以下所示:

    >>>
    >>> i = 256*256 >>> print('The value of i is', i) The value of i is 65536 

    關鍵字參數end可用於在輸出後避免換行,或者使用不一樣的字符串結束輸出:

    >>>
    >>> a, b = 0, 1 >>> while b < 1000: ... print(b, end=',') ... a, b = b, a+b ... 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,

四.更多控制流工具

除了while剛剛介紹語句以外,Python還知道其餘語言中常見的控制流語句,而且有一些曲折。

4.1 if陳述

也許最着名的陳述類型是if陳述。例如:

>>>
>>> x = int(input("Please enter an integer: ")) Please enter an integer: 42 >>> if x < 0: ... x = 0 ... print('Negative changed to zero') ... elif x == 0: ... print('Zero') ... elif x == 1: ... print('Single') ... else: ... print('More') ... More 

能夠有零個或多個elif零件,else零件是可選的。關鍵字' elif'是'else if'的縮寫,有助於避免過分縮進。一 if... ... elif... ... elif... ...序列的替代switch或 case其它語言中的語句。

4.2 for陳述

forPython中語句與您在C或Pascal中使用語句略有不一樣。而不是老是迭代數字的算術級數(如在Pascal中),或者讓用戶可以定義迭代步驟和暫停條件(如C),Python的for語句迭代任何序列的項目(列表或string),按照它們出如今序列中的順序。例如(沒有雙關語):

>>>
>>> # Measure some strings: ... words = ['cat', 'window', 'defenestrate'] >>> for w in words: ... print(w, len(w)) ... cat 3 window 6 defenestrate 12 

若是您須要修改在循環內迭代的序列(例如複製所選項目),建議您先複製一份。迭代序列不會隱式地複製。切片表示法使這特別方便:

>>>
>>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words ['defenestrate', 'cat', 'window', 'defenestrate'] 

有了,該示例將嘗試建立一個無限列表,一遍又一遍地插入for in words:defenestrate

4.3 range()功能

若是你須要迭代一系列數字,內置函數 range()就派上用場了。它生成算術進度:

>>>
>>> for i in range(5): ... print(i) ... 0 1 2 3 4 

給定的終點永遠不是生成序列的一部分; range(10)生成10個值,長度爲10的序列的項目的合法索引。可讓範圍從另外一個數字開始,或者指定不一樣的增量(甚至是負數;有時這稱爲「步驟」):

range(5, 10) 5, 6, 7, 8, 9 range(0, 10, 3) 0, 3, 6, 9 range(-10, -100, -30) -10, -40, -70 

要遍歷序列的索引,您能夠組合range()並 len()以下:

>>>
>>> a = ['Mary', 'had', 'a', 'little', 'lamb'] >>> for i in range(len(a)): ... print(i, a[i]) ... 0 Mary 1 had 2 a 3 little 4 lamb 

可是,在大多數此類狀況下,使用該enumerate() 函數很方便,請參閱循環技術

若是你只打印一個範圍,就會發生一件奇怪的事:

>>>
>>> print(range(10)) range(0, 10) 

在許多方面,返回的對象range()表現得好像它是一個列表,但事實上並不是如此。它是一個對象,當您迭代它時,它返回所需序列的連續項,但它並不真正使列表,從而節省空間。

咱們說這樣的對象是可迭代的,也就是說,適合做爲函數和構造的目標,這些函數和構造指望在供應耗盡以前它們能夠從中得到連續的項目。咱們已經看到該for語句是一個迭代器功能list() 是另外一個; 它從迭代建立列表:

>>>
>>> list(range(5)) [0, 1, 2, 3, 4] 

稍後咱們將看到更多返回iterables的函數,並將iterables做爲參數。

4.4 breakcontinue語句以及else循環條款

break聲明中,相似於C,爆發最內層的 forwhile循環。

循環語句可能有一個else子句; 當循環經過列表耗盡(with for)或條件變爲false(with while)時終止,可是當循環被break語句終止時不執行它這經過如下循環來舉例說明,該循環搜索素數:

>>>
>>> for n in range(2, 10): ... for x in range(2, n): ... if n % x == 0: ... print(n, 'equals', x, '*', n//x) ... break ... else: ... # loop fell through without finding a factor ... print(n, 'is a prime number') ... 2 is a prime number 3 is a prime number 4 equals 2 * 2 5 is a prime number 6 equals 2 * 3 7 is a prime number 8 equals 2 * 4 9 equals 3 * 3 

(是的,這是正確的代碼,仔細一看:該else條款屬於for循環,不是if。陳述)

當循環使用,該else條款有更多的共同點與 else一個條款try聲明比它認爲的 if語句:一個try語句的else時候也不例外條款發生運行和循環的else條款時沒有運行break 發生。有關try語句和異常的更多信息,請參閱 處理異常

continue聲明也是從C借用的,繼續循環的下一次迭代:

>>>
>>> for num in range(2, 10): ... if num % 2 == 0: ... print("Found an even number", num) ... continue ... print("Found a number", num) Found an even number 2 Found a number 3 Found an even number 4 Found a number 5 Found an even number 6 Found a number 7 Found an even number 8 Found a number 9 

4.5 pass陳述

pass語句什麼也不作。當語法須要語句但程序不須要操做時,能夠使用它。例如:

>>>
>>> while True: ... pass # Busy-wait for keyboard interrupt (Ctrl+C) ... 

這一般用於建立最小類:

>>>
>>> class MyEmptyClass: ... pass ... 

pass當您處理新代碼時,能夠使用另外一個地方做爲函數或條件體的佔位符,容許您在更抽象的層次上繼續思考。pass被自動忽略:

>>>
>>> def initlog(*args): ... pass # Remember to implement this! ... 

4.6 定義函數

咱們能夠建立一個將Fibonacci系列寫入任意邊界的函數:

>>>
>>> def fib(n): # write Fibonacci series up to n ... """Print a Fibonacci series up to n.""" ... a, b = 0, 1 ... while a < n: ... print(a, end=' ') ... a, b = b, a+b ... print() ... >>> # Now call the function we just defined: ... fib(2000) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 

該關鍵字def引入了一個函數定義它必須後跟函數名稱和帶括號的形式參數列表。構成函數體的語句從下一行開始,而且必須縮進。

函數體的第一個語句能夠選擇是字符串文字; 此字符串文字是函數的文檔字符串或docstring(有關文檔字符串的更多信息,請參閱文檔字符串部分。)有些工具使用文檔字符串自動生成在線或印刷文檔,或者讓用戶以交互方式瀏覽代碼; 在您編寫的代碼中包含docstrings是一種很好的作法,因此要養成習慣。

函數執行引入了用於函數局部變量的新符號表。更準確地說,函數中的全部變量賦值都將值存儲在本地符號表中; 而變量引用首先在本地符號表中查找,而後在封閉函數的本地符號表中查找,而後在全局符號表中查找,最後在內置名稱表中查找。所以,全局變量不能直接在函數內賦值(除非在global語句中命名),儘管能夠引用它們。

調用函數調用的實際參數(參數)在被調用函數的本地符號表中引入; 所以,使用call by value傳遞參數(其中始終是對象引用,而不是對象的值)。[1]當函數調用另外一個函數時,將爲該調用建立一個新的本地符號表。

函數定義在當前符號表中引入函數名稱。函數名稱的值具備解釋器將其識別爲用戶定義函數的類型。此值能夠分配給另外一個名稱,該名稱也能夠用做函數。這是通常的重命名機制:

>>>
>>> fib <function fib at 10042ed0> >>> f = fib >>> f(100) 0 1 1 2 3 5 8 13 21 34 55 89 

來自其餘語言,您可能會反對這fib不是一個函數而是一個過程,由於它不返回值。實際上,即便是沒有return語句的函數也會 返回一個值,儘管它是一個至關無聊的值。調用此值None(它是內置名稱)。None若是它是惟一寫入的值,則解釋器一般會禁止寫入值。若是你真的想使用它,你能夠看到它print()

>>>
>>> fib(0) >>> print(fib(0)) None 

編寫一個函數能夠很簡單地返回Fibonacci系列的數字列表,而不是打印它:

>>>
>>> def fib2(n): # return Fibonacci series up to n ... """Return a list containing the Fibonacci series up to n.""" ... result = [] ... a, b = 0, 1 ... while a < n: ... result.append(a) # see below ... a, b = b, a+b ... return result ... >>> f100 = fib2(100) # call it >>> f100 # write the result [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] 

像往常同樣,此示例演示了一些新的Python功能:

  • return語句返回一個函數的值。 return沒有表達式參數返回None從函數的末尾掉落也會返回None
  • 該語句result.append(a)調用list對象 方法result方法是「屬於」對象並被命名的函數 obj.methodname,其中obj是某個對象(多是表達式),而且methodname是由對象的類型定義的方法的名稱。不一樣類型定義不一樣的方法。不一樣類型的方法能夠具備相同的名稱而不會引發歧義。(能夠使用定義本身的對象類型和方法,請參閱append()示例中顯示的方法是爲列表對象定義的; 它在列表的末尾添加了一個新元素。在這個例子中它至關於 ,但效率更高。result result [a]

4.7 更多關於定義函數的信息

也能夠使用可變數量的參數定義函數。有三種形式能夠組合。

4.7.1 默認參數值

最有用的形式是爲一個或多個參數指定默認值。這建立了一個函數,能夠使用比定義容許的參數更少的參數調用。例如:

def ask_ok(prompt, retries=4, reminder='Please try again!'): while True: ok = input(prompt) if ok in ('y', 'ye', 'yes'): return True if ok in ('n', 'no', 'nop', 'nope'): return False retries = retries - 1 if retries < 0: raise ValueError('invalid user response') print(reminder) 

能夠經過多種方式調用此函數:

  • 只給出強制性參數: ask_ok('Do you really want to quit?')
  • 給出一個可選參數: ask_ok('OK to overwrite the file?', 2)
  • 甚至給出全部論據: ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')

此示例還介紹了in關鍵字。這測試序列是否包含某個值。

默認值在定義範圍內的函數定義點進行計算 ,以便進行

i = 5 def f(arg=i): print(arg) i = 6 f() 

將打印5

重要警告: 默認值僅評估一次。當默認值是可變對象(例如列表,字典或大多數類的實例)時,這會產生差別。例如,如下函數會累積在後續調用中傳遞給它的參數:

def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3)) 

這將打印出來

[1] [1, 2] [1, 2, 3] 

若是您不但願在後續調用之間共享默認值,則能夠編寫以下函數:

def f(a, L=None): if L is None: L = [] L.append(a) return L 

4.7.2 關鍵字參數

也能夠使用 表單的關鍵字參數調用函數kwarg=value例如,如下功能:

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): print("-- This parrot wouldn't", action, end=' ') print("if you put", voltage, "volts through it.") print("-- Lovely plumage, the", type) print("-- It's", state, "!") 

接受一個所需參數(voltage)和三個可選參數(stateaction,和type)。能夠經過如下任何方式調用此函數:

parrot(1000) # 1 positional argument parrot(voltage=1000) # 1 keyword argument parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments parrot('a million', 'bereft of life', 'jump') # 3 positional arguments parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword 

但如下全部調用都將無效:

parrot() # required argument missing parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument parrot(110, voltage=220) # duplicate value for the same argument parrot(actor='John Cleese') # unknown keyword argument 

在函數調用中,關鍵字參數必須遵循位置參數。傳遞的全部關鍵字參數必須與函數接受的參數之一匹配(例如actor,不是函數的有效參數 parrot),而且它們的順序並不重要。這還包括非可選參數(例如parrot(voltage=1000)也是有效的)。沒有參數可能會屢次得到一個值。如下是因爲此限制而失敗的示例:

>>>
>>> def function(a): ... pass ... >>> function(0, a=0) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: function() got multiple values for keyword argument 'a' 

**name存在表單的最終形式參數時,它接收包含除了與形式參數相對應的全部關鍵字參數的字典(參見映射類型 - 字典)。這能夠與形式的形式參數*name(在下一小節中描述)組合,該參數接收包含超出形式參數列表的位置參數的元組。*name必須在以前發生**name。)例如,若是咱們定義一個這樣的函數:

def cheeseshop(kind, *arguments, **keywords): print("-- Do you have any", kind, "?") print("-- I'm sorry, we're all out of", kind) for arg in arguments: print(arg) print("-" * 40) for kw in keywords: print(kw, ":", keywords[kw]) 

它能夠像這樣調用:

cheeseshop("Limburger", "It's very runny, sir.", "It's really very, VERY runny, sir.", shopkeeper="Michael Palin", client="John Cleese", sketch="Cheese Shop Sketch") 

固然它會打印:

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch

請注意,打印關鍵字參數的順序保證與函數調用中提供它們的順序相匹配。

4.7.3 任意參數列表

最後,最不經常使用的選項是指定能夠使用任意數量的參數調用函數。這些參數將被包含在一個元組中(參見元組和序列)。在可變數量的參數以前,可能會出現零個或多個正常參數。

def write_multiple_items(file, separator, *args): file.write(separator.join(args)) 

一般,這些variadic參數將在形式參數列表中排在最後,由於它們會挖掘傳遞給函數的全部剩餘輸入參數。*args 參數以後出現的任何形式參數都是「僅關鍵字」參數,這意味着它們只能用做關鍵字而不是位置參數。

>>>
>>> def concat(*args, sep="/"): ... return sep.join(args) ... >>> concat("earth", "mars", "venus") 'earth/mars/venus' >>> concat("earth", "mars", "venus", sep=".") 'earth.mars.venus' 

4.7.4 解壓縮參數列表

當參數已經在列表或元組中但須要爲須要單獨位置參數的函數調用解包時,會發生相反的狀況。例如,內置range()函數須要單獨的 startstop參數。若是它們不是單獨可用的,請使用*-operator 編寫函數調用以 從列表或元組中解壓縮參數:

>>>
>>> list(range(3, 6)) # normal call with separate arguments [3, 4, 5] >>> args = [3, 6] >>> list(range(*args)) # call with arguments unpacked from a list [3, 4, 5] 

以相同的方式,字典能夠使用**-operator 提供關鍵字參數

>>>
>>> def parrot(voltage, state='a stiff', action='voom'): ... print("-- This parrot wouldn't", action, end=' ') ... print("if you put", voltage, "volts through it.", end=' ') ... print("E's", state, "!") ... >>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"} >>> parrot(**d) -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised ! 

4.7.5 Lambda表達式

能夠使用lambda關鍵字建立小的匿名函數此函數返回其兩個參數的總和:Lambda函數能夠在須要函數對象的任何地方使用。它們在語法上限於單個表達式。從語義上講,它們只是正常函數定義的語法糖。與嵌套函數定義同樣,lambda函數能夠引用包含範圍的變量:lambda a, b: a+b

>>>
>>> def make_incrementor(n): ... return lambda x: x + n ... >>> f = make_incrementor(42) >>> f(0) 42 >>> f(1) 43 

上面的示例使用lambda表達式返回一個函數。另外一個用途是傳遞一個小函數做爲參數:

>>>
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')] >>> pairs.sort(key=lambda pair: pair[1]) >>> pairs [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')] 

4.7.6 文檔字符串

如下是有關文檔字符串的內容和格式的一些約定。

第一行應始終是對象目的的簡短概述。爲簡潔起見,它不該顯式聲明對象的名稱或類型,由於這些可經過其餘方式得到(除非名稱剛好是描述函數操做的動詞)。該行應以大寫字母開頭,以句點結尾。

若是文檔字符串中有更多行,則第二行應爲空白,從而在視覺上將摘要與其他描述分開。如下行應該是一個或多個段落,描述對象的調用約定,其反作用等。

Python解析器不會從Python中刪除多行字符串文字的縮進,所以處理文檔的工具必須在須要時刪除縮進。這是使用如下約定完成的。字符串第一以後的第一個非空行 肯定整個文檔字符串的縮進量。(咱們不能使用第一行,由於它一般與字符串的開頭引號相鄰,所以它的縮進在字符串文字中不明顯。)而後從字符串的全部行的開頭剝離與該縮進「等效」的空格。 。縮進的行不該該出現,可是若是它們出現,則應該剝離它們的全部前導空格。應在擴展標籤後測試空白的等效性(一般爲8個空格)。

如下是多行文檔字符串的示例:

>>>
>>> def my_function(): ... """Do nothing, but document it. ... ...  No, really, it doesn't do anything. ...  """ ... pass ... >>> print(my_function.__doc__) Do nothing, but document it.  No, really, it doesn't do anything. 

4.7.7 功能註釋

函數註釋是關於用戶定義函數使用的類型的徹底可選元數據信息(請參閱PEP 3107和 PEP 484瞭解更多信息)。

註釋__annotations__做爲字典存儲在函數屬性中,對函數的任何其餘部分沒有影響。參數註釋由參數名稱後面的冒號定義,後跟一個表達式,用於評估註釋的值。返回註釋由->參數列表和表示def語句結尾的冒號之間的文字,後跟表達式定義如下示例具備位置參數,關鍵字參數和註釋的返回值:

>>>
>>> def f(ham: str, eggs: str = 'eggs') -> str: ... print("Annotations:", f.__annotations__) ... print("Arguments:", ham, eggs) ... return ham + ' and ' + eggs ... >>> f('spam') Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>} Arguments: spam eggs 'spam and eggs' 

4.8 Intermezzo:編碼風格

如今您要編寫更長,更復雜的Python,如今是討論編碼風格的好時機大多數語言均可以用不一樣的風格編寫(或更簡潔,格式化); 有些比其餘人更具可讀性。讓其餘人輕鬆閱讀您的代碼老是一個好主意,採用一種好的編碼風格對此有很大幫助。

對於Python, PEP 8已成爲大多數項目堅持的風格指南; 它促進了一種很是易讀且使人賞心悅目的編碼風格。每一個Python開發人員都應該在某個時候閱讀它; 如下是爲您提取的最重要的要點:

  • 使用4空格縮進,沒有標籤。

    4個空格是小壓痕(容許更大的嵌套深度)和大壓痕(更容易閱讀)之間的良好折衷。標籤引入混淆,最好省略。

  • 換行,使其不超過79個字符。

    這有助於用戶使用小型顯示器,而且能夠在較大的顯示器上並排放置多個代碼文件。

  • 使用空行分隔函數和類,以及函數內的較大代碼塊。

  • 若是可能,將評論放在他們本身的一行上。

  • 使用docstrings。

  • 在操做符周圍和逗號後面使用空格,但不能直接在包圍結構中使用:f(1, 2) g(3, 4)

  • 一致地命名您的類和函數; 約定 CamelCase用於類和lower_case_with_underscores函數和方法。始終使用self第一個方法參數的名稱(有關類和方法的更多信息,請參閱類的初步查看)。

  • 若是您的代碼旨在用於國際環境,請不要使用花哨的編碼。Python的默認值,UTF-8甚至純ASCII在任何狀況下都能最好地工做。

  • 一樣,若是隻有最輕微的機會,說不一樣語言的人會閱讀或維護代碼,請不要在標識符中使用非ASCII字符。

五.數據結構

本章將更詳細地介紹您已經瞭解的一些內容,並添加了一些新內容。

5.1 更多關於列表

列表數據類型有更多方法。如下是列表對象的全部方法:

list. append

將項添加到列表的末尾。至關於a[len(a):] [x]

list. extend 可迭代的

經過附加iterable中的全部項來擴展列表。至關於 a[len(a):] iterable

list. insert i

在給定位置插入項目。第一個參數是要插入的元素的索引,所以插入列表的前面,而且等效於a.insert(0,x)a.insert(len(a), x)a.append(x)

list. remove

從列表中刪除值爲x的第一個項目若是沒有這樣的項目則是錯誤的。

list. pop

刪除列表中給定位置的項目,而後將其返回。若是未指定索引,則a.pop()刪除並返回列表中的最後一項。方法簽名中i周圍的方括號表示該參數是可選的,而不是您應該在該位置鍵入方括號。您將在Python Library Reference中常常看到這種表示法。)

list. clear

從列表中刪除全部項目。至關於del a[:]

list. index [start [end 

在值爲x的第一個項的列表中返回從零開始的索引ValueError若是沒有這樣的項目,則提升a 

可選參數startend被解釋爲切片表示法,並用於將搜索限制爲列表的特定子序列。返回的索引是相對於完整序列的開頭而不是start參數計算的。

list. count

返回x出如今列表中的次數。

list. sort key = Nonereverse = False 

對列表中的項目進行排序(參數可用於排序自定義,請參閱sorted()其說明)。

list. reverse

反轉列表中的元素。

list. copy

返回列表的淺表副本。至關於a[:]

使用大多數列表方法的示例:

>>>
>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana'] >>> fruits.count('apple') 2 >>> fruits.count('tangerine') 0 >>> fruits.index('banana') 3 >>> fruits.index('banana', 4) # Find next banana starting a position 4 6 >>> fruits.reverse() >>> fruits ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange'] >>> fruits.append('grape') >>> fruits ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape'] >>> fruits.sort() >>> fruits ['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear'] >>> fruits.pop() 'pear' 

您可能已經注意到,方法同樣insertremove或者sort只修改列表沒有返回值印刷-它們返回的默認 None[1] 這是Python中全部可變數據結構的設計原則。

5.1.1 使用列表做爲堆棧

list方法能夠很容易地將列表用做堆棧,其中添加的最後一個元素是檢索到的第一個元素(「last-in,first-out」)。要將項添加到堆棧頂部,請使用append()要從堆棧頂部檢索項目,請在pop()沒有顯式索引的狀況下使用例如:

>>>
>>> stack = [3, 4, 5] >>> stack.append(6) >>> stack.append(7) >>> stack [3, 4, 5, 6, 7] >>> stack.pop() 7 >>> stack [3, 4, 5, 6] >>> stack.pop() 6 >>> stack.pop() 5 >>> stack [3, 4] 

5.1.2 使用列表做爲隊列

也能夠使用列表做爲隊列,其中添加的第一個元素是檢索的第一個元素(「先進先出」); 可是,列表不能用於此目的。雖然列表末尾的追加和彈出很快,可是從列表的開頭進行插入或彈出是很慢的(由於全部其餘元素都必須移動一個)。

要實現隊列,請使用collections.deque設計爲具備快速追加和從兩端彈出的隊列例如:

>>>
>>> from collections import deque >>> queue = deque(["Eric", "John", "Michael"]) >>> queue.append("Terry") # Terry arrives >>> queue.append("Graham") # Graham arrives >>> queue.popleft() # The first to arrive now leaves 'Eric' >>> queue.popleft() # The second to arrive now leaves 'John' >>> queue # Remaining queue in order of arrival deque(['Michael', 'Terry', 'Graham']) 

5.1.3 列表理解

列表推導提供了建立列表的簡明方法。常見的應用是建立新的列表,其中每一個元素是應用於另外一個序列的每一個成員或可迭代的一些操做的結果,或者建立知足特定條件的那些元素的子序列。

例如,假設咱們要建立一個正方形列表,例如:

>>>
>>> squares = [] >>> for x in range(10): ... squares.append(x**2) ... >>> squares [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

請注意,這會建立(或覆蓋)一個名爲x在循環完成後仍然存在的變量咱們能夠使用如下方法計算沒有任何反作用的正方形列表:

squares = list(map(lambda x: x**2, range(10))) 

或者,等效地:

squares = [x**2 for x in range(10)] 

這更簡潔,更易讀。

列表推導由括號組成,括號中包含一個表達式,後跟一個for子句,而後是零個或多個forif 子句。結果將是一個新的列表,該列表是經過在其後面forif子句的上下文中評估表達式而獲得的。例如,若是列表不相等,則此listcomp將兩個列表的元素組合在一塊兒:

>>>
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y] [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)] 

它至關於:

>>>
>>> combs = [] >>> for x in [1,2,3]: ... for y in [3,1,4]: ... if x != y: ... combs.append((x, y)) ... >>> combs [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)] 

請注意這兩個片斷中forif語句的順序是如何相同的。

若是表達式是元組(例如前面示例中的元組),則必須將其括起來。(x, y)

>>>
>>> vec = [-4, -2, 0, 2, 4] >>> # create a new list with the values doubled >>> [x*2 for x in vec] [-8, -4, 0, 4, 8] >>> # filter the list to exclude negative numbers >>> [x for x in vec if x >= 0] [0, 2, 4] >>> # apply a function to all the elements >>> [abs(x) for x in vec] [4, 2, 0, 2, 4] >>> # call a method on each element >>> freshfruit = [' banana', ' loganberry ', 'passion fruit '] >>> [weapon.strip() for weapon in freshfruit] ['banana', 'loganberry', 'passion fruit'] >>> # create a list of 2-tuples like (number, square) >>> [(x, x**2) for x in range(6)] [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)] >>> # the tuple must be parenthesized, otherwise an error is raised >>> [x, x**2 for x in range(6)]  File "<stdin>", line 1, in <module>  [x, x**2 for x in range(6)]  ^ SyntaxError: invalid syntax >>> # flatten a list using a listcomp with two 'for' >>> vec = [[1,2,3], [4,5,6], [7,8,9]] >>> [num for elem in vec for num in elem] [1, 2, 3, 4, 5, 6, 7, 8, 9] 

列表推導能夠包含複雜的表達式和嵌套函數:

>>>
>>> from math import pi >>> [str(round(pi, i)) for i in range(1, 6)] ['3.1', '3.14', '3.142', '3.1416', '3.14159'] 

5.1.4 嵌套列表理解

列表推導中的初始表達式能夠是任意表達式,包括另外一個列表推導。

考慮如下3x4矩陣示例,該矩陣實現爲3個長度爲4的列表:

>>>
>>> matrix = [ ... [1, 2, 3, 4], ... [5, 6, 7, 8], ... [9, 10, 11, 12], ... ] 

如下列表理解將轉置行和列:

>>>
>>> [[row[i] for row in matrix] for i in range(4)] [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]] 

正如咱們在上一節中看到的那樣,嵌套的listcomp在其後面的上下文中進行計算for,所以該示例等效於:

>>>
>>> transposed = [] >>> for i in range(4): ... transposed.append([row[i] for row in matrix]) ... >>> transposed [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]] 

反過來,它與:

>>>
>>> transposed = [] >>> for i in range(4): ... # the following 3 lines implement the nested listcomp ... transposed_row = [] ... for row in matrix: ... transposed_row.append(row[i]) ... transposed.append(transposed_row) ... >>> transposed [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]] 

在現實世界中,您應該更喜歡內置函數來處理複雜的流語句。zip()功能對於這個用例很是有用:

>>>
>>> list(zip(*matrix)) [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)] 

有關此行中星號的詳細信息,請參閱解壓縮參數列表

5.2 del聲明

有一種方法能夠從列表中刪除一個項目,而不是它的值:del語句。這與pop()返回值方法不一樣del語句還可用於從列表中刪除切片或清除整個列表(咱們以前經過將空列表分配給切片來執行此操做)。例如:

>>>
>>> a = [-1, 1, 66.25, 333, 333, 1234.5] >>> del a[0] >>> a [1, 66.25, 333, 333, 1234.5] >>> del a[2:4] >>> a [1, 66.25, 1234.5] >>> del a[:] >>> a [] 

del 也能夠用來刪除整個變量:

>>>
>>> del a 

a如下引用名稱是一個錯誤(至少在爲其分配了另外一個值以前)。咱們會在del之後找到其餘用途

5.3 元組和序列

咱們看到列表和字符串有許多常見屬性,例如索引和切片操做。它們是序列數據類型的兩個示例(請參閱 序列類型 - 列表,元組,範圍)。因爲Python是一種不斷髮展的語言,所以可能會添加其餘序列數據類型。還有另外一種標準序列數據類型: 元組

元組由逗號分隔的多個值組成,例如:

>>>
>>> t = 12345, 54321, 'hello!' >>> t[0] 12345 >>> t (12345, 54321, 'hello!') >>> # Tuples may be nested: ... u = t, (1, 2, 3, 4, 5) >>> u ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5)) >>> # Tuples are immutable: ... t[0] = 88888 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> # but they can contain mutable objects: ... v = ([1, 2, 3], [3, 2, 1]) >>> v ([1, 2, 3], [3, 2, 1]) 

如您所見,輸出元組始終用括號括起來,以便正確解釋嵌套元組; 它們能夠輸入有或沒有周圍的括號,儘管一般括號是必要的(若是元組是較大表達式的一部分)。沒法分配元組的各個項,可是能夠建立包含可變對象的元組,例如列表。

儘管元組看起來與列表相似,但它們一般用於不一樣的狀況並用於不一樣的目的。元組是不可變的,而且一般包含異構的元素序列,這些元素能夠經過解包(參見本節後面部分)或索引(或者甚至是屬性的狀況下namedtuples)來訪問。列表是可變的,它們的元素一般是同類的,能夠經過遍歷列表來訪問。

一個特殊的問題是構造包含0或1項的元組:語法有一些額外的怪癖來適應這些。空元組是由一對空括號構成的; 經過使用逗號跟隨值來構造具備一個項目的元組(在括號中包含單個值是不夠的)。醜陋但有效。例如:

>>>
>>> empty = () >>> singleton = 'hello', # <-- note trailing comma >>> len(empty) 0 >>> len(singleton) 1 >>> singleton ('hello',) 

該語句元組打包的一個例子:值在元組中打包在一塊兒。反向操做也是可能的:12345, 54321,'hello!'1234554321'hello!'

>>>
>>> x, y, z = t 

這足夠恰當地稱爲序列解包,適用於右側的任何序列。序列解包須要在等號左側有儘量多的變量,由於序列中有元素。請注意,多重賦值實際上只是元組打包和序列解包的組合。

5.4 集合

Python還包括集合的數據類型集合是無序集合,沒有重複元素。基本用途包括成員資格測試和消除重複條目。集合對象還支持數學運算,如並集,交集,差別和對稱差別。

大括號或set()函數可用於建立集合。注意:要建立一個空集,你必須使用set(),而不是{}後者建立一個空字典,一個咱們將在下一節討論的數據結構。

這是一個簡短的演示:

>>>
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} >>> print(basket) # show that duplicates have been removed {'orange', 'banana', 'pear', 'apple'} >>> 'orange' in basket # fast membership testing True >>> 'crabgrass' in basket False >>> # Demonstrate set operations on unique letters from two words ... >>> a = set('abracadabra') >>> b = set('alacazam') >>> a # unique letters in a {'a', 'r', 'b', 'c', 'd'} >>> a - b # letters in a but not in b {'r', 'd', 'b'} >>> a | b # letters in a or b or both {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'} >>> a & b # letters in both a and b {'a', 'c'} >>> a ^ b # letters in a or b but not both {'r', 'd', 'b', 'm', 'z', 'l'} 

列表推導相似,也支持集合理解:

>>>
>>> a = {x for x in 'abracadabra' if x not in 'abc'} >>> a {'r', 'd'} 

5.5 字典

Python內置的另外一個有用的數據類型是字典(請參閱 映射類型 - 字典)。詞典有時在其餘語言中被稱爲「關聯記憶」或「關聯陣列」。與經過一系列數字索引的序列不一樣,字典由索引,能夠是任何不可變類型; 字符串和數字老是鍵。若是元組僅包含字符串,數字或元組,則它們可用做鍵; 若是元組直接或間接包含任何可變對象,則不能將其用做鍵。你不能用鏈表作關鍵字,由於鏈表能夠用索引賦值,切片賦值,或相似的方法進行修改append()extend()

最好將字典視爲一組無序的鍵:值對,並要求鍵是惟一的(在一個字典中)。一對大括號建立一個空字典:{}在括號內放置以逗號分隔的鍵:值對列表,將初始鍵:值對添加到字典中; 這也是字典在輸出上的寫法。

字典上的主要操做是使用某個鍵存儲值並提取給定鍵的值。也能夠刪除鍵:值對del若是使用已在使用的密鑰進行存儲,則會忘記與該密鑰關聯的舊值。使用不存在的密鑰提取值是錯誤的。

list(d.keys())對字典執行會以任意順序返回字典中使用的全部鍵的列表(若是您但願對其進行排序,則只需使用它 sorted(d.keys()))。[2] 要檢查單個鍵是否在字典中,請使用in關鍵字。

這是一個使用字典的小例子:

>>>
>>> tel = {'jack': 4098, 'sape': 4139} >>> tel['guido'] = 4127 >>> tel {'sape': 4139, 'guido': 4127, 'jack': 4098} >>> tel['jack'] 4098 >>> del tel['sape'] >>> tel['irv'] = 4127 >>> tel {'guido': 4127, 'irv': 4127, 'jack': 4098} >>> list(tel.keys()) ['irv', 'guido', 'jack'] >>> sorted(tel.keys()) ['guido', 'irv', 'jack'] >>> 'guido' in tel True >>> 'jack' not in tel False 

dict()構造直接從鍵-值對的序列構建字典:

>>>
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) {'sape': 4139, 'jack': 4098, 'guido': 4127} 

此外,dict comprehensions可用於從任意鍵和值表達式建立字典:

>>>
>>> {x: x**2 for x in (2, 4, 6)} {2: 4, 4: 16, 6: 36} 

當鍵是簡單字符串時,有時使用關鍵字參數指定對更容易:

>>>
>>> dict(sape=4139, guido=4127, jack=4098) {'sape': 4139, 'jack': 4098, 'guido': 4127} 

5.6 循環技術

循環遍歷字典時,能夠使用該items()方法同時檢索密鑰和相應的值

>>>
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'} >>> for k, v in knights.items(): ... print(k, v) ... gallahad the pure robin the brave 

循環遍歷序列時,能夠使用該enumerate()函數同時檢索位置索引和相應的值

>>>
>>> for i, v in enumerate(['tic', 'tac', 'toe']): ... print(i, v) ... 0 tic 1 tac 2 toe 

要同時循環兩個或更多個序列,條目能夠與該zip()功能配對

>>>
>>> questions = ['name', 'quest', 'favorite color'] >>> answers = ['lancelot', 'the holy grail', 'blue'] >>> for q, a in zip(questions, answers): ... print('What is your {0}? It is {1}.'.format(q, a)) ... What is your name? It is lancelot. What is your quest? It is the holy grail. What is your favorite color? It is blue. 

要反向循環序列,首先在正向指定序列,而後調用該reversed()函數。

>>>
>>> for i in reversed(range(1, 10, 2)): ... print(i) ... 9 7 5 3 1 

要按排序順序循環序列,請使用sorted()返回新排序列表函數,同時保持源不變。

>>>
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] >>> for f in sorted(set(basket)): ... print(f) ... apple banana orange pear 

當你循環它時,有時頗有可能改變一個列表; 可是,建立新列表一般更簡單,更安全。

>>>
>>> import math >>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8] >>> filtered_data = [] >>> for value in raw_data: ... if not math.isnan(value): ... filtered_data.append(value) ... >>> filtered_data [56.2, 51.7, 55.3, 52.5, 47.8] 

5.7 更多關於條件

whileif語句中使用的條件能夠包含任何運算符,而不只僅是比較。

比較運算符in檢查序列中是否出現(不發生)值。運算符比較兩個對象是否真的是同一個對象; 這隻對像列表這樣的可變對象很重要。全部比較運算符具備相同的優先級,低於全部數值運算符的優先級。not inisis not

比較能夠連接。例如,測試是否小於,並且等於== cabbc

比較能夠使用布爾運算符進行組合andor,和一個比較的結果(或任何其它的布爾表達式的)能夠與否認not這些優先級低於比較運算符; 它們之間,not具備最高優先級和or最低優先級,所以至關於與往常同樣,括號可用於表達所需的組成。and not or C(A and (not B)) or C

布爾運算符andor所謂的短路 運算符:它們的參數從左到右進行計算,一旦肯定結果,評估就會中止。例如,若是AC爲true但B爲false,則不評估表達式 當用做通常值而不是布爾值時,短路運算符的返回值是最後一個求值的參數。and and CC

能夠將比較結果或其餘布爾表達式分配給變量。例如,

>>>
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance' >>> non_null = string1 or string2 or string3 >>> non_null 'Trondheim' 

請注意,在Python中,與C不一樣,賦值不能出如今表達式中。C程序員可能會抱怨這一點,但它避免了C程序中遇到的常見問題:===預期輸入表達式

5.8 比較序列和其餘類型

能夠將序列對象與具備相同序列類型的其餘對象進行比較。比較使用詞典排序:首先比較前兩個項目,若是它們不一樣,則肯定比較的結果; 若是它們相等,則比較接下來的兩個項目,依此類推,直到任一序列用完爲止。若是要比較的兩個項自己是相同類型的序列,則遞歸地執行詞典比較。若是兩個序列的全部項目相等,則認爲序列相等。若是一個序列是另外一個序列的初始子序列,則較短的序列是較小的(較小的)序列。字符串的字典順序使用Unicode代碼點編號來排序單個字符。相同類型序列之間比較的一些示例:

(1, 2, 3) < (1, 2, 4) [1, 2, 3] < [1, 2, 4] 'ABC' < 'C' < 'Pascal' < 'Python' (1, 2, 3, 4) < (1, 2, 4) (1, 2) < (1, 2, -1) (1, 2, 3) == (1.0, 2.0, 3.0) (1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4) 

請注意,若是對象具備適當的比較方法,則將不一樣類型的對象與對象進行比較<或是>合法的。例如,混合數字類型根據它們的數值進行比較,所以0等於0.0等。不然,解釋器將引起TypeError異常,而不是提供任意排序

六.模塊

若是退出Python解釋器並再次輸入,則所作的定義(函數和變量)將丟失。所以,若是您想編寫一個稍長的程序,最好使用文本編輯器爲解釋器準備輸入並將該文件做爲輸入運行。這稱爲建立腳本隨着程序變得愈來愈長,您可能但願將其拆分爲多個文件以便於維護。您可能還想使用您在多個程序中編寫的便捷功能,而無需將其定義複製到每一個程序中。

爲了支持這一點,Python有一種方法能夠將定義放在一個文件中,並在腳本或解釋器的交互式實例中使用它們。這樣的文件稱爲 模塊 ; 模塊中的定義能夠導入到其餘模塊或模塊中(在頂級和計算器模式下執行的腳本中能夠訪問的變量集合)。

模塊是包含Python定義和語句的文件。文件名是.py附加後綴的模塊名稱在模塊中,模塊的名稱(做爲字符串)可用做全局變量的值 __name__例如,使用您喜歡的文本編輯器建立一個fibo.py在當前目錄中調用的文件,其中包含如下內容:

# Fibonacci numbers module

def fib(n): # write Fibonacci series up to n a, b = 0, 1 while b < n: print(b, end=' ') a, b = b, a+b print() def fib2(n): # return Fibonacci series up to n result = [] a, b = 0, 1 while b < n: result.append(b) a, b = b, a+b return result 

如今輸入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) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>> fibo.__name__ 'fibo' 

若是您打算常用某個函數,能夠將其分配給本地名稱:

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

6.1 更多關於模塊

模塊能夠包含可執行語句以及函數定義。這些語句用於初始化模塊。它們僅在一次在import語句中遇到模塊名時執行[1] (若是文件做爲腳本執行,它們也會運行。)

每一個模塊都有本身的私有符號表,該表用做模塊中定義的全部函數的全局符號表。所以,模塊的做者能夠在模塊中使用全局變量,而沒必要擔憂與用戶的全局變量的意外衝突。另外一方面,若是您知道本身在作什麼,則能夠使用與其函數相同的符號來觸摸模塊的全局變量modname.itemname

模塊能夠導入其餘模塊。習慣但不要求將全部 import語句放在模塊的開頭(或腳本,就此而言)。導入的模塊名稱放在導入模塊的全局符號表中。

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程序員不使用此工具,由於它在解釋器中引入了一組未知的名稱,可能隱藏了您已定義的一些內容。

請注意,一般*從模塊或包導入的作法是不受歡迎的,由於它常常致使代碼可讀性差。可是,能夠使用它來保存交互式會話中的輸入。

若是後跟模塊名稱as,則如下名稱as將直接綁定到導入的模塊。

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

這其實是以與 將要執行的方式相同的方式導入模塊import fibofib

在使用from具備相似效果時也能夠使用它

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

注意

 

出於效率緣由,每一個模塊僅在每一個解釋器會話中導入一次。所以,若是更改模塊,則必須從新啓動解釋器 - 或者,若是隻是一個模塊要進行交互式測試,請使用importlib.reload(),例如import importlib;importlib.reload(modulename)

6.1.1 將模塊做爲腳本執行

當你運行Python模塊時

python fibo.py <arguments> 

將執行模塊中的代碼,就像您導入它同樣,但__name__設置爲"__main__"這意味着經過在模塊的末尾添加此代碼:

if __name__ == "__main__": import sys fib(int(sys.argv[1])) 

您能夠使該文件可用做腳本以及可導入模塊,由於解析命令行的代碼僅在模塊做爲「主」文件執行時纔會運行:

$ python fibo.py 50 1 1 2 3 5 8 13 21 34 

若是導入模塊,則不運行代碼:

>>>
>>> import fibo >>> 

這一般用於爲模塊提供方便的用戶界面,或用於測試目的(在腳本執行測試套件時運行模塊)。

6.1.2 模塊搜索路徑

spam導入命名模塊時,解釋器首先搜索具備該名稱的內置模塊。若是未找到,則會搜索spam.py由變量給出的目錄列表中指定 的文件sys.path。 sys.path從這些位置初始化:

  • 包含輸入腳本的目錄(或未指定文件時的當前目錄)。
  • PYTHONPATH (目錄名列表,語法與shell變量相同 PATH)。
  • 依賴於安裝的默認值。

注意

在支持符號連接的文件系統上,在遵循符號連接後計算包含輸入腳本的目錄。換句話說,包含符號連接的目錄不會添加到模塊搜索路徑中。

初始化後,Python程序能夠修改sys.path包含正在運行的腳本的目錄位於搜索路徑的開頭,位於標準庫路徑以前。這意味着將加載該目錄中的腳本,而不是庫目錄中的同名模塊。除非有意更換,不然這是一個錯誤。有關更多信息,請參見標準模塊一節

6.1.3 「編譯」的Python文件

爲了加速加載模塊,Python將每一個模塊的編譯版本緩存在__pycache__名稱下的目錄中,其中版本對編譯文件的格式進行編碼; 它一般包含Python版本號。例如,在CPython版本3.3中,spam.py的編譯版本將被緩存爲此命名約定容許來自不一樣版本和不一樣版本的Python的已編譯模塊共存。module.version.pyc__pycache__/spam.cpython-33.pyc

Python根據編譯版本檢查源的修改日期,以查看它是否已過時並須要從新編譯。這是一個徹底自動化的過程。此外,編譯的模塊與平臺無關,所以能夠在具備不一樣體系結構的系統之間共享相同的庫。

Python在兩種狀況下不檢查緩存。首先,它老是從新編譯而且不存儲直接從命令行加載的模塊的結果。其次,若是沒有源模塊,它不會檢查緩存。要支持非源(僅編譯)分發,已編譯的模塊必須位於源目錄中,而且不得有源模塊。

專家提示:

  • 您能夠使用Python命令上-O-OO開關來減少已編譯模塊的大小。-O開關刪除斷言語句時,-OO開關同時刪除斷言語句和__doc__字符串。因爲某些程序可能依賴於這些程序可用,所以若是您知道本身在作什麼,則應該只使用此選項。「優化」模塊有一個opt-標籤,一般更小。將來版本可能會改變優化的效果。
  • .pyc 文件讀取程序時,程序運行速度不比文件讀取程序時運行速度快.py.pyc文件來講,惟一更快的是它們加載的速度。
  • 該模塊compileall能夠爲目錄中的全部模塊建立.pyc文件。
  • 有關此過程的更多詳細信息,包括決策的流程圖 PEP 3147

6.2 標準模塊

Python附帶了一個標準模塊庫,在單獨的文檔Python庫參考(如下稱爲「庫參考」)中進行了描述。一些模塊內置於解釋器中; 這些操做提供對不屬於語言核心但仍然內置的操做的訪問,以提升效率或提供對系統調用等操做系統原語的訪問。這些模塊的集合是一個配置選項,它也取決於底層平臺。例如,該winreg模塊僅在Windows系統上提供。一個特定的模塊值得注意: sys它是內置於每一個Python解釋器中的。變量 sys.ps1sys.ps2定義用做主要和次要提示的字符串:

>>>
>>> import sys >>> sys.ps1 '>>> ' >>> sys.ps2 '... ' >>> sys.ps1 = 'C> ' C> print('Yuck!') Yuck! C> 

僅當解釋器處於交互模式時才定義這兩個變量。

變量sys.path是一個字符串列表,用於肯定解釋器的模塊搜索路徑。它被初始化爲從環境變量獲取的默認路徑PYTHONPATH,或者來自內置默認值 PYTHONPATH沒有設定。您能夠使用標準列表操做對其進行修改:

>>>
>>> import sys >>> sys.path.append('/ufs/guido/lib/python') 

6.3 dir()功能

內置函數dir()用於找出模塊定義的名稱。它返回一個排序的字符串列表:

>>>
>>> import fibo, sys >>> dir(fibo) ['__name__', 'fib', 'fib2'] >>> dir(sys) ['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__',  '__package__', '__stderr__', '__stdin__', '__stdout__',  '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe',  '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv',  'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder',  'call_tracing', 'callstats', 'copyright', 'displayhook',  'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix',  'executable', 'exit', 'flags', 'float_info', 'float_repr_style',  'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',  'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',  'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount',  'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',  'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path',  'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',  'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit',  'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout',  'thread_info', 'version', 'version_info', 'warnoptions'] 

沒有參數,dir()列出您當前定義的名稱:

>>>
>>> a = [1, 2, 3, 4, 5] >>> import fibo >>> fib = fibo.fib >>> dir() ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys'] 

請注意,它列出了全部類型的名稱:變量,模塊,函數等。

dir()不列出內置函數和變量的名稱。若是您須要這些列表,則在標準模塊中定義它們 builtins

>>>
>>> import builtins >>> dir(builtins) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',  'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',  'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',  'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',  'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',  'FileExistsError', 'FileNotFoundError', 'FloatingPointError',  'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',  'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError',  'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError',  'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented',  'NotImplementedError', 'OSError', 'OverflowError',  'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',  'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',  'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',  'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',  'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',  'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',  'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__',  '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs',  'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable',  'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits',  'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit',  'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',  'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',  'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview',  'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property',  'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',  'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars',  'zip'] 

6.4 

包是一種使用「點模塊名稱」構造Python模塊命名空間的方法。例如,模塊名稱A.B指定在名爲B的包中命名的子模塊A就像模塊的使用使得不一樣模塊的做者沒必要擔憂彼此的全局變量名稱同樣,使用虛線模塊名稱能夠節省NumPy或Pillow等多模塊軟件包的做者沒必要擔憂彼此的模塊名稱。

假設您要設計一組模塊(「包」),用於統一處理聲音文件和聲音數據。有許多不一樣的聲音格式(一般由它們的擴展的承認,例如:.wav, .aiff.au),因此你可能須要爲不一樣的文件格式之間轉換,建立和維護一個不斷增加的集合。您可能還須要對聲音數據執行許多不一樣的操做(例如混音,添加回聲,應用均衡器功能,建立人工立體聲效果),因此此外您還將編寫一個永無止境的模塊流來執行這些行動。這是您的包的可能結構(以分層文件系統的形式表示):

sound/                          Top-level package
      __init__.py               Initialize the sound package
      formats/                  Subpackage for file format conversions
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Subpackage for sound effects
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Subpackage for filters
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

導入包時,Python會在目錄中搜索 sys.pathpackage子目錄。

這些__init__.py文件須要使Python將目錄視爲包含包; 這樣作是爲了防止具備通用名稱的目錄,例如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 itemimportImportError

相反,當使用語法時,除了最後一個項目以外的每一個項目都必須是一個包; 最後一項能夠是模塊或包,但不能是前一項中定義的類或函數或變量。import item.subitem.subsubitem

6.4.1 從包中導入* 

如今用戶寫的時候會發生什麼理想狀況下,人們但願以某種方式傳遞給文件系統,找到包中存在哪些子模塊,並將它們所有導入。這可能須要很長時間,導入子模塊可能會產生沒必要要的反作用,這種反作用只有在顯式導入子模塊時纔會發生。from sound.effects import *

惟一的解決方案是讓包做者提供包的顯式索引。import語句使用如下約定:若是包的 __init__.py代碼定義了一個名爲的列表__all__,則它將被視爲遇到時應導入的模塊名稱列表在發佈新版本的軟件包時,由軟件包做者決定是否保持此列表的最新狀態。若是包裝做者沒有看到從包裝中導入*的用途,他們也可能決定不支持它。例如,該文件可能包含如下代碼:from package import *sound/effects/__init__.py

__all__ = ["echo", "surround", "reverse"] 

這意味着將導入的三個命名子模塊from sound.effects import *sound

若是__all__沒有定義,語句 也不會導入從包中的全部子模塊到當前的命名空間; 它只確保已導入(可能在其中運行任何初始化代碼),而後導入包中定義的任何名稱。這包括定義的任何名稱(以及顯式加載的子模塊)它還包括由先前語句顯式加載的包的任何子模塊考慮如下代碼:from sound.effects import*sound.effectssound.effects__init__.py__init__.pyimport

import sound.effects.echo import sound.effects.surround from sound.effects import * 

在此示例中,echosurround模塊將導入當前命名空間,由於它們是在執行語句sound.effects中定義的from...import(這在__all__定義時也有效 。)

雖然某些模塊設計爲在使用時僅導出遵循某些模式的名稱,但在生產代碼中仍然被認爲是很差的作法。import *

請記住,使用沒有錯實際上,除非導入模塊須要使用來自不一樣包的同名子模塊,不然這是推薦的表示法。fromPackage import specific_submodule

6.4.2 包內引用

當包被組織成子包時(與sound示例中包同樣),您能夠使用絕對導入來引用兄弟包的子模塊。例如,若是模塊sound.filters.vocoder須要使用包中echo模塊sound.effects,則能夠使用from sound.effects import echo

您還能夠使用import語句形式編寫相對導入。這些導入使用前導點來指示相對導入中涉及的當前和父包。 例如,模塊中,您能夠使用:from module import namesurround

from . import echo from .. import formats from ..filters import equalizer 

請注意,相對導入基於當前模塊的名稱。因爲主模塊的名稱始終是"__main__",所以用做Python應用程序主模塊的模塊必須始終使用絕對導入。

6.4.3 多個目錄中的包

包支持另外一個特殊屬性__path__這被初始化爲一個列表,其中包含在__init__.py執行該文件中的代碼以前保存包的目錄的名稱這個變量能夠修改; 這樣作會影響未來對包中包含的模塊和子包的搜索。

雖然一般不須要此功能,但它可用於擴展程序包中的模塊集。

七.輸入輸出

有幾種方法能夠呈現程序的輸出; 數據能夠以人類可讀的形式打印,或寫入文件以供未來使用。本章將討論一些可能性。

7.1 Fancier輸出格式

到目前爲止,咱們遇到了兩種寫值的方法:表達式語句print()函數。(第三種方法是使用write()文件對象方法;標準輸出文件能夠做爲參考sys.stdout。有關詳細信息,請參閱庫參考。)

一般,您須要更多地控制輸出的格式,而不只僅是打印空格分隔的值。有兩種方法能夠格式化輸出; 第一種方法是本身作全部的字符串處理; 使用字符串切片和鏈接操做,您能夠建立任何您能夠想象的佈局。字符串類型有一些方法能夠執行將字符串填充到給定列寬的有用操做; 這些將在稍後討論。第二種方法是使用格式化的字符串文字str.format()方法。

string模塊包含一個Template類,它提供了另外一種將值替換爲字符串的方法。

固然還有一個問題:如何將值轉換爲字符串?幸運的是,Python有辦法將任何值轉換爲字符串:將其傳遞給repr() 或str()函數。

str()函數用於返回值至關於人類可讀的值的表示,同時repr()用於生成可由解釋器讀取的表示(或者SyntaxError若是沒有等效語法則強制執行)。對於沒有特定人類消費表示的對象,str()將返回相同的值repr()許多值,例如數字或結構(如列表和字典),使用任一函數都具備相同的表示。特別是字符串有兩個不一樣的表示。

一些例子:

>>>
>>> s = 'Hello, world.' >>> str(s) 'Hello, world.' >>> repr(s) "'Hello, world.'" >>> str(1/7) '0.14285714285714285' >>> x = 10 * 3.25 >>> y = 200 * 200 >>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...' >>> print(s) The value of x is 32.5, and y is 40000... >>> # The repr() of a string adds string quotes and backslashes: ... hello = 'hello, world\n' >>> hellos = repr(hello) >>> print(hellos) 'hello, world\n' >>> # The argument to repr() may be any Python object: ... repr((x, y, ('spam', 'eggs'))) "(32.5, 40000, ('spam', 'eggs'))" 

如下是編寫正方形和立方體表的兩種方法:

>>>
>>> for x in range(1, 11): ... print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ') ... # Note use of 'end' on previous line ... print(repr(x*x*x).rjust(4)) ...  1 1 1  2 4 8  3 9 27  4 16 64  5 25 125  6 36 216  7 49 343  8 64 512  9 81 729 10 100 1000 >>> for x in range(1, 11): ... print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x)) ...  1 1 1  2 4 8  3 9 27  4 16 64  5 25 125  6 36 216  7 49 343  8 64 512  9 81 729 10 100 1000 

(請注意,在第一個示例中,每一個列之間的一個空格是按照print()工做方式添加的:默認狀況下,它在參數之間添加空格。)

此示例演示了str.rjust()字符串對象方法,方法經過在左側填充空格,在給定寬度的字段中對字符串進行右對齊。有相似的方法str.ljust()和 str.center()這些方法不會寫任何東西,它們只返回一個新字符串。若是輸入字符串太長,它們不會截斷它,但會保持不變; 這會弄亂你的列布局,但這一般比替代方案更好,這多是一個值。(若是你真的想要截斷,你能夠隨時添加切片操做,如 x.ljust(n)[:n]。)

還有另外一種方法,str.zfill()用零填充左側的數字字符串。它瞭解正負號:

>>>
>>> '12'.zfill(5) '00012' >>> '-3.14'.zfill(7) '-003.14' >>> '3.14159265359'.zfill(5) '3.14159265359' 

str.format()方法的基本用法以下所示:

>>>
>>> print('We are the {} who say "{}!"'.format('knights', 'Ni')) We are the knights who say "Ni!" 

其中的括號和字符(稱爲格式字段)將替換爲傳遞給str.format()方法的對象括號中的數字可用於表示傳遞給str.format()方法的對象的位置 

>>>
>>> print('{0} and {1}'.format('spam', 'eggs')) spam and eggs >>> print('{1} and {0}'.format('spam', 'eggs')) eggs and spam 

若是在str.format()方法中使用關鍵字參數,則使用參數的名稱引用它們的值。

>>>
>>> print('This {food} is {adjective}.'.format( ... food='spam', adjective='absolutely horrible')) This spam is absolutely horrible. 

位置和關鍵字參數能夠任意組合:

>>>
>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',  other='Georg')) The story of Bill, Manfred, and Georg. 

'!a'(apply ascii()),'!s'(apply str())和'!r' (apply repr())可用於在格式化以前轉換值:

>>>
>>> contents = 'eels' >>> print('My hovercraft is full of {}.'.format(contents)) My hovercraft is full of eels. >>> print('My hovercraft is full of {!r}.'.format(contents)) My hovercraft is full of 'eels'. 

可選的':'格式說明符能夠跟隨字段名稱。這樣能夠更好地控制值的格式化方式。如下示例將Pi舍入到小數點後的三個位置。

>>>
>>> import math >>> print('The value of PI is approximately {0:.3f}.'.format(math.pi)) The value of PI is approximately 3.142. 

在該':'字段以後傳遞一個整數將致使該字段爲最小字符數。這對於使表很是有用頗有用。

>>>
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678} >>> for name, phone in table.items(): ... print('{0:10} ==> {1:10d}'.format(name, phone)) ... Jack ==> 4098 Dcab ==> 7678 Sjoerd ==> 4127 

若是你有一個很是長的格式字符串,你不想拆分,那麼若是你能夠引用變量來按名稱而不是按位置進行格式化將會很好。這能夠經過簡單地傳遞dict並使用方括號'[]'來訪問鍵來完成

>>>
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678} >>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; ' ... 'Dcab: {0[Dcab]:d}'.format(table)) Jack: 4098; Sjoerd: 4127; Dcab: 8637678 

這也能夠經過將表做爲關鍵字參數傳遞'**'表示法來完成。

>>>
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678} >>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table)) Jack: 4098; Sjoerd: 4127; Dcab: 8637678 

這與內置函數結合使用特別有用,內置函數 vars()返回包含全部局部變量的字典。

有關字符串格式的完整概述str.format(),請參閱 格式字符串語法

7.1.1 舊字符串格式

%運營商還能夠用於字符串格式化。它將左參數解釋爲很是相似於sprintf()要應用於右參數的格式字符串,並返回由此格式化操做產生的字符串。例如:

>>>
>>> import math >>> print('The value of PI is approximately %5.3f.' % math.pi) The value of PI is approximately 3.142. 

能夠在printf樣式的字符串格式部分中找到更多信息

7.2 讀寫文件

open()返回一個文件對象,最經常使用的有兩個參數:open(filename, mode)

>>>
>>> f = open('workfile', 'w') 

第一個參數是包含文件名的字符串。第二個參數是另外一個字符串,其中包含一些描述文件使用方式的字符。 模式能夠是'r'僅讀取文件時,'w' 僅寫入(將刪除同名的現有文件),並 'a'打開文件進行追加; 寫入文件的任何數據都會自動添加到最後。 'r+'打開文件進行讀寫。所述模式參數是可選的; 'r'將被假設,若是它被省略。

一般,文件以文本模式打開,這意味着您從文件讀取和寫入文件,這些文件以特定編碼進行編碼。若是未指定編碼,則默認值取決於平臺(請參閱參考資料 open())。'b'附加到模式後以二進制模式打開文件 :如今以bytes對象的形式讀取和寫入數據。此模式應用於全部不包含文本的文件。

在文本模式下,讀取時的默認設置是將平臺特定的行結尾(\n在Unix上,\r\n在Windows上)轉換爲just \n在文本模式下寫入時,默認設置是將事件的發生轉換\n爲特定於平臺的行結尾。這幕後的修改文件數據精細的文本文件,但會喜歡,在破壞了二進制數據 JPEGEXE文件。在讀取和寫入此類文件時要很是當心地使用二進制模式。

with在處理文件對象時,最好使用關鍵字。優勢是文件在套件完成後正確關閉,即便在某個時刻引起了異常。使用with也比寫至關於短得多tryfinally塊:

>>>
>>> with open('workfile') as f: ... read_data = f.read() >>> f.closed True 

若是您沒有使用該with關鍵字,那麼您應該調用 f.close()以關閉該文件並當即釋放它使用的任何系統資源。若是您沒有顯式關閉文件,Python的垃圾收集器最終將銷燬該對象併爲您關閉打開的文件,但該文件可能會保持打開狀態一段時間。另外一個風險是不一樣的Python實現將在不一樣的時間進行清理。

經過with語句或經過調用關閉文件對象後f.close(),嘗試使用該文件對象將自動失敗。

>>>
>>> f.close() >>> f.read() Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: I/O operation on closed file 

7.2.1 文件對象的方法

本節中的其他示例將假定f已建立一個名爲的文件對象 

要讀取文件的內容,請調用f.read(size),讀取一些數據並將其做爲字符串(在文本模式下)或字節對象(在二進制模式下)返回。 size是可選的數字參數。省略size或negative時,將讀取並返回文件的所有內容; 若是文件的大小是機器內存的兩倍,那麼這就是你的問題。不然,最多讀取並返回大小字節。若是已到達文件末尾,f.read()則返回空字符串('')。

>>>
>>> f.read() 'This is the entire file.\n' >>> f.read() '' 

f.readline()從文件中讀取一行; 換行符(\n)留在字符串的末尾,若是文件沒有以換行符結尾,則只在文件的最後一行省略。這使得返回值明確無誤; 若是f.readline()返回一個空字符串,則表示已到達文件末尾,而空行表示爲'\n'一個只包含一個換行符的字符串。

>>>
>>> f.readline() 'This is the first line of the file.\n' >>> f.readline() 'Second line of the file\n' >>> f.readline() '' 

要從文件中讀取行,能夠循環遍歷文件對象。這是內存高效,快速,並致使簡單的代碼:

>>>
>>> for line in f: ... print(line, end='') ... This is the first line of the file. Second line of the file 

若是要讀取列表中文件的全部行,也能夠使用 list(f)f.readlines()

f.write(string)string的內容寫入文件,返回寫入的字符數。

>>>
>>> f.write('This is a test\n') 15 

在編寫以前,須要將其餘類型的對象轉換爲字符串(在文本模式下)或字節對象(在二進制模式下):

>>>
>>> value = ('the answer', 42) >>> s = str(value) # convert the tuple to string >>> f.write(s) 18 

f.tell() 返回一個整數,給出文件對象在文件中的當前位置,表示爲二進制模式下文件開頭的字節數和文本模式下的不透明數字。

要更改文件對象的位置,請使用經過向參考點添加偏移計算位置; 參數點由from_what參數選擇from_what從文件的開頭0措施值,1使用當前文件位置,和2使用該文件做爲參考點的端部。 from_what能夠省略,默認爲0,使用文件的開頭做爲參考點。f.seek(offset, from_what)

>>>
>>> f = open('workfile', 'rb+') >>> f.write(b'0123456789abcdef') 16 >>> f.seek(5) # Go to the 6th byte in the file 5 >>> f.read(1) b'5' >>> f.seek(-3, 2) # Go to the 3rd byte before the end 13 >>> f.read(1) b'd' 

在文本文件中(那些b在模式字符串中沒有打開的文件),只容許相對於文件開頭的搜索(尋找到文件結尾的例外),惟一有效的偏移值是從,或者返回的那些,或者零。任何其餘偏移值都會產生未定義的行爲。seek(0,2)f.tell()

文件對象有一些額外的方法,如isatty()和 truncate()其中較不頻繁地使用的; 有關文件對象的完整指南,請參閱「庫參考」。

7.2.2 使用保存結構化數據json

能夠輕鬆地將字符串寫入文件並從文件中讀取。數字須要更多的努力,由於該read()方法只返回字符串,必須將其傳遞給相似的函數int(),它接受相似字符串'123' 並返回其數值123.當您想要保存更復雜的數據類型(如嵌套列表和字典,手工解析和序列化變得複雜。

Python容許您使用稱爲JSON(JavaScript Object Notation)的流行數據交換格式,而不是讓用戶不斷編寫和調試代碼以將複雜的數據類型保存到文件中調用的標準模塊json能夠採用Python數據層次結構,並將它們轉換爲字符串表示形式; 這個過程稱爲序列化從字符串表示中重建數據稱爲反序列化在序列化和反序列化之間,表示對象的字符串可能已存儲在文件或數據中,或經過網絡鏈接發送到某個遠程機器。

注意

 

JSON格式一般被現代應用程序用於容許數據交換。許多程序員已經熟悉它,這使其成爲互操做性的良好選擇。

若是您有一個對象x,則能夠使用一行簡單的代碼查看其JSON字符串表示:

>>>
>>> import json >>> json.dumps([1, 'simple', 'list']) '[1, "simple", "list"]' 

dumps()調用函數的另外一個變體dump(),只是將對象序列化爲文本文件所以,若是f是一個 文本文件,對象爲寫入打開,咱們能夠這樣作:

json.dump(x, f) 

要再次解碼對象,if f是一個已打開以供閱讀文本文件對象:

x = json.load(f) 

這種簡單的序列化技術能夠處理列表和字典,可是在JSON中序列化任意類實例須要額外的努力。json模塊的參考包含對此的解釋。

八.錯誤和異常

到目前爲止,錯誤消息尚未提到,可是若是你已經嘗試過這些例子,你可能已經看過了一些。存在(至少)兩種可區分的錯誤:語法錯誤異常

8.1 語法錯誤

語法錯誤(也稱爲解析錯誤)多是您在學習Python時最多見的抱怨:

>>>
>>> while True print('Hello world') File "<stdin>", line 1 while True print('Hello world') ^ SyntaxError: invalid syntax 

解析器重複出現違規行,並顯示一個指向檢測到錯誤的行中最先點的「箭頭」。該錯誤是由箭頭前面的標記引發的(或至少在其中檢測到的):在該示例中,在函數處檢測到錯誤print(),由於':'在它以前缺乏冒號()。打印文件名和行號,以便在輸入來自腳本時知道查找位置。

8.2 異常

即便語句或表達式在語法上是正確的,但在嘗試執行它時可能會致使錯誤。在執行期間檢測到的錯誤稱爲異常,而且不是無條件致命的:您將很快學會如何在Python程序中處理它們。可是,大多數異常都不是由程序處理的,並致使錯誤消息,以下所示:

>>>
>>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't convert 'int' object to str implicitly 

錯誤消息的最後一行代表發生了什麼。異常有不一樣的類型,類型做爲消息的一部分打印出來:示例中的類型是ZeroDivisionErrorNameErrorTypeError做爲異常類型打印的字符串是發生的內置異常的名稱。對於全部內置異常都是如此,但對於用戶定義的異常不必定是這樣(儘管它是一個有用的約定)。標準異常名稱是內置標識符(不是保留關鍵字)。

該行的其他部分根據異常類型及其緣由提供詳細信息。

錯誤消息的前一部分以堆棧回溯的形式顯示發生異常的上下文。一般它包含列出源代碼行的堆棧回溯; 可是,它不會顯示從標準輸入讀取的行。

內置異常列出了內置異常及其含義。

8.3 處理異常

能夠編寫處理所選異常的程序。請查看如下示例,該示例要求用戶輸入,直到輸入有效整數,但容許用戶中斷程序(使用Control-C或操做系統支持的任何內容); 請注意,經過引起KeyboardInterrupt異常來發出用戶生成的中斷信號

>>>
>>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ... 

try聲明的工做原理以下。

  • 首先,執行try子句try和 except關鍵字之間的語句)。
  • 若是沒有發生異常,則跳過except子句try完成語句的執行 
  • 若是在執行try子句期間發生異常,則跳過該子句的其他部分。而後,若是其類型匹配在except關鍵字後面命名的異常 ,則執行except子句,而後在try語句以後繼續執行
  • 若是發生的異常與except子句中指定的異常不匹配,則將其傳遞給外部try語句; 若是沒有找處處理程序,則它是一個未處理的異常,執行將中止並顯示如上所示的消息。

一個try語句可能有不止一個,除了子句,分別指定處理不一樣的異常。最多將執行一個處理程序。處理程序僅處理相應try子句中發生的異常,而不處理同一try語句的其餘處理程序中的異常except子句能夠將多個異常命名爲帶括號的元組,例如:

... except (RuntimeError, TypeError, NameError): ... pass 

若是except子句中的類與同一個類或其基類相同,則它與異常兼容(但不是相反 - 列出派生類的except子句與基類不兼容)。例如,如下代碼將按如下順序打印B,C,D:

class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B") 

請注意,若是except子句被顛倒(第一個),它將打印B,B,B - 觸發第一個匹配的except子句。except B

最後一個except子句能夠省略異常名稱,以用做通配符。請謹慎使用,由於以這種方式很容易掩蓋真正的編程錯誤!它還能夠用於打印錯誤消息,而後從新引起異常(容許調用者也處理異常):

import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise 

try... except語句有一個可選的else子句,其中,若是存在的話,必須遵循全部的條款除外。若是try子句不引起異常,則對於必須執行的代碼頗有用。例如:

for arg in sys.argv[1:]: try: f = open(arg, 'r') except OSError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close() 

使用該else子句比向該子句添加其餘代碼更好,try由於它避免意外捕獲由try... except語句保護的代碼未引起的異常

發生異常時,它可能具備關聯值,也稱爲異常參數參數的存在和類型取決於異常類型。

except子句能夠在異常名稱後面指定一個變量。該變量綁定到存儲參數的異常實例 instance.args爲方便起見,異常實例定義 __str__()了參數能夠直接打印而無需引用.args也能夠在提高以前首先實例化異常,並根據須要向其添加任何屬性。

>>>
>>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # the exception instance ... print(inst.args) # arguments stored in .args ... print(inst) # __str__ allows args to be printed directly, ... # but may be overridden in exception subclasses ... x, y = inst.args # unpack args ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs 

若是異常有參數,則它們將做爲未處理異常的消息的最後一部分(「詳細信息」)打印。

異常處理程序不只處理異常(若是它們當即出如今try子句中),並且還發生在try子句中調用(甚至間接)的函數內部。例如:

>>>
>>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as err: ... print('Handling run-time error:', err) ... Handling run-time error: division by zero 

8.4 提升異常

raise語句容許程序員強制發生指定的異常。例如:

>>>
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: HiThere 

惟一的參數raise表示要引起的異常。這必須是異常實例或異常類(派生自的類Exception)。若是傳遞了一個異常類,它將經過調用沒有參數的構造函數來隱式實例化:

raise ValueError # shorthand for 'raise ValueError()' 

若是您須要肯定是否引起了異常但不打算處理它,則更簡單的raise語句形式容許您從新引起異常:

>>>
>>> try: ... raise NameError('HiThere') ... except NameError: ... print('An exception flew by!') ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in <module> NameError: HiThere 

8.5 用戶定義的異常

程序能夠經過建立新的異常類來命名它們本身的異常(有關Python類的更多信息,請參閱 )。一般應Exception直接或間接地類中派生異常

能夠定義異常類,它能夠執行任何其餘類能夠執行的任何操做,但一般保持簡單,一般只提供許多屬性,這些屬性容許處理程序爲異常提取有關錯誤的信息。在建立可能引起多個不一樣錯誤的模塊時,一般的作法是爲該模塊定義的異常建立基類,併爲不一樣錯誤條件建立特定異常類的子類:

class Error(Exception): """Base class for exceptions in this module.""" pass class InputError(Error): """Exception raised for errors in the input.  Attributes:  expression -- input expression in which the error occurred  message -- explanation of the error  """ def __init__(self, expression, message): self.expression = expression self.message = message class TransitionError(Error): """Raised when an operation attempts a state transition that's not  allowed.  Attributes:  previous -- state at beginning of transition  next -- attempted new state  message -- explanation of why the specific transition is not allowed  """ def __init__(self, previous, next, message): self.previous = previous self.next = next self.message = message 

大多數異常都定義爲名稱以「錯誤」結尾,相似於標準異常的命名。

許多標準模塊定義了它們本身的異常,以報告它們定義的函數中可能出現的錯誤。有關類的更多信息,請參見類Classes

8.6 定義清理動做

try語句有另外一個可選子句,用於定義必須在全部狀況下執行的清理操做。例如:

>>>
>>> try: ... raise KeyboardInterrupt ... finally: ... print('Goodbye, world!') ... Goodbye, world! KeyboardInterrupt Traceback (most recent call last): File "<stdin>", line 2, in <module> 

一個finally子句在離開以前一直執行try 的語句,是否已經發生也不例外。try子句中發生異常且未被except子句處理 (或者它已經發生在except或 else子句中)時,在finally子句執行後從新引起它finally條款也被執行「的出路」的時候,任何其餘條款try的語句是經過左 breakcontinuereturn聲明。一個更復雜的例子:

>>>
>>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print("division by zero!") ... else: ... print("result is", result) ... finally: ... print("executing finally clause") ... >>> divide(2, 1) result is 2.0 executing finally clause >>> divide(2, 0) division by zero! executing finally clause >>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str' 

如您所見,該finally子句在任何狀況下都會執行。TypeError經過劃分兩個字符串而 引起的內容不禁該except子句處理 ,所以在該finally 子句執行後從新引起

在實際應用程序中,該finally子句對於釋放外部資源(例如文件或網絡鏈接)很是有用,不管資源的使用是否成功。

8.7 預約義的清理操做

某些對象定義了在再也不須要該對象時要執行的標準清理操做,不管使用該對象的操做是成功仍是失敗。請查看如下示例,該示例嘗試打開文件並將其內容打印到屏幕上。

for line in open("myfile.txt"): print(line, end="") 

此代碼的問題在於,在部分代碼執行完畢後,它會使文件保持打開一段不肯定的時間。這在簡單腳本中不是問題,但對於較大的應用程序多是一個問題。with語句容許以一種方式使用文件等對象,以確保始終及時正確地清理它們。

with open("myfile.txt") as f: for line in f: print(line, end="") 

執行語句後,即便在處理行時遇到問題,文件f也始終關閉。與文件同樣,提供預約義清理操做的對象將在其文檔中指出這一點。

九.類

類提供了將數據和功能捆綁在一塊兒的方法。建立新類會建立一種新類型的對象,從而容許建立該類型的新實例每一個類實例均可以附加屬性以維護其狀態。類實例還能夠具備用於修改其狀態的方法(由其類定義)。

與其餘編程語言相比,Python的類機制添加了具備最少新語法和語義的類。它是C ++和Modula-3中的類機制的混合體。Python類提供面向對象編程的全部標準功能:類繼承機制容許多個基類,派生類能夠覆蓋其基類或類的任何方法,而且方法能夠調用具備相同名稱的基類的方法。對象能夠包含任意數量和種類的數據。與模塊同樣,類依賴於Python的動態特性:它們是在運行時建立的,而且能夠在建立後進一步修改。

在C ++術語中,一般類成員(包括數據成員)是 公共的(除了參見下面的私有變量),而且全部成員函數都是 虛擬的與在Modula-3中同樣,沒有用於從其方法引用對象成員的簡寫:方法函數使用表示對象的顯式第一個參數聲明,該參數由調用隱式提供。與Smalltalk同樣,類自己也是對象。這爲導入和重命名提供了語義。與C ++和Modula-3不一樣,內置類型能夠用做用戶擴展的基類。此外,與C ++同樣,大多數具備特殊語法(算術運算符,下標等)的內置運算符均可以從新定義爲類實例。

(因爲缺少廣泛接受的術語來討論類,我偶爾會使用Smalltalk和C ++術語。我會使用Modula-3術語,由於它的面向對象語義比C ++更接近Python,但我但願讀者不多據說過。)

9.1 一個關於名稱和對象的詞

對象具備個性,多個名稱(在多個範圍內)能夠綁定到同一個對象。這在其餘語言中稱爲別名。乍一看Python時一般不會理解這一點,在處理不可變的基本類型(數字,字符串,元組)時能夠安全地忽略它。可是,別名對涉及可變對象(如列表,字典和大多數其餘類型)的Python代碼的語義可能會產生驚人的影響。這一般用於程序的好處,由於別名在某些方面表現得像指針。例如,傳遞一個對象很便宜,由於實現只傳遞一個指針; 若是函數修改了做爲參數傳遞的對象,調用者將看到更改 - 這消除了對Pascal中兩個不一樣的參數傳遞機制的須要。

9.2 Python範圍和命名空間

在介紹類以前,我首先要告訴你一些Python的範圍規則。類定義對命名空間有一些巧妙的技巧,你須要知道範圍和命名空間如何工做才能徹底理解正在發生的事情。順便說一下,關於這個主題的知識對任何高級Python程序員都頗有用。

讓咱們從一些定義開始。

一個命名空間是從名字到對象的映射。大多數名稱空間當前都是做爲Python詞典實現的,但一般不會以任何方式顯示(性能除外),而且它可能在未來發生變化。命名空間的示例是:內置名稱集(包含諸如abs()和內置異常名稱之類的函數); 模塊中的全局名稱; 和函數調用中的本地名稱。在某種意義上,對象的屬性集也造成了命名空間。關於命名空間的重要一點是,不一樣命名空間中的名稱之間絕對沒有關係; 例如,兩個不一樣的模塊均可以定義函數maximize而不會產生混淆 - 模塊的用戶必須在其前面加上模塊名稱。

順便說一句,我對word 後面的任何名稱使用word 屬性 - 例如,在表達式中z.realreal是對象的屬性 z嚴格地說,對模塊中名稱的引用是屬性引用:在表達式中modname.funcnamemodname是模塊對象而且funcname是它的屬性。在這種狀況下,模塊的屬性和模塊中定義的全局名稱之間剛好有一個直接的映射:它們共享相同的命名空間![1]

屬性能夠是隻讀的或可寫的。在後一種狀況下,能夠分配屬性。模塊屬性是可寫的:你能夠寫 可寫屬性也能夠使用語句刪除 例如,將從名爲的對象中刪除該屬性modname.the_answer 42deldelmodname.the_answerthe_answermodname

命名空間在不一樣時刻建立,具備不一樣的生命週期。包含內置名稱的命名空間是在Python解釋器啓動時建立的,而且永遠不會被刪除。讀入模塊定義時會建立模塊的全局命名空間; 一般,模塊名稱空間也會一直持續到解釋器退出。由解釋程序的頂級調用執行的語句(從腳本文件讀取或交互式讀取)被視爲調用模塊的一部分__main__,所以它們具備本身的全局命名空間。(內置名稱實際上也存在於模塊中;這稱爲builtins。)

函數的本地命名空間在調用函數時建立,並在函數返回或引起函數內未處理的異常時刪除。(實際上,遺忘是描述實際狀況的更好方法。)固然,遞歸調用每一個都有本身的本地命名空間。

範圍是Python程序,其中一個命名空間是可直接訪問的文本區域。這裏的「可直接訪問」意味着對名稱的非限定引用會嘗試在命名空間中查找名稱。

儘管範圍是靜態肯定的,但它們是動態使用的。在執行期間的任什麼時候候,至少有三個嵌套的做用域,其名稱空間能夠直接訪問:

  • 最早搜索的最內部範圍包含本地名稱
  • 從最近的封閉範圍開始搜索的任何封閉函數的範圍包含非本地名稱,也包括非全局名稱
  • 倒數第二個範圍包含當前模塊的全局名稱
  • 最外面的範圍(最後搜索)是包含內置名稱的命名空間

若是名稱聲明爲全局,則全部引用和賦值將直接轉到包含模塊全局名稱的中間做用域。要從新綁定在最內層範圍以外找到的變量,nonlocal能夠使用語句; 若是沒有聲明爲非本地變量,那些變量是隻讀的(嘗試寫入這樣的變量只會在最裏面的範圍內建立一個新的局部變量,保持同名的外部變量不變)。

一般,本地範圍引用(文本)當前函數的本地名稱。在外部函數中,本地做用域引用與全局做用域相同的名稱空間:模塊的名稱空間。類定義在本地範圍中放置另外一個命名空間。

重要的是要意識到範圍是以文本方式肯定的:模塊中定義的函數的全局範圍是模塊的命名空間,不管從何處或經過調用函數的別名。另外一方面,名稱的實際搜索是在運行時動態完成的 - 可是,語言定義在「編譯」時朝着靜態名稱解析發展,因此不要依賴於動態名稱解析!(事實上​​,局部變量已經靜態肯定。)

Python的一個特殊之處在於 - 若是沒有global語句生效 - 對名稱的賦值老是進入最內層範圍。分配不復制數據 - 它們只是將名稱綁定到對象。刪除也是如此:該語句刪除了本地範圍引用的命名空間的綁定實際上,引入新名稱的全部操做都使用本地範圍:特別是,語句和函數定義綁定本地範圍中的模塊或函數名稱。del xximport

global陳述可用於代表特定變量存在於全球範圍內,並應在那裏反彈; 該 nonlocal陳述代表特定變量存在於封閉範圍內,應該在那裏反彈。

9.2.1 範圍和命名空間示例

這是一個例子展現瞭如何引用不一樣的範圍和命名空間,以及如何globalnonlocal影響可變結合:

def scope_test(): def do_local(): spam = "local spam" def do_nonlocal(): nonlocal spam spam = "nonlocal spam" def do_global(): global spam spam = "global spam" spam = "test spam" do_local() print("After local assignment:", spam) do_nonlocal() print("After nonlocal assignment:", spam) do_global() print("After global assignment:", spam) scope_test() print("In global scope:", spam) 

示例代碼的輸出是:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

請注意本地分配(默認)如何不改變scope_test垃圾郵件的綁定nonlocal分配改變了scope_test垃圾郵件的綁定,而且該global分配更改了模塊級綁定。

您還能夠分配以前 看到以前沒有垃圾郵件綁定global

9.3 初看類

類引入了一些新語法,三種新對象類型和一些新語義。

9.3.1 類定義語法

最簡單的類定義形式以下所示:

class ClassName: <statement-1> . . . <statement-N> 

類定義,如函數定義(def語句)必須在它們產生任何影響以前執行。(您能夠想象將類定義放在if語句的分支中,或者放在函數內部。)

實際上,類定義中的語句一般是函數定義,但其餘語句是容許的,有時也頗有用 - 咱們稍後會再回過頭來討論它。類中的函數定義一般具備一種特殊形式的參數列表,由方法的調用約定決定 - 再次,這將在後面解釋。

輸入類定義時,會建立一個新的命名空間,並將其用做本地範圍 - 所以,對局部變量的全部賦值都將進入此新命名空間。特別是,函數定義在此處綁定新函數的名稱。

正常保留類定義(經過結尾)時,會建立一個類對象這基本上是類定義建立的命名空間內容的包裝器; 咱們將在下一節中瞭解有關類對象的更多信息。恢復原始本地範圍(在輸入類定義以前生效的範圍),而且類對象在此處綁定到類定義標頭(ClassName在示例中)中給出的類名

9.3.2 類對象

類對象支持兩種操做:屬性引用和實例化。

屬性引用使用Python中全部屬性引用使用的標準語法:obj.name有效的屬性名稱是建立類對象時類的命名空間中的全部名稱。因此,若是類定義看起來像這樣:

class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world' 

而後MyClass.iMyClass.f是有效的屬性的引用,分別返回一個整數和一個功能對象。也能夠爲類屬性分配,所以您能夠MyClass.i經過賦值更改值。 __doc__也是一個有效的屬性,返回屬於該類的docstring : ."A simple exampleclass"

實例化使用函數表示法。只是僞裝類對象是一個無參數函數,它返回一個新的類實例。例如(假設上述類):

x = MyClass() 

建立類的新實例,並將此對象分配給局部變量x

實例化操做(「調用」類對象)建立一個空對象。許多類喜歡建立具備針對特定初始狀態定製的實例的對象。所以,類能夠定義一個名爲的特殊方法 __init__(),以下所示:

def __init__(self): self.data = [] 

當類定義__init__()方法時,類實例化會自動調用__init__()新建立的類實例。所以,在此示例中,能夠經過如下方式獲取新的初始化實例:

x = MyClass() 

固然,該__init__()方法可能具備更大靈活性的論據。在這種狀況下,將給予類實例化運算符的參數傳遞給__init__()例如,

>>>
>>> class Complex: ... def __init__(self, realpart, imagpart): ... self.r = realpart ... self.i = imagpart ... >>> x = Complex(3.0, -4.5) >>> x.r, x.i (3.0, -4.5) 

9.3.3 實例對象

如今咱們能夠用實例對象作什麼?實例對象理解的惟一操做是屬性引用。有兩種有效的屬性名稱,數據屬性和方法。

數據屬性對應於Smalltalk中的「實例變量」,以及C ++中的「數據成員」。不須要聲明數據屬性; 就像局部變量同樣,它們在第一次被分配時就會存在。例如,若是 xMyClass上面建立的實例,則如下代碼段將打印該值16,而不留下跟蹤:

x.counter = 1 while x.counter < 10: x.counter = x.counter * 2 print(x.counter) del x.counter 

另外一種實例屬性引用是一種方法方法是「屬於」對象的函數。(在Python中,術語方法不是類實例惟一的:其餘對象類型也能夠有方法。例如,列表對象具備名爲append,insert,remove,sort等的方法。可是,在下面的討論中,除非另有明確說明,不然咱們將專門使用術語方法來表示類實例對象的方法。)

實例對象的有效方法名稱取決於其類。根據定義,做爲函數對象的類的全部屬性都定義其實例的相應方法。因此在咱們的例子中,x.f是一個有效的方法引用,由於它MyClass.f是一個函數,但x.i不是,由於 MyClass.i它不是。x.f它不是同一個東西MyClass.f- 它是一個方法對象,而不是一個函數對象。

9.3.4 方法對象

一般,綁定後當即調用方法:

x.f() 

MyClass示例中,這將返回字符串可是,沒有必要當即調用方法:是一個方法對象,能夠存儲起來並在之後調用。例如:'hello world'x.f

xf = x.f while True: print(xf()) 

將繼續打印,直到時間結束。hello world

調用方法時到底發生了什麼?您可能已經注意到, x.f()上面沒有參數調用,即便f()指定參數的函數定義這個論點怎麼了?當一個須要參數的函數被調用而沒有任何東西時,Python確定會引起異常 - 即便參數實際上沒有被使用......

實際上,您可能已經猜到了答案:方法的特殊之處在於實例對象做爲函數的第一個參數傳遞。在咱們的示例中,調用x.f()徹底等同於MyClass.f(x)一般,使用n個參數列表調用方法等效於使用經過在第一個參數以前插入方法的實例對象而建立的參數列表來調用相應的函數。

若是您仍然不瞭解方法的工做原理,那麼查看實現可能會澄清問題。當引用不是數據屬性的實例屬性時,將搜索其類。若是名稱表示做爲函數對象的有效類屬性,則經過打包(指向)實例對象和剛在抽象對象中找到的函數對象來建立方法對象:這是方法對象。當使用參數列表調用方法對象時,將從實例對象和參數列表構造新的參數列表,並使用此新參數列表調用函數對象。

9.3.5 類和實例變量

通常來講,實例變量是針對每一個實例惟一的數據,而類變量是針對類的全部實例共享的屬性和方法:

class Dog: kind = 'canine' # class variable shared by all instances def __init__(self, name): self.name = name # instance variable unique to each instance >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.kind # shared by all dogs 'canine' >>> e.kind # shared by all dogs 'canine' >>> d.name # unique to d 'Fido' >>> e.name # unique to e 'Buddy' 

正如在關於名稱和對象的單詞中所討論的,共享數據可能具備使人驚訝的效果,涉及可變對象,例如列表和字典。例如,如下代碼中技巧列表不該該用做類變量,由於全部Dog 實例只共享一個列表

class Dog: tricks = [] # mistaken use of a class variable def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks # unexpectedly shared by all dogs ['roll over', 'play dead'] 

正確設計類應該使用實例變量:

class Dog: def __init__(self, name): self.name = name self.tricks = [] # creates a new empty list for each dog def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks ['roll over'] >>> e.tricks ['play dead'] 

9.4 隨機備註

數據屬性覆蓋具備相同名稱的方法屬性; 爲了不可能致使大型程序中難以發現的錯誤的意外名稱衝突,使用某種最小化衝突機會的約定是明智的。可能的約定包括大寫方法名稱,使用小的惟一字符串(可能只是下劃線)爲數據屬性名稱加前綴,或者使用動詞做爲數據屬性的方法和名詞。

數據屬性能夠由方法以及對象的普通用戶(「客戶端」)引用。換句話說,類不可用於實現純抽象數據類型。事實上,Python中沒有任何東西能夠強制執行數據隱藏 - 它徹底基於約定。(另外一方面,用C語言編寫的Python實現能夠徹底隱藏實現細節並在必要時控制對對象的訪問;這能夠由用C編寫的Python的擴展來使用。)

客戶端應謹慎使用數據屬性 - 客戶端可能會經過標記其數據屬性來破壞方法維護的不變量。請注意,客戶端能夠將本身的數據屬性添加到實例對象,而不會影響方法的有效性,只要避免名稱衝突 - 一樣,命名約定能夠在這裏節省不少麻煩。

從方法中引用數據屬性(或其餘方法!)沒有簡寫。我發現這實際上增長了方法的可讀性:當瀏覽方法時,沒有可能混淆局部變量和實例變量。

一般,調用方法的第一個參數self這只不過是一個慣例:這個名字self對Python來講絕對沒有特殊意義。但請注意,若是不遵循慣例,您的代碼可能對其餘Python程序員來講可讀性較低,而且能夠想象可能會編寫依賴於此類約定的 類瀏覽器程序。

做爲類屬性的任何函數對象都爲該類的實例定義了一個方法。函數定義沒必要在文本中包含在類定義中:將函數對象賦值給類中的局部變量也是能夠的。例如:

# Function defined outside the class
def f1(self, x, y): return min(x, x+y) class C: f = f1 def g(self): return 'hello world' h = g 

如今fg而且h是類的全部屬性都C引用了函數對象,所以它們都是實例的方法 Ch徹底等同於g請注意,這種作法一般只會使程序的讀者感到困惑。

方法能夠經過使用self 參數的方法屬性來調用其餘方法

class Bag: def __init__(self): self.data = [] def add(self, x): self.data.append(x) def addtwice(self, x): self.add(x) self.add(x) 

方法能夠以與普通函數相同的方式引用全局名稱。與方法關聯的全局範圍是包含其定義的模塊。(一個類永遠不會被用做全局範圍。)雖然不多遇到在方法中使用全局數據的充分理由,可是全局範圍有許多合法用途:一方面,導入全局範圍的函數和模塊能夠由方法,以及在其中定義的函數和類使用。一般,包含該方法的類自己在此全局範圍內定義,在下一節中,咱們將找到一些方法想要引用其本身的類的一些很好的理由。

每一個值都是一個對象,所以有一個(也稱爲其類型)。它存儲爲object.__class__

9.5 繼承

固然,若是不支持繼承,語言特性就不值得稱爲「類」。派生類定義的語法以下所示:

class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N> 

名稱BaseClassName必須在含有派生類定義中的範圍來限定。代替基類名稱,也容許使用其餘任意表達式。這可能頗有用,例如,在另外一個模塊中定義基類時:

class DerivedClassName(modname.BaseClassName): 

派生類定義的執行與基類的執行相同。構造類對象時,會記住基類。這用於解析屬性引用:若是在類中找不到請求的屬性,則搜索繼續查找基類。若是基類自己是從其餘類派生的,則遞歸應用此規則。

關於派生類的實例化沒有什麼特別之處: DerivedClassName()建立一個新的類實例。方法引用按以下方式解析:搜索相應的類屬性,若是須要,沿着基類鏈向下移動,若是生成函數對象,則方法引用有效。

派生類能夠覆蓋其基類的方法。由於方法在調用同一對象的其餘方法時沒有特殊權限,因此調用同一基類中定義的另外一個方法的基類方法最終可能會調用覆蓋它的派生類的方法。(對於C ++程序員:Python中的全部方法都是有效的virtual。)

派生類中的重寫方法實際上可能但願擴展而不是簡單地替換同名的基類方法。有一種直接調用基類方法的簡單方法:只需調用便可這對客戶來講偶爾也頗有用。(請注意,這僅在基類能夠在全局範圍內訪問時纔有效。)BaseClassName.methodname(self, arguments)BaseClassName

Python有兩個內置函數能夠繼承:

  • 使用isinstance()檢查實例的類型: 會,若是僅或來源於一些類isinstance(obj,int)Trueobj.__class__intint
  • 使用issubclass()檢查類繼承: 是,由於是的子類然而, 就是由於不是一個子類issubclass(bool,int)Trueboolintissubclass(float, int)Falsefloatint

9.5.1 多重繼承

Python也支持多重繼承的形式。具備多個基類的類定義以下所示:

class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N> 

在大多數狀況下,在最簡單的狀況下,您能夠將從父類繼承的屬性的搜索視爲深度優先,從左到右,而不是在層次結構中存在重疊的同一類中搜索兩次。所以,若是沒有找到屬性,則在DerivedClassName其中搜索Base1,而後(遞歸地)搜索它Base1,若是在那裏找不到,則搜索它Base2,依此類推。

事實上,它稍微複雜一些; 方法解析順序動態變化以支持協做調用super()這種方法在一些其餘多繼承語言中稱爲call-next-method,而且比單繼承語言中的超級調用更強大。

動態排序是必要的,由於全部多重繼承的狀況都表現出一個或多個菱形關係(其中至少有一個父類能夠經過最底層的多個路徑訪問)。例如,全部類都繼承自object,所以任何多重繼承的狀況都提供了多個到達的路徑object爲了防止基類被屢次訪問,動態算法將搜索順序線性化,以保留每一個類中指定的從左到右的順序,只調用每一個父類一次,這是單調的(意思是一個類能夠被子類化,而不會影響其父級的優先順序)。總之,這些屬性使得設計具備多重繼承的可靠且可擴展的類成爲可能。有關詳細信息,請參閱https://www.python.org/download/releases/2.3/mro/

9.6 私有變量

Python中不存在除對象內部以外沒法訪問的「私有」實例變量。可是,大多數Python代碼都遵循一個約定:如下劃線(例如_spam爲前綴的名稱應被視爲API的非公共部分(不管是函數,方法仍是數據成員)。它應被視爲實施細節,若有更改,恕不另行通知。

因爲類私有成員有一個有效的用例(即爲了不名稱與子類定義的名稱衝突),所以對這種稱爲名稱修改的機制的支持有限表單的任何標識符 __spam(至少兩個前導下劃線,最多一個尾隨下劃線)在文本上替換爲_classname__spam,其中classname當前類名稱帶有前導下劃線。只要它出如今類的定義中,就能夠在不考慮標識符的句法位置的狀況下完成這種修改。

名稱修改有助於讓子類覆蓋方法而不破壞類內方法調用。例如:

class Mapping: def __init__(self, iterable): self.items_list = [] self.__update(iterable) def update(self, iterable): for item in iterable: self.items_list.append(item) __update = update # private copy of original update() method class MappingSubclass(Mapping): def update(self, keys, values): # provides new signature for update() # but does not break __init__() for item in zip(keys, values): self.items_list.append(item) 

請注意,修剪規則主要是爲了不事故; 它仍然能夠訪問或修改被視爲私有的變量。這在特殊狀況下甚至可能頗有用,例如在調試器中。

請注意,代碼傳遞給exec()eval()不將調用類的類名視爲當前類; 這相似於global語句的效果,其效果一樣限於字節編譯在一塊兒的代碼。一樣的限制也適用於 getattr()setattr()delattr(),以及引用時爲 __dict__直接。

9.7 賠率和結束

有時,使用相似於Pascal「record」或C「struct」的數據類型是有用的,將一些命名數據項捆綁在一塊兒。一個空的類定義能夠很好地完成:

class Employee: pass john = Employee() # Create an empty employee record # Fill the fields of the record john.name = 'John Doe' john.dept = 'computer lab' john.salary = 1000 

須要特定抽象數據類型的Python代碼一般能夠傳遞一個模擬該數據類型方法的類。例如,若是您有一個從文件對象格式化某些數據的函數,則能夠使用方法定義一個類,read()而後readline()從字符串緩衝區獲取數據,並將其做爲參數傳遞。

實例方法對象也具備屬性:m.__self__是具備該方法的實例對象m(),而且m.__func__與該方法對應的函數對象。

9.8 迭代器

到目前爲止,您可能已經注意到大多數容器對象均可以使用for語句循環

for element in [1, 2, 3]: print(element) for element in (1, 2, 3): print(element) for key in {'one':1, 'two':2}: print(key) for char in "123": print(char) for line in open("myfile.txt"): print(line, end='') 

這種訪問方式清晰,簡潔,方便。迭代器的使用遍佈並統一了Python。在幕後,for語句調用iter()容器對象。該函數返回一個迭代器對象,該對象定義__next__()一次訪問容器中元素的方法當沒有更多元素時, __next__()引起一個StopIteration異常,告訴 for循環終止。您能夠__next__()使用next()內置函數調用該方法這個例子說明了它是如何工做的:

>>>
>>> s = 'abc' >>> it = iter(s) >>> it <iterator object at 0x00A1DB50> >>> next(it) 'a' >>> next(it) 'b' >>> next(it) 'c' >>> next(it) Traceback (most recent call last): File "<stdin>", line 1, in <module> next(it) StopIteration 

看過迭代器協議背後的機制,很容易將迭代器行爲添加到類中。定義一個使用__iter__()方法返回對象的__next__()方法。若是類定義了__next__(),那麼__iter__()能夠返回self

class Reverse: """Iterator for looping over a sequence backwards.""" def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index] 
>>>
>>> rev = Reverse('spam') >>> iter(rev) <__main__.Reverse object at 0x00A1DB50> >>> for char in rev: ... print(char) ... m a p s 

9.9 發電機

Generator是一個用於建立迭代器的簡單而強大的工具。它們像常規函數同樣編寫,但yield只要他們想要返回數據就使用該語句。每次next()調用它時,生成器從它中止的地方恢復(它記住全部數據值和上次執行的語句)。一個例子代表,生成器很容易建立:

def reverse(data): for index in range(len(data)-1, -1, -1): yield data[index] 
>>>
>>> for char in reverse('golf'): ... print(char) ... f l o g 

任何能夠使用生成器完成的操做也能夠使用基於類的迭代器完成,如上一節中所述。使發電機如此緊湊的緣由是__iter__()__next__()方法是自動建立的。

另外一個關鍵特性是在調用之間自動保存局部變量和執行狀態。與使用self.index 和等實例變量的方法相比,這使得函數更容易編寫而且更加清晰self.data

除了自動建立方法和保存程序狀態外,當生成器終止時,它們會自動引起StopIteration結合使用這些功能,能夠輕鬆建立迭代器,而不須要編寫常規函數。

9.10 生成器表達式

一些簡單的生成器能夠使用相似於列表推導的語法簡潔地編碼爲表達式,但使用括號而不是方括號。這些表達式適用於經過封閉函數當即使用生成器的狀況。生成器表達式比完整的生成器定義更緊湊但功能更少,而且每每比等效列表推導更具內存友好性。

例子:

>>>
>>> sum(i*i for i in range(10)) # sum of squares 285 >>> xvec = [10, 20, 30] >>> yvec = [7, 5, 3] >>> sum(x*y for x,y in zip(xvec, yvec)) # dot product 260 >>> from math import pi, sin >>> sine_table = {x: sin(x*pi/180) for x in range(0, 91)} >>> unique_words = set(word for line in page for word in line.split()) >>> valedictorian = max((student.gpa, student.name) for student in graduates) >>> data = 'golf' >>> list(data[i] for i in range(len(data)-1, -1, -1)) ['f', 'l', 'o', 'g']

十.標準庫簡介

10.1 操做系統接口

os模塊提供了許多與操做系統交互的功能:

>>>
>>> import os >>> os.getcwd() # Return the current working directory 'C:\\Python36' >>> os.chdir('/server/accesslogs') # Change current working directory >>> os.system('mkdir today') # Run the command mkdir in the system shell 0 

必定要使用樣式而不是這將不會影響內置函數,該函數的運行方式大不相同。import osfrom os import*os.open()open()

內置函數dir()help()函數可用做處理大型模塊的交互式輔助工具,例如os

>>>
>>> import os >>> dir(os) <returns a list of all module functions> >>> help(os) <returns an extensive manual page created from the module's docstrings> 

對於平常文件和目錄管理任務,該shutil模塊提供了更易於使用的更高級別的界面:

>>>
>>> import shutil >>> shutil.copyfile('data.db', 'archive.db') 'archive.db' >>> shutil.move('/build/executables', 'installdir') 'installdir' 

10.2 文件通配符

glob模塊提供了從目錄通配符搜索中建立文件列表的功能:

>>>
>>> import glob >>> glob.glob('*.py') ['primes.py', 'random.py', 'quote.py'] 

10.3 命令行參數

通用實用程序腳本一般須要處理命令行參數。這些參數做爲列表存儲在sys模塊的argv屬性中。例如,如下輸出來自在命令行運行python demo.py one two three

>>>
>>> import sys >>> print(sys.argv) ['demo.py', 'one', 'two', 'three'] 

getopt模塊使用Unix 函數的約定 處理sys.argvgetopt()argparse模塊提供了更強大和靈活的命令行處理

10.4 錯誤輸出重定向和程序終止

sys模塊還具備stdinstdoutstderr的屬性後者對於發出警告和錯誤消息很是有用,即便在重定向stdout時能夠看到它們

>>>
>>> sys.stderr.write('Warning, log file not found starting a new one\n') Warning, log file not found starting a new one 

終止腳本的最直接方法是使用sys.exit()

10.5 字符串模式匹配

re模塊爲高級字符串處理提供正則表達式工具。對於複雜的匹配和操做,正則表達式提供了簡潔,優化的解決方案:

>>>
>>> import re >>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest') ['foot', 'fell', 'fastest'] >>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat') 'cat in the hat' 

當只須要簡單的功能時,首選字符串方法,由於它們更易於閱讀和調試:

>>>
>>> 'tea for too'.replace('too', 'two') 'tea for two' 

10.6 數學

math模塊提供對浮點數學的底層C庫函數的訪問:

>>>
>>> import math >>> math.cos(math.pi / 4) 0.70710678118654757 >>> math.log(1024, 2) 10.0 

random模塊提供了進行隨機選擇的工具:

>>>
>>> import random >>> random.choice(['apple', 'pear', 'banana']) 'apple' >>> random.sample(range(100), 10) # sampling without replacement [30, 83, 16, 4, 8, 81, 41, 50, 18, 33] >>> random.random() # random float 0.17970987693706186 >>> random.randrange(6) # random integer chosen from range(6) 4 

statistics模塊計算數值數據的基本統計屬性(均值,中位數,方差等):

>>>
>>> import statistics >>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5] >>> statistics.mean(data) 1.6071428571428572 >>> statistics.median(data) 1.25 >>> statistics.variance(data) 1.3720238095238095 

SciPy項目< https://scipy.org >有許多其餘模塊用於數值計算。

10.7 上網

有許多模塊可用於訪問互聯網和處理互聯網協議。其中兩個最簡單的方法是urllib.request從URL檢索數據和smtplib發送郵件:

>>>
>>> from urllib.request import urlopen >>> with urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl') as response: ... for line in response: ... line = line.decode('utf-8') # Decoding the binary data to text. ... if 'EST' in line or 'EDT' in line: # look for Eastern Time ... print(line) <BR>Nov. 25, 09:43:32 PM EST >>> import smtplib >>> server = smtplib.SMTP('localhost') >>> server.sendmail('soothsayer@example.org', 'jcaesar@example.org', ... """To: jcaesar@example.org ... From: soothsayer@example.org ... ... Beware the Ides of March. ... """) >>> server.quit() 

(請注意,第二個示例須要在localhost上運行的郵件服務器。)

10.8 日期和時間

datetime模塊提供了以簡單和複雜的方式操做日期和時間的類。雖然支持日期和時間算法,但實現的重點是有效的成員提取以進行輸出格式化和操做。該模塊還支持時區感知的對象。

>>>
>>> # dates are easily constructed and formatted >>> from datetime import date >>> now = date.today() >>> now datetime.date(2003, 12, 2) >>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.") '12-02-03. 02 Dec 2003 is a Tuesday on the 02 day of December.' >>> # dates support calendar arithmetic >>> birthday = date(1964, 7, 31) >>> age = now - birthday >>> age.days 14368 

10.9 數據壓縮

:共同數據存檔和壓縮格式直接由模塊,包括支持zlibgzipbz2lzmazipfile和 tarfile

>>>
>>> import zlib >>> s = b'witch which has which witches wrist watch' >>> len(s) 41 >>> t = zlib.compress(s) >>> len(t) 37 >>> zlib.decompress(t) b'witch which has which witches wrist watch' >>> zlib.crc32(s) 226805979 

10.10 績效評估

一些Python用戶對了解同一問題的不一樣方法的相對性能產生了濃厚的興趣。Python提供了一種能夠當即回答這些問題的測量工具。

例如,使用元組打包和解包功能而不是傳統方法來交換參數可能很誘人。timeit 模塊快速展現了適度的性能優點:

>>>
>>> from timeit import Timer >>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit() 0.57535828626024577 >>> Timer('a,b = b,a', 'a=1; b=2').timeit() 0.54962537085770791 

timeit精細的粒度級別相比profile和 pstats模塊提供了用於在更大的代碼塊中識別時間關鍵部分的工具。

10.11 質量控制

開發高質量軟件的一種方法是在開發過程當中爲每一個函數編寫測試,並在開發過程當中常常運行這些測試。

doctest模塊提供了一個工具,用於掃描模塊並驗證程序文檔字符串中嵌入的測試。測試構造就像將典型調用及其結果剪切並粘貼到文檔字符串同樣簡單。這經過向用戶提供示例來改進文檔,而且它容許doctest模塊確保代碼保持對文檔的真實性:

def average(values): """Computes the arithmetic mean of a list of numbers.  >>> print(average([20, 30, 70]))  40.0  """ return sum(values) / len(values) import doctest doctest.testmod() # automatically validate the embedded tests 

unittest模塊不像模塊那樣輕鬆doctest,但它容許在單獨的文件中維護更全面的測試集:

import unittest class TestStatisticalFunctions(unittest.TestCase): def test_average(self): self.assertEqual(average([20, 30, 70]), 40.0) self.assertEqual(round(average([1, 5, 7]), 1), 4.3) with self.assertRaises(ZeroDivisionError): average([]) with self.assertRaises(TypeError): average(20, 30, 70) unittest.main() # Calling from the command line invokes all tests 

10.12 包含電池

Python具備「包含電池」的理念。經過其較大包裝的複雜和強大功能能夠最好地看到這一點。例如:

  • xmlrpc.clientxmlrpc.server模塊實現遠程過程調用了在瑣碎的任務。儘管有模塊名稱,但不須要直接瞭解或處理XML。
  • email軟件包是一個用於管理電子郵件的庫,包括MIME和其餘基於 RFC 2822的消息文檔。不一樣於smtplib和 poplib其實際上發送和接收消息,電子郵件封裝具備用於構建或複雜的消息結構(包括附件)進行解碼,並用於實現互聯網編碼和頭協議的完整工具集。
  • json軟件包爲解析這種流行的數據交換格式提供了強大的支持。csv模塊支持以逗號分隔值格式直接讀取和寫入文件,這些格式一般由數據庫和電子表格支持。XML處理是支持的xml.etree.ElementTreexml.dom和 xml.sax包。這些模塊和軟件包共同大大簡化了Python應用程序和其餘工具之間的數據交換。
  • sqlite3模塊是SQLite數據庫庫的包裝器,提供了一個持久性數據庫,能夠使用稍微非標準的SQL語法進行更新和訪問。
  • 國際化是由多個模塊,包括所支持 gettextlocale以及codecs包。

十一.標準庫簡介 - 第二部分

第二次巡演涵蓋了支持專業編程需求的更高級模塊。這些模塊不多出如今小腳本中。

11.1 輸出格式

reprlib模塊提供了一個repr()定製版本,用於縮放大型或深層嵌套容器的顯示:

>>>
>>> import reprlib >>> reprlib.repr(set('supercalifragilisticexpialidocious')) "{'a', 'c', 'd', 'e', 'f', 'g', ...}" 

pprint模塊提供了更加複雜的控制,能夠以解釋器可讀的方式打印內置和用戶定義的對象。當結果長於一行時,「漂亮的打印機」會添加換行符和縮進以更清楚地顯示數據結構:

>>>
>>> import pprint >>> t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta', ... 'yellow'], 'blue']]] ... >>> pprint.pprint(t, width=30) [[[['black', 'cyan'],  'white',  ['green', 'red']],  [['magenta', 'yellow'],  'blue']]] 

textwrap模塊格式化文本段落以適合給定的屏幕寬度:

>>>
>>> import textwrap >>> doc = """The wrap() method is just like fill() except that it returns ... a list of strings instead of one big string with newlines to separate ... the wrapped lines.""" ... >>> print(textwrap.fill(doc, width=40)) The wrap() method is just like fill() except that it returns a list of strings instead of one big string with newlines to separate the wrapped lines. 

locale模塊訪問文化特定數據格式的數據庫。locale格式函數的分組屬性提供了使用組分隔符格式化數字的直接方法:

>>>
>>> import locale >>> locale.setlocale(locale.LC_ALL, 'English_United States.1252') 'English_United States.1252' >>> conv = locale.localeconv() # get a mapping of conventions >>> x = 1234567.8 >>> locale.format("%d", x, grouping=True) '1,234,567' >>> locale.format_string("%s%.*f", (conv['currency_symbol'], ... conv['frac_digits'], x), grouping=True) '$1,234,567.80' 

11.2 模板

string模塊包括一個通用Template類,其語法簡化,適合最終用戶編輯。這容許用戶自定義他們的應用程序而無需更改應用程序。

該格式使用由$有效的Python標識符(字母數字字符和下劃線)組成的佔位符名稱使用大括號圍繞佔位符容許其後跟更多的字母數字字母,沒有中間空格。寫做$$會建立一個轉義$

>>>
>>> from string import Template >>> t = Template('${village}folk send $$10 to $cause.') >>> t.substitute(village='Nottingham', cause='the ditch fund') 'Nottinghamfolk send $10 to the ditch fund.' 

substitute()方法KeyError在字典或關鍵字參數中未提供佔位符時引起對於郵件合併樣式應用程序,用戶提供的數據可能不完整, safe_substitute()方法可能更合適 - 若是數據丟失,它將保持佔位符不變:

>>>
>>> t = Template('Return the $item to $owner.') >>> d = dict(item='unladen swallow') >>> t.substitute(d) Traceback (most recent call last): ... KeyError: 'owner' >>> t.safe_substitute(d) 'Return the unladen swallow to $owner.' 

模板子類能夠指定自定義分隔符。例如,照片瀏覽器的批量重命名實用程序能夠選擇對佔位符使用百分號,例如當前日期,圖像序列號或文件格式:

>>>
>>> import time, os.path >>> photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg'] >>> class BatchRename(Template): ... delimiter = '%' >>> fmt = input('Enter rename style (%d-date %n-seqnum %f-format): ') Enter rename style (%d-date %n-seqnum %f-format): Ashley_%n%f >>> t = BatchRename(fmt) >>> date = time.strftime('%d%b%y') >>> for i, filename in enumerate(photofiles): ... base, ext = os.path.splitext(filename) ... newname = t.substitute(d=date, n=i, f=ext) ... print('{0} --> {1}'.format(filename, newname)) img_1074.jpg --> Ashley_0.jpg img_1076.jpg --> Ashley_1.jpg img_1077.jpg --> Ashley_2.jpg 

模板的另外一個應用是將程序邏輯與多種輸出格式的細節分開。這樣就能夠替換XML文件,純文本報告和HTML Web報告的自定義模板。

11.3 使用二進制數據記錄佈局

struct模塊提供pack()和 unpack()使用可變長度二進制記錄格式的功能。如下示例顯示如何在不使用zipfile模塊的狀況下循環ZIP文件中的標頭信息 打包代碼"H"並分別"I"表示兩個和四個字節的無符號數字。"<"代表他們是標準的尺寸和little-endian字節順序:

import struct with open('myfile.zip', 'rb') as f: data = f.read() start = 0 for i in range(3): # show the first 3 file headers start += 14 fields = struct.unpack('<IIIHH', data[start:start+16]) crc32, comp_size, uncomp_size, filenamesize, extra_size = fields start += 16 filename = data[start:start+filenamesize] start += filenamesize extra = data[start:start+extra_size] print(filename, hex(crc32), comp_size, uncomp_size) start += extra_size + comp_size # skip to the next header 

11.4 多線程

線程化是一種解耦非順序依賴的任務的技術。線程可用於提升接受用戶輸入的應用程序的響應能力,而其餘任務在後臺運行。相關用例是與另外一個線程中的計算並行運行I / O.

如下代碼顯示了threading當主程序繼續運行時,高級模塊如何在後臺運行任務:

import threading, zipfile class AsyncZip(threading.Thread): def __init__(self, infile, outfile): threading.Thread.__init__(self) self.infile = infile self.outfile = outfile def run(self): f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED) f.write(self.infile) f.close() print('Finished background zip of:', self.infile) background = AsyncZip('mydata.txt', 'myarchive.zip') background.start() print('The main program continues to run in foreground.') background.join() # Wait for the background task to finish print('Main program waited until background was done.') 

多線程應用程序的主要挑戰是協調共享數據或其餘資源的線程。爲此,線程模塊提供了許多同步原語,包括鎖,事件,條件變量和信號量。

雖然這些工具功能強大,但較小的設計錯誤可能致使難以重現的問題。所以,任務協調的首選方法是將對資源的全部訪問集中在一個線程中,而後使用該 queue模塊爲來自其餘線程的請求提供該線程。使用Queue對象進行線程間通訊和協調的應用程序更易於設計,更易讀,更可靠。

11.5 記錄

logging模塊提供功能齊全且靈活的記錄系統。最簡單的是,日誌消息被髮送到文件或sys.stderr

import logging logging.debug('Debugging information') logging.info('Informational message') logging.warning('Warning:config file %s not found', 'server.conf') logging.error('Error occurred') logging.critical('Critical error -- shutting down') 

這會產生如下輸出:

WARNING:root:Warning:config file server.conf not found
ERROR:root:Error occurred
CRITICAL:root:Critical error -- shutting down

默認狀況下,信息和調試消息被抑制,輸出發送到標準錯誤。其餘輸出選項包括經過電子郵件,數據報,套接字或HTTP服務器路由消息。:新過濾器可根據消息優先級來選擇不一樣的路由DEBUG, INFOWARNINGERROR,和CRITICAL

日誌記錄系統能夠直接從Python配置,也能夠從用戶可編輯的配置文件加載,以便自定義日誌記錄而無需更改應用程序。

11.6 弱引用

Python執行自動內存管理(大多數對象的引用計數和 垃圾收集以消除循環)。在消除了對最後一次引用後不久釋放的內存。

這種方法適用於大多數應用程序,但偶爾須要跟蹤對象,只要它們被其餘東西使用。不幸的是,只是跟蹤它們會建立一個使它們永久化的引用。weakref模塊提供了跟蹤對象的工具,而無需建立引用。當再也不須要該對象時,它將自動從weakref表中刪除,併爲weakref對象觸發回調。典型應用程序包括緩存建立成本高昂的對象:

>>>
>>> import weakref, gc >>> class A: ... def __init__(self, value): ... self.value = value ... def __repr__(self): ... return str(self.value) ... >>> a = A(10) # create a reference >>> d = weakref.WeakValueDictionary() >>> d['primary'] = a # does not create a reference >>> d['primary'] # fetch the object if it is still alive 10 >>> del a # remove the one reference >>> gc.collect() # run garbage collection right away 0 >>> d['primary'] # entry was automatically removed Traceback (most recent call last): File "<stdin>", line 1, in <module> d['primary'] # entry was automatically removed File "C:/python36/lib/weakref.py", line 46, in __getitem__ o = self.data[key]() KeyError: 'primary' 

11.7 使用列表的工具

內置列表類型能夠知足許多數據結構需求。可是,有時須要具備不一樣性能權衡的替代實現。

array模塊提供的array()對象相似於僅存儲同類數據的列表,而且更緊湊地存儲它。如下示例顯示了存儲爲兩個字節無符號二進制數(typecode "H"的數字數組,而不是一般的Python int對象列表的每一個條目一般16個字節:

>>>
>>> from array import array >>> a = array('H', [4000, 10, 700, 22222]) >>> sum(a) 26932 >>> a[1:3] array('H', [10, 700]) 

collections模塊提供的deque()對象相似於列表,具備更快的附加和左側彈出,但在中間查找較慢。這些對象很是適合實現隊列和廣度優先樹搜索:

>>>
>>> from collections import deque >>> d = deque(["task1", "task2", "task3"]) >>> d.append("task4") >>> print("Handling", d.popleft()) Handling task1 
unsearched = deque([starting_node]) def breadth_first_search(unsearched): node = unsearched.popleft() for m in gen_moves(node): if is_goal(m): return m unsearched.append(m) 

除了替代列表實現以外,該庫還提供了其餘工具,例如bisect具備用於操做排序列表的函數模塊:

>>>
>>> import bisect >>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')] >>> bisect.insort(scores, (300, 'ruby')) >>> scores [(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')] 

heapq模塊提供了基於常規列表實現堆的功能。最低值的條目始終保持在零位置。這對於重複訪問最小元素但不想運行完整列表排序的應用程序很是有用:

>>>
>>> from heapq import heapify, heappop, heappush >>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] >>> heapify(data) # rearrange the list into heap order >>> heappush(data, -5) # add a new entry >>> [heappop(data) for i in range(3)] # fetch the three smallest entries [-5, 0, 1] 

11.8 十進制浮點運算

decimal模塊提供Decimal十進制浮點運算數據類型。float 二進制浮點的內置實現相比,該類特別有用

  • 財務申請和其餘須要精確十進制表示的用途,
  • 控制精度,
  • 控制四捨五入以知足法律或監管要求,
  • 跟蹤有效小數位,或
  • 用戶但願結果與手工完成的計算相匹配的應用程序。

例如,計算70美分手機費用的5%稅,會產生十進制浮點和二進制浮點數的不一樣結果。若是結果四捨五入到最接近的分數,則差別變得很大:

>>>
>>> from decimal import * >>> round(Decimal('0.70') * Decimal('1.05'), 2) Decimal('0.74') >>> round(.70 * 1.05, 2) 0.73 

Decimal結果保留尾隨零,自動推斷來自兩個地方的意義被乘數4點地方的意義。Decimal能夠手動複製數學,避免了二進制浮點不能精確表示十進制數時可能出現的問題。

精確表示使Decimal類可以執行不適合二進制浮點的模數計算和相等性測試:

>>>
>>> Decimal('1.00') % Decimal('.10') Decimal('0.00') >>> 1.00 % 0.10 0.09999999999999995 >>> sum([Decimal('0.1')]*10) == Decimal('1.0') True >>> sum([0.1]*10) == 1.0 False 

decimal模塊提供了所需的精度算術:

>>>
>>> getcontext().prec = 36 >>> Decimal(1) / Decimal(7) Decimal('0.142857142857142857142857142857142857')

十二.虛擬環境和包

12.1 介紹

Python應用程序一般會使用不做爲標準庫一部分的包和模塊。應用程序有時須要特定版本的庫,由於應用程序可能須要修復特定的錯誤,或者能夠使用庫的接口的過期版本編寫應用程序。

這意味着一個Python安裝可能沒法知足每一個應用程序的要求。若是應用程序A須要特定模塊的1.0版本但應用程序B須要2.0版本,則需求存在衝突,安裝版本1.0或2.0將致使一個應用程序沒法運行。

此問題的解決方案是建立一個虛擬環境,一個包含特定Python版本的Python安裝的自包含目錄樹,以及許多其餘軟件包。

而後,不一樣的應用能夠使用不一樣的虛擬環 要解決先前的衝突需求示例,應用程序A能夠擁有本身的1.0版本安裝虛擬環境,而應用程序B則具備版本2.0的另外一個虛擬環境。若是應用程序B要求將庫升級到3.0版,則不會影響應用程序A的環境。

12.2 建立虛擬環境

用於建立和管理虛擬環境的模塊被調用 venv。 venv一般會安裝您可用的最新版本的Python。若是您的系統上有多個版本的Python,則能夠經過運行python3或任何您想要的版原本選擇特定的Python版本

要建立虛擬環境,請肯定要放置它的目錄,而後將該venv模塊做爲帶有目錄路徑的腳本運行

python3 -m venv tutorial-env 

tutorial-env若是目錄不存在,這將建立目錄,並在其中建立包含Python解釋器,標準庫和各類支持文件的副本的目錄。

建立虛擬環境後,您能夠激活它。

在Windows上,運行:

tutorial-env\Scripts\activate.bat 

在Unix或MacOS上,運行:

source tutorial-env/bin/activate 

(這個腳本是爲bash shell編寫的。若是你使用 cshfish shell,你應該使用替代 activate.cshactivate.fish腳本。)

激活虛擬環境將改變shell的提示,以顯示您正在使用的虛擬環境,並修改環境,以便運行 python將得到特定版本和Python的安裝。例如:

$ source ~/envs/tutorial-env/bin/activate
(tutorial-env) $ python Python 3.5.1 (default, May 6 2016, 10:59:36) ... >>> import sys >>> sys.path ['', '/usr/local/lib/python35.zip', ..., '~/envs/tutorial-env/lib/python3.5/site-packages'] >>> 

12.3 使用pip管理包

您能夠使用名爲pip的程序安裝,升級和刪除軟件包 默認狀況下,pip將從Python包索引< https://pypi.org > 安裝包您能夠在Web瀏覽器中瀏覽Python Package Index,也能夠使用pip有限的搜索功能:

(tutorial-env) $ pip search astronomy skyfield - Elegant astronomy for Python gary - Galactic astronomy and gravitational dynamics. novas - The United States Naval Observatory NOVAS astronomy library astroobs - Provides astronomy ephemeris to plan telescope observations PyAstronomy - A collection of astronomy related tools for Python. ... 

pip有許多子命令:「搜索」,「安裝」,「卸載」,「凍結」等。(有關完整文檔,請參閱安裝Python模塊指南pip。)

您能夠經過指定包的名稱來安裝最新版本的包:

(tutorial-env) $ pip install novas Collecting novas Downloading novas-3.1.1.3.tar.gz (136kB) Installing collected packages: novas Running setup.py install for novas Successfully installed novas-3.1.1.3 

您還能夠經過提供包名稱後跟==以及版本號來安裝特定版本的包

(tutorial-env) $ pip install requests==2.6.0 Collecting requests==2.6.0 Using cached requests-2.6.0-py2.py3-none-any.whl Installing collected packages: requests Successfully installed requests-2.6.0 

若是從新運行此命令,pip將注意到已安裝所請求的版本而且不執行任何操做。您能夠提供不一樣的版本號來獲取該版本,也能夠運行以將軟件包升級到最新版本:pip install --upgrade

(tutorial-env) $ pip install --upgrade requests Collecting requests Installing collected packages: requests Found existing installation: requests 2.6.0 Uninstalling requests-2.6.0: Successfully uninstalled requests-2.6.0 Successfully installed requests-2.7.0 

pip uninstall 後跟一個或多個包名稱將從虛擬環境中刪除包。

pip show 將顯示有關特定包的信息:

(tutorial-env) $ pip show requests --- Metadata-Version: 2.0 Name: requests Version: 2.7.0 Summary: Python HTTP for Humans. Home-page: http://python-requests.org Author: Kenneth Reitz Author-email: me@kennethreitz.com License: Apache 2.0 Location: /Users/akuchling/envs/tutorial-env/lib/python3.4/site-packages Requires: 

pip list 將顯示虛擬環境中安裝的全部軟件包:

(tutorial-env) $ pip list novas (3.1.1.3) numpy (1.9.2) pip (7.0.3) requests (2.7.0) setuptools (16.0) 

pip freeze將生成相似的已安裝包列表,但輸出使用指望的格式一個常見的約定是將此列表放在一個文件中:pip installrequirements.txt

(tutorial-env) $ pip freeze > requirements.txt (tutorial-env) $ cat requirements.txt novas==3.1.1.3 numpy==1.9.2 requests==2.7.0 

而後requirements.txt能夠將其提交到版本控制並做爲應用程序的一部分提供。而後,用戶能夠安裝全部必需的包install -r

(tutorial-env) $ pip install -r requirements.txt Collecting novas==3.1.1.3 (from -r requirements.txt (line 1)) ... Collecting numpy==1.9.2 (from -r requirements.txt (line 2)) ... Collecting requests==2.7.0 (from -r requirements.txt (line 3)) ... Installing collected packages: novas, numpy, requests Running setup.py install for novas Successfully installed novas-3.1.1.3 numpy-1.9.2 requests-2.7.0 

pip還有更多選擇。有關 完整文檔,請參閱安裝Python模塊指南pip當您編寫包並但願在Python包索引中使其可用時,請參閱Distributing Python Modules指南。

十三.浮點運算:問題和侷限

浮點數在計算機硬件中表示爲基數2(二進制)分數。例如,小數部分

0.125

值爲1/10 + 2/100 + 5/1000,二進制分數相同

0.001

值爲0/2 + 0/4 + 1/8。這兩個分數具備相同的值,惟一真正的區別是第一個用基數10分數表示法寫入,第二個用基數2表示。

不幸的是,大多數小數部分不能徹底表示爲二進制分數。結果是,一般,您輸入的十進制浮點數僅由實際存儲在機器中的二進制浮點數近似。

這個問題最初在基數10中更容易理解。考慮1/3的分數。您能夠將其近似爲基數10分數:

0.3

或更好,

0.33

或更好,

0.333

等等。不管你願意記下多少位數,結果都不會徹底是1/3,但將會愈來愈好地逼近1/3。

一樣,不管您願意使用多少個2位數,十進制值0.1都不能徹底表示爲基數2分數。在鹼2中,1/10是無限重複的部分

0.0001100110011001100110011001100110011001100110011... 

停在任何有限的位數,你獲得一個近似值。在今天的大多數機器上,浮點數使用二進制分數近似,分子使用前53位從最高位開始,分母爲2的冪。在1/10的狀況下,二進制分數接近但不徹底等於1/10的真實值。3602879701896397 ** 55

因爲顯示值的方式,許多用戶不知道近似值。Python僅打印十進制近似值到機器存儲的二進制近似值的真實十進制值。在大多數機器上,若是Python要打印存儲爲0.1的二進制近似值的真實十進制值,則必須顯示

>>>
>>> 0.1 0.1000000000000000055511151231257827021181583404541015625 

這比大多數人認爲有用的數字更多,所以Python經過顯示舍入值來保持可管理的位數

>>>
>>> 1 / 10 0.1 

請記住,即便打印結果看起來像1/10的精確值,實際存儲的值也是最接近的可表示二進制分數。

有趣的是,有許多不一樣的十進制數,它們共享相同的最接近的近似二進制分數。例如,數字0.10.10000000000000001和 0.1000000000000000055511151231257827021181583404541015625都是近似的因爲全部這些十進制值共享相同的近似值,所以能夠在保留不變量的同時顯示它們中的任何一個3602879701896397 **55eval(repr(x)) == x

從歷史上看,Python提示和內置repr()函數將選擇具備17位有效數字的函數0.10000000000000001從Python 3.1開始,Python(在大多數系統上)如今可以選擇最短的並簡單地顯示0.1

請注意,這是二進制浮點的本質:這不是Python中的錯誤,也不是代碼中的錯誤。您將在支持硬件浮點運算的全部語言中看到相同的類型(儘管某些語言默認狀況下可能沒法顯示差別,或者在全部輸出模式下都不會顯示差別)。

爲了得到更愉快的輸出,您可能但願使用字符串格式來生成有限數量的有效數字:

>>>
>>> format(math.pi, '.12g') # give 12 significant digits '3.14159265359' >>> format(math.pi, '.2f') # give 2 digits after the point '3.14' >>> repr(math.pi) '3.141592653589793' 

重要的是要意識到,從實際意義上說,這是一種錯覺:你只是簡單地圍繞真實機器價值顯示

一種錯覺可能會產生另外一種錯覺。例如,因爲0.1不徹底是1/10,所以將三個0.1的值相加可能不會精確地產生0.3,或者:

>>>
>>> .1 + .1 + .1 == .3 False 

此外,因爲0.1不能接近1/10的精確值,0.3不能接近3/10的精確值,那麼使用round()函數預先舍入 不能幫助:

>>>
>>> round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1) False 

儘管數字不能接近其預期的精確值,但該round()函數可用於後舍入,以使具備不精確值的結果彼此至關:

>>>
>>> round(.1 + .1 + .1, 10) == round(.3, 10) True 

二進制浮點運算有不少這樣的驚喜。下面在「表示錯誤」部分中詳細解釋了「0.1」的問題。有關 其餘常見驚喜的更完整說明,請參閱浮點危險

正如接近結尾所說,「沒有簡單的答案。」不過,不要過度警戒浮點!Python浮點運算中的錯誤是從浮點硬件繼承而來的,而且在大多數機器上每一個操做的錯誤數量不超過1 ** 2 ** 53。這對於大多數任務來講已經足夠了,可是你須要記住它不是十進制算術而且每次浮點運算都會遇到新的舍入錯誤。

雖然存在病態狀況,但對於大多數臨時使用浮點運算,若是您只是將最終結果的顯示四捨五入到您指望的十進制數字,您將看到最終指望的結果。 str()一般就足夠了,而且爲了更好的控制,請參閱格式字符串語法中str.format() 方法的格式說明符

對於須要精確十進制表示的用例,請嘗試使用decimal適用於會計應用程序和高精度應用程序的實現十進制算術模塊。

精確算術的另外一種形式由fractions模塊支持,該模塊基於有理數實現算術(所以能夠精確地表示像1/3這樣的數字)。

若是您是浮點運算的重要用戶,您應該查看Numerical Python軟件包以及SciPy項目提供的數學和統計操做的許多其餘軟件包。請參閱< https://scipy.org >。

Python提供的工具,可對這些罕見的狀況下幫助當你真的 想要知道一個浮動的精確值。float.as_integer_ratio()方法將float的值表示爲分數:

>>>
>>> x = 3.14159 >>> x.as_integer_ratio() (3537115888337719, 1125899906842624) 

因爲比率是精確的,它能夠用於無損地從新建立原始值:

>>>
>>> x == 3537115888337719 / 1125899906842624 True 

float.hex()方法以十六進制表示浮點數(基數爲16),再次給出計算機存儲的精確值:

>>>
>>> x.hex() '0x1.921f9f01b866ep+1' 

這個精確的十六進制表示可用於精確重建浮點值:

>>>
>>> x == float.fromhex('0x1.921f9f01b866ep+1') True 

因爲表示是精確的,所以能夠跨不一樣版本的Python(平臺獨立性)可靠地移植值,並與支持相同格式的其餘語言(例如Java和C99)交換數據。

另外一個有用的工具是math.fsum()有助於減小求和過程當中精度損失功能。當值添加到運行總計時,它會跟蹤「丟失的數字」。這可能會對總體準確性產生影響,所以錯誤不會累積到影響最終總數的程度:

>>>
>>> sum([0.1] * 10) == 1.0 False >>> math.fsum([0.1] * 10) == 1.0 True 

13.1 表示錯誤

本節詳細介紹「0.1」示例,並說明如何自行對此類案例進行精確分析。假設基本熟悉二進制浮點表示。

表示錯誤是指某些(大多數,實際上)小數部分不能徹底表示爲二進制(基數2)分數的事實。這就是爲何Python(或Perl,C,C ++,Java,Fortran和許多其餘人)一般不會顯示您指望的確切十進制數的主要緣由。

這是爲何?1/10不能徹底表示爲二進制分數。今天(2000年11月)幾乎全部機器都使用IEEE-754浮點運算,幾乎全部平臺都將Python浮點數映射到IEEE-754「雙精度」。754雙精度包含53位精度,所以在輸入時,計算機努力將0.1轉換爲J / 2 ** N形式的最接近的分數,其中J是包含正好53位的整數。重寫

1 / 10 ~= J / (2**N) 

J ~= 2**N / 10 

而且回想起J剛好有53位(可是),N的最佳值是56:>= 2**522**53

>>>
>>> 2**52 <= 2**56 // 10 < 2**53 True 

也就是說,56是N的惟一值,它使J正好爲53位。而後,J的最佳可能值是舍入的商:

>>>
>>> q, r = divmod(2**56, 10) >>> r 6 

因爲餘數超過10的一半,所以經過四捨五入得到最佳近似值:

>>>
>>> q+1 7205759403792794 

所以,754雙精度的最佳可能近似值爲1/10:

7205759403792794 / 2 ** 56 

將分子和分母除以2將分數減小到:

3602879701896397 / 2 ** 55 

請注意,因爲咱們向上舍入,這實際上比1/10大一點; 若是咱們沒有四捨五入,則商數將小於1/10。但在任何狀況下都不能徹底是 1/10!

因此計算機永遠不會「看到」1/10:它看到的是上面給出的精確分數,它能夠得到的最佳754雙近似值:

>>>
>>> 0.1 * 2 ** 55 3602879701896397.0 

若是咱們將該分數乘以10 ** 55,咱們能夠看到55到十進制數字的值:

>>>
>>> 3602879701896397 * 10 ** 55 // 2 ** 55 1000000000000000055511151231257827021181583404541015625 

意味着存儲在計算機中的確切數字等於十進制值0.1000000000000000055511151231257827021181583404541015625。許多語言(包括舊版本的Python)不是顯示完整的十進制值,而是將結果舍入爲17位有效數字:

>>>
>>> format(0.1, '.17f') '0.10000000000000001' 

fractionsdecimal模塊進行這些計算很簡單:

>>>
>>> from decimal import Decimal >>> from fractions import Fraction >>> Fraction.from_float(0.1) Fraction(3602879701896397, 36028797018963968) >>> (0.1).as_integer_ratio() (3602879701896397, 36028797018963968) >>> Decimal.from_float(0.1) Decimal('0.1000000000000000055511151231257827021181583404541015625') >>> format(Decimal.from_float(0.1), '.17') '0.10000000000000001'

十四.附錄

14.1 交互模式

14.1.1 錯誤處理

發生錯誤時,解釋器會輸出錯誤消息和堆棧跟蹤。在交互模式下,它而後返回主要提示; 當輸入來自文件時,它在打印堆棧跟蹤後以非零退出狀態退出。(由語句中except子句處理的異常try在此上下文中不是錯誤。)某些錯誤無條件地致命並致使退出非零退出; 這適用於內部不一致和一些內存不足的狀況。全部錯誤消息都寫入標準錯誤流; 執行命令的正常輸出寫入標準輸出。

將中斷字符(一般Control-CDelete鍵入主要或輔助提示會取消輸入並返回主要提示。[1] 在執行命令時鍵入中斷會引起 KeyboardInterrupt異常,這可能由try 語句處理

14.1.2 可執行的Python腳本

在BSD的Unix系統上,Python腳本能夠直接執行,就像shell腳本同樣,經過放置行

#!/usr/bin/env python3.5

(假設翻譯是在用戶的 PATH)在腳本的開頭,給文件一個可執行模式。#!必須是文件的前兩個字符。在某些平臺上,第一行必須以Unix樣式行ends('\n'結束,而不是以Windows('\r\n')行結尾。請注意,hash或pound字符'#'用於在Python中啓動註釋。

能夠使用chmod命令爲腳本指定可執行模式或權限 

$ chmod +x myscript.py

在Windows系統上,沒有「可執行模式」的概念。Python安裝程序自動關聯.py文件,python.exe以便雙擊Python文件將其做爲腳本運行。.pyw在這種狀況下,擴展也能夠是一般出現的控制檯窗口被抑制。

14.1.3 交互式啓動文件

當您以交互方式使用Python時,每次啓動解釋器時都會執行一些標準命令,這一般很方便。您能夠經過設置名爲的環境變量來完成此操做PYTHONSTARTUP到包含啓動命令的文件的名稱。這相似於.profile Unix shell 功能。

此文件僅在交互式會話中讀取,而不是在Python從腳本讀取命令時讀取,而不是在/dev/tty做爲顯式命令源(其餘行爲相似於交互式會話)的狀況下讀取它在執行交互式命令的同一命名空間中執行,所以能夠在交互式會話中無限制地使用它定義或導入的對象。您也能夠更改提示sys.ps1sys.ps2此文件。

若是要從當前目錄中讀取其餘啓動文件,能夠使用相似代碼在全局啓動文件中對此進行編程若是要在腳本中使用啓動文件,則必須在腳本中明確執行此操做:if os.path.isfile('.pythonrc.py'):exec(open('.pythonrc.py').read())

import os filename = os.environ.get('PYTHONSTARTUP') if filename and os.path.isfile(filename): with open(filename) as fobj: startup_file = fobj.read() exec(startup_file) 

14.1.4 定製模塊

Python提供了兩個鉤子來讓你自定義它:sitecustomize和 usercustomize要查看其工做原理,首先須要找到用戶site-packages目錄的位置。啓動Python並運行如下代碼:

>>>
>>> import site >>> site.getusersitepackages() '/home/user/.local/lib/python3.5/site-packages' 

如今,您能夠建立usercustomize.py在該目錄中命名的文件,並將所需內容放入其中。它會影響Python的每次調用,除非它是以-s禁用自動導入選項啓動的。

sitecustomize以相同的方式工做,但一般由全局site-packages目錄中的計算機管理員建立,並在以前導入usercustomize有關site 更多詳細信息,請參閱模塊的文檔

相關文章
相關標籤/搜索