按章節浏覽文檔:
Python 3.11 有什麼新變化?
或顯示自 2.0 以來的全部新變化
教程
從這裡看起
標准庫參考
放在枕邊作為參考
語言參考
講解基礎內容和基本語法
Python安裝和使用
各種操作系統的介紹都有
Python 常用指引
深入了解特定主題
安裝 Python 模塊
從官方的 PyPI 或者其他來源安裝模塊
分發 Python 模塊
發布模塊,供其他人安裝
擴展和嵌入
給 C/C++ 程序員的教程
Python/C API 接口
給 C/C++ 程序員的參考手冊
常見問題
經常被問到的問題(答案也有!)
索引和表格:
全局模塊索引
快速查找所有的模塊
總目錄
所有的函數、類、術語
術語對照表
解釋最重要的術語
搜索頁面
搜索本文檔
完整目錄
列出所有的章節和子章節
Python 教程 — Python 3.11.0b3 文檔
Python 模塊索引 — Python 3.11.0b3 文檔
Python 語言參考手冊 — Python 3.11.0b3 文檔
內置函數 — Python 3.11.0b3 文檔
Python 標准庫 — Python 3.11.0b3 文檔
擴展和嵌入 Python 解釋器 — Python 3.11.0b3 文檔
Python 解釋器內置了很多函數和類型,任何時候都能使用。以下按字母順序給出列表。
內置函數
A
abs()
aiter()
all()
any()
anext()
ascii()
B
bin()
bool()
breakpoint()
bytearray()
bytes()
C
callable()
chr()
classmethod()
compile()
complex()
D
delattr()
dict()
dir()
divmod()
E
enumerate()
eval()
exec()
F
filter()
float()
format()
frozenset()
G
getattr()
globals()
H
hasattr()
hash()
help()
hex()
I
id()
input()
int()
isinstance()
issubclass()
iter()
L
len()
list()
locals()
M
map()
max()
memoryview()
min()
N
next()
O
object()
oct()
open()
ord()
P
pow()
print()
property()
R
range()
repr()
reversed()
round()
S
set()
setattr()
slice()
sorted()
staticmethod()
str()
sum()
super()
T
tuple()
type()
V
vars()
Z
zip()
_
__import__()
Python 語言參考手冊 — Python 3.11.0b3 文檔
Python 語言參考手冊 描述了 Python 語言的具體語法和語義,這份庫參考則介紹了與 Python 一同發行的標准庫。它還描述了通常包含在 Python 發行版中的一些可選組件。
Python 標准庫非常龐大,所提供的組件涉及范圍十分廣泛,正如以下內容目錄所顯示的。這個庫包含了多個內置模塊 (以 C 編寫),Python 程序員必須依靠它們來實現系統級功能,例如文件 I/O,此外還有大量以 Python 編寫的模塊,提供了日常編程中許多問題的標准解決方案。其中有些模塊經過專門設計,通過將特定平台功能抽象化為平台中立的 API 來鼓勵和加強 Python 程序的可移植性。
Windows 版本的 Python 安裝程序通常包含整個標准庫,往往還包含許多額外組件。對於類 Unix 操作系統,Python 通常會分成一系列的軟件包,因此可能需要使用操作系統所提供的包管理工具來獲取部分或全部可選組件。
在這個標准庫以外還存在成千上萬並且不斷增加的其他組件 (從單獨的程序、模塊、軟件包直到完整的應用開發框架),訪問 Python 包索引 即可獲取這些第三方包。
abs(x)
返回一個數的絕對值。 參數可以是整數、浮點數或任何實現了
__abs__()
的對象。 如果參數是一個復數,則返回它的模。
aiter(async_iterable)
返回 asynchronous iterable 的 asynchronous iterator 。相當於調用
x.__aiter__()
。注意:與 iter() 不同,aiter() 沒有兩個參數的版本。
3.10 新版功能.
all(iterable)
如果 iterable 的所有元素均為真值(或可迭代對象為空)則返回
True
。 等價於:def all(iterable): for element in iterable: if not element: return False return True
awaitable anext(async_iterator[, default])
當進入 await 狀態時,從給定 asynchronous iterator 返回下一數據項,迭代完畢則返回 default。
這是內置函數 next() 的異步版本,類似於:
調用 async_iterator 的 __anext__() 方法,返回一個 awaitable。等待返回迭代器的下一個值。若有給出 default,則在迭代完畢後會返回給出的值,否則會觸發 StopAsyncIteration。
3.10 新版功能.
any(iterable)
如果 iterable 的任一元素為真值則返回
True
。 如果可迭代對象為空,返回False
。 等價於:def any(iterable): for element in iterable: if element: return True return False
ascii(object)
與 repr() 類似,返回一個字符串,表示對象的可打印形式,但在 repr() 返回的字符串中,非 ASCII 字符會用
\x
、\u
和\U
進行轉義。生成的字符串類似於 Python 2 中 repr() 的返回結果。
bin(x)
將整數轉變為以“0b”前綴的二進制字符串。結果是一個合法的 Python 表達式。如果 x 不是 Python 的 int 對象,它必須定義
__index__()
方法,以便返回整數值。下面是一些例子:>>>
>>> bin(3) '0b11' >>> bin(-10) '-0b1010'若要控制是否顯示前綴“0b”,可以采用以下兩種方案:
>>>
>>> format(14, '#b'), format(14, 'b') ('0b1110', '1110') >>> f'{14:#b}', f'{14:b}' ('0b1110', '1110')另見 format() 獲取更多信息。
class bool([x])
返回布爾值,
True
或False
。x 用標准的 真值測試過程 進行轉換。如果 x 為 False 或省略,則返回False
;否則返回True
。 bool 類是 int 的子類(見 數字類型 --- int, float, complex )。它不能再被繼承。它唯一的實例就是False
和True
(參閱 布爾值 )。在 3.7 版更改: x 現在只能作為位置參數。
breakpoint(*args, **kws)
此函數會在調用時將你陷入調試器中。具體來說,它調用 sys.breakpointhook() ,直接傳遞
args
和kws
。默認情況下,sys.breakpointhook()
調用 pdb.set_trace() 且沒有參數。在這種情況下,它純粹是一個便利函數,因此您不必顯式導入 pdb 且鍵入盡可能少的代碼即可進入調試器。但是, sys.breakpointhook() 可以設置為其他一些函數並被 breakpoint() 自動調用,以允許進入你想用的調試器。引發一個 審計事件
builtins.breakpoint
並附帶參數breakpointhook
。3.7 新版功能.
class bytearray([source[, encoding[, errors]]])
返回一個新的 bytes 數組。 bytearray 類是一個可變序列,包含范圍為 0 <= x < 256 的整數。它有可變序列大部分常見的方法,見 可變序列類型 的描述;同時有 bytes 類型的大部分方法,參見 bytes 和 bytearray 操作。
可選形參 source 可以用不同的方式來初始化數組:
如果是一個 string,您必須提供 encoding 參數(errors 參數仍是可選的);bytearray() 會使用 str.encode() 方法來將 string 轉變成 bytes。
如果是一個 integer,會初始化大小為該數字的數組,並使用 null 字節填充。
如果是一個遵循 緩沖區接口 的對象,該對象的只讀緩沖區將被用來初始化字節數組。
如果是一個 iterable 可迭代對象,它的元素的范圍必須是
0 <= x < 256
的整數,它會被用作數組的初始內容。如果沒有實參,則創建大小為 0 的數組。
另見 二進制序列類型 --- bytes, bytearray, memoryview 和 bytearray 對象。
class bytes([source[, encoding[, errors]]])
返回一個新的“bytes”對象,這是一個不可變序列,包含范圍為
0 <= x < 256
的整數。bytes 是 bytearray 的不可變版本——帶有同樣不改變序列的方法,支持同樣的索引、切片操作。因此,構造函數的實參和 bytearray() 相同。
字節對象還可以用字面值創建,參見 字符串與字節串字面值。
另見 二進制序列類型 --- bytes, bytearray, memoryview,bytes 對象 和 bytes 和 bytearray 操作。
callable(object)
如果參數 object 是可調用的就返回 True,否則返回 False。 如果返回
True
,調用仍可能失敗,但如果返回False
,則調用 object 將肯定不會成功。 請注意類是可調用的(調用類將返回一個新的實例);如果實例所屬的類有__call__()
則它就是可調用的。3.2 新版功能: 這個函數一開始在 Python 3.0 被移除了,但在 Python 3.2 被重新加入。
chr(i)
返回 Unicode 碼位為整數 i 的字符的字符串格式。例如,
chr(97)
返回字符串'a'
,chr(8364)
返回字符串'€'
。這是 ord() 的逆函數。實參的合法范圍是 0 到 1,114,111(16 進制表示是 0x10FFFF)。如果 i 超過這個范圍,會觸發 ValueError 異常。
@classmethod
把一個方法封裝成類方法。
類方法隱含的第一個參數就是類,就像實例方法接收實例作為參數一樣。要聲明一個類方法,按慣例請使用以下方案:
class C: @classmethod def f(cls, arg1, arg2): ...
@classmethod
這樣的形式稱為函數的 decorator -- 詳情參閱 函數定義。類方法的調用可以在類上進行 (例如
C.f()
) 也可以在實例上進行 (例如C().f()
)。 其所屬類以外的類實例會被忽略。 如果類方法在其所屬類的派生類上調用,則該派生類對象會被作為隱含的第一個參數被傳入。類方法與 C++ 或 Java 中的靜態方法不同。 如果你需要後者,請參閱本節中的 staticmethod()。 有關類方法的更多信息,請參閱 標准類型層級結構。
在 3.9 版更改: 類方法現在可以包裝其他 描述器 例如 property()。
在 3.10 版更改: 類方法現在繼承了方法的屬性(
__module__
、__name__
、__qualname__
、__doc__
和__annotations__
),並擁有一個新的``__wrapped__`` 屬性。在 3.11 版更改: Class methods can no longer wrap other descriptors such as property().
compile(source, filename, mode, flags=0, dont_inherit=False, optimize=- 1)
將 source 編譯成代碼或 AST 對象。代碼對象可以被 exec() 或 eval() 執行。source 可以是常規的字符串、字節字符串,或者 AST 對象。參見 ast 模塊的文檔了解如何使用 AST 對象。
filename 實參需要是代碼讀取的文件名;如果代碼不需要從文件中讀取,可以傳入一些可辨識的值(經常會使用
'<string>'
)。mode 實參指定了編譯代碼必須用的模式。如果 source 是語句序列,可以是
'exec'
;如果是單一表達式,可以是'eval'
;如果是單個交互式語句,可以是'single'
。(在最後一種情況下,如果表達式執行結果不是None
將會被打印出來。)可選參數 flags 和 dont_inherit 控制應當激活哪個 編譯器選項 以及應當允許哪個 future 特性。 如果兩者都未提供 (或都為零) 則代碼會應用與調用 compile() 的代碼相同的旗標來編譯。 如果給出了 flags 參數而未給出 dont_inherit (或者為零) 則會在無論如何都將被使用的旗標之外還會額外使用 flags 參數所指定的編譯器選項和 future 語句。 如果 dont_inherit 為非零整數,則只使用 flags 參數 -- 外圍代碼中的旗標 (future 特性和編譯器選項) 會被忽略。
編譯器選項和 future 語句是由比特位來指明的。 比特位可以通過一起按位 OR 來指明多個選項。 指明特定 future 特性所需的比特位可以在 __future__ 模塊的
_Feature
實例的compiler_flag
屬性中找到。 編譯器旗標 可以在 ast 模塊中查找帶有PyCF_
前綴的名稱。optimize 實參指定編譯器的優化級別;默認值
-1
選擇與解釋器的 -O 選項相同的優化級別。顯式級別為0
(沒有優化;__debug__
為真)、1
(斷言被刪除,__debug__
為假)或2
(文檔字符串也被刪除)。如果編譯的源碼不合法,此函數會觸發 SyntaxError 異常;如果源碼包含 null 字節,則會觸發 ValueError 異常。
如果您想分析 Python 代碼的 AST 表示,請參閱 ast.parse()。
引發一個 審計事件
compile
附帶參數source
,filename
。備注
在
'single'
或'eval'
模式編譯多行代碼字符串時,輸入必須以至少一個換行符結尾。 這使 code 模塊更容易檢測語句的完整性。警告
在將足夠大或者足夠復雜的字符串編譯成 AST 對象時,Python 解釋器有可能因為 Python AST 編譯器的棧深度限制而崩潰。
在 3.2 版更改: Windows 和 Mac 的換行符均可使用。而且在
'exec'
模式下的輸入不必再以換行符結尾了。另增加了 optimize 參數。在 3.5 版更改: 之前 source 中包含 null 字節的話會觸發 TypeError 異常。
3.8 新版功能:
ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
現在可在旗標中傳入以啟用對最高層級await
,async for
和async with
的支持。
class complex([real[, imag]])
返回值為 real + imag*1j 的復數,或將字符串或數字轉換為復數。如果第一個形參是字符串,則它被解釋為一個復數,並且函數調用時必須沒有第二個形參。第二個形參不能是字符串。每個實參都可以是任意的數值類型(包括復數)。如果省略了 imag,則默認值為零,構造函數會像 int 和 float 一樣進行數值轉換。如果兩個實參都省略,則返回
0j
。對於一個普通 Python 對象
x
,complex(x)
會委托給x.__complex__()
。 如果__complex__()
未定義則將回退至__float__()
。 如果__float__()
未定義則將回退至__index__()
。備注
當從字符串轉換時,字符串在
+
或-
的周圍必須不能有空格。例如complex('1+2j')
是合法的,但complex('1 + 2j')
會觸發 ValueError 異常。數字類型 --- int, float, complex 描述了復數類型。
在 3.6 版更改: 您可以使用下劃線將代碼文字中的數字進行分組。
在 3.8 版更改: 如果
__complex__()
和__float__()
未定義則回退至__index__()
。
delattr(object, name)
setattr() 相關的函數。實參是一個對象和一個字符串。該字符串必須是對象的某個屬性。如果對象允許,該函數將刪除指定的屬性。例如
delattr(x, 'foobar')
等價於del x.foobar
。class dict(**kwarg)
class dict(mapping, **kwarg)
class dict(iterable, **kwarg)
創建一個新的字典。dict 對象是一個字典類。參見 dict 和 映射類型 --- dict 了解這個類。
其他容器類型,請參見內置的 list、set 和 tuple 類,以及 collections 模塊。
dir([object])
如果沒有實參,則返回當前本地作用域中的名稱列表。如果有實參,它會嘗試返回該對象的有效屬性列表。
如果對象有一個名為
__dir__()
的方法,那麼該方法將被調用,並且必須返回一個屬性列表。這允許實現自定義__getattr__()
或__getattribute__()
函數的對象能夠自定義 dir() 來報告它們的屬性。如果對象未提供
__dir__()
方法,該函數會盡量從對象的 __dict__ 屬性和其類型對象中收集信息。得到的列表不一定是完整,如果對象帶有自定義__getattr__()
方法時,結果可能不准確。默認的 dir() 機制對不同類型的對象行為不同,它會試圖返回最相關而不是最全的信息:
如果對象是模塊對象,則列表包含模塊的屬性名稱。
如果對象是類型或類對象,則列表包含它們的屬性名稱,並且遞歸查找所有基類的屬性。
否則,列表包含對象的屬性名稱,它的類屬性名稱,並且遞歸查找它的類的所有基類的屬性。
返回的列表按字母表排序。例如:
>>>
>>> import struct >>> dir() # show the names in the module namespace ['__builtins__', '__name__', 'struct'] >>> dir(struct) # show the names in the struct module ['Struct', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', '__loader__', '__name__', '__package__', '_clearcache', 'calcsize', 'error', 'pack', 'pack_into', 'unpack', 'unpack_from'] >>> class Shape: ... def __dir__(self): ... return ['area', 'perimeter', 'location'] >>> s = Shape() >>> dir(s) ['area', 'location', 'perimeter']備注
因為 dir() 主要是為了便於在交互式時使用,所以它會試圖返回人們感興趣的名字集合,而不是試圖保證結果的嚴格性或一致性,它具體的行為也可能在不同版本之間改變。例如,當實參是一個類時,metaclass 的屬性不包含在結果列表中。
divmod(a, b)
以兩個(非復數)數字為參數,在作整數除法時,返回商和余數。若操作數為混合類型,則適用二進制算術運算符的規則。對於整數而言,結果與
(a // b, a % b)
相同。對於浮點數則結果為``(q, a % b)``,其中 q 通常為math.floor(a / b)
,但可能比它小 1。在任何情況下,q * b + a % b
都非常接近 a,如果a % b
非零,則結果符號與 b 相同,並且0 <= abs(a % b) < abs(b)
。
enumerate(iterable, start=0)
返回一個枚舉對象。iterable 必須是一個序列,或 iterator,或其他支持迭代的對象。 enumerate() 返回的迭代器的 __next__() 方法返回一個元組,裡面包含一個計數值(從 start 開始,默認為 0)和通過迭代 iterable 獲得的值。
>>>
>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter'] >>> list(enumerate(seasons)) [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')] >>> list(enumerate(seasons, start=1)) [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]等價於:
def enumerate(sequence, start=0): n = start for elem in sequence: yield n, elem n += 1
eval(expression[, globals[, locals]])
實參是一個字符串,以及可選的 globals 和 locals。globals 實參必須是一個字典。locals 可以是任何映射對象。
表達式解析參數 expression 並作為 Python 表達式進行求值(從技術上說是一個條件列表),采用 globals 和 locals 字典作為全局和局部命名空間。 如果存在 globals 字典,並且不包含
__builtins__
鍵的值,則在解析 expression 之前會插入以該字符串為鍵以對內置模塊 builtins 的字典的引用為值的項。 這樣就可以在將 globals 傳給 eval() 之前通過向其傳入你自己的__builtins__
字典來控制可供被執行代碼可以使用哪些內置模塊。 如果 locals 字典被省略則它默認為 globals 字典。 如果兩個字典都被省略,則將使用調用 eval() 的環境中的 globals 和 locals 來執行該表達式。 注意,eval() 無法訪問閉包環境中的 嵌套作用域 (非局部變量)。返回值就是表達式的求值結果。 語法錯誤將作為異常被報告。 例如:
>>>
>>> x = 1 >>> eval('x+1') 2該函數還可用於執行任意代碼對象(比如由 compile() 創建的對象)。 這時傳入的是代碼對象,而非一個字符串了。如果代碼對象已用參數為 mode 的
'exec'
進行了編譯,那麼 eval() 的返回值將為None
。提示: exec() 函數支持語句的動態執行。 globals() 和 locals() 函數分別返回當前的全局和本地字典,可供傳給 eval() 或 exec() 使用。
如果給出的源數據是個字符串,那麼其前後的空格和制表符將被剔除。
另外可以參閱 ast.literal_eval(),該函數可以安全執行僅包含文字的表達式字符串。
引發一個 審計事件
exec
附帶參數code_object
。
exec(object, [globals, [locals, ]]*, closure=None)
這個函數支持動態執行 Python 代碼。 object 必須是字符串或者代碼對象。 如果是字符串,那麼該字符串將被解析為一系列 Python 語句並執行(除非發生語法錯誤)。 1 如果是代碼對象,它將被直接執行。 在任何情況下,被執行的代碼都應當是有效的文件輸入(見參考手冊中的 文件輸入 一節)。 請注意即使在傳遞給 exec() 函數的代碼的上下文中,nonlocal, yield 和 return 語句也不能在函數定義以外使用。 該函數的返回值是
None
。無論在什麼情況下,如果省略了可選部分,代碼將運行於當前作用域中。如果只提供了 globals,則必須為字典對象(而不能是字典的子類),同時用於存放全局變量和局部變量。如果提供了 globals 和 locals,則將分別用於全局變量和局部變量。locals 可以是任意字典映射對象。請記住,在模塊級別,globals 和 locals 是同一個字典。如果 exec 獲得兩個獨立的對象作為 globals 和 locals,代碼執行起來就像嵌入到某個類定義中一樣。
如果 globals 字典不包含
__builtins__
鍵值,則將為該鍵插入對內建 builtins 模塊字典的引用。因此,在將執行的代碼傳遞給 exec() 之前,可以通過將自己的__builtins__
字典插入到 globals 中來控制可以使用哪些內置代碼。The closure argument specifies a closure--a tuple of cellvars. It's only valid when the object is a code object containing free variables. The length of the tuple must exactly match the number of free variables referenced by the code object.
引發一個 審計事件
exec
附帶參數code_object
。備注
內置 globals() 和 locals() 函數各自返回當前的全局和本地字典,因此可以將它們傳遞給 exec() 的第二個和第三個實參。
備注
默認情況下,locals 的行為如下面 locals() 函數描述的一樣:不要試圖改變默認的 locals 字典。 如果您需要在 exec() 函數返回時查看代碼對 locals 的影響,請明確地傳遞 locals 字典。
在 3.11 版更改: Added the closure parameter.
filter(function, iterable)
用 iterable 中函數 function 返回真的那些元素,構建一個新的迭代器。iterable 可以是一個序列,一個支持迭代的容器,或一個迭代器。如果 function 是
None
,則會假設它是一個身份函數,即 iterable 中所有返回假的元素會被移除。請注意,
filter(function, iterable)
相當於一個生成器表達式,當 function 不是None
的時候為(item for item in iterable if function(item))
;function 是None
的時候為(item for item in iterable if item)
。請參閱 itertools.filterfalse() 了解,只有 function 返回 false 時才選取 iterable 中元素的補充函數。
class float([x])
返回從數字或字符串 x 生成的浮點數。
如果參數是個字符串,則應包含一個十進制數字,前面可選帶上符號,也可選前後帶有空白符。符號可以是``'+'`` 或
'-'
;'+'
符號對值沒有影響。參數也可以是一個代表 NaN(非數字)或正負無窮大的字符串。更確切地說,在去除前導和尾部的空白符後,輸入參數必須符合以下語法:sign ::= "+" | "-" infinity ::= "Infinity" | "inf" nan ::= "nan" numeric_value ::=floatnumber| infinity | nan numeric_string ::= [sign] numeric_value這裡的
floatnumber
是指 Python 的浮點數格式,在 浮點數字面值 中有介紹。大小寫沒有關系,所以“inf”、“Inf”、“INFINITY”、“iNfINity”都可接受為正無窮的拼寫形式。另一方面,如果實參是整數或浮點數,則返回具有相同值(在 Python 浮點精度范圍內)的浮點數。如果實參在 Python 浮點精度范圍外,則會觸發 OverflowError。
對於一個普通 Python 對象
x
,float(x)
會委托給x.__float__()
。 如果__float__()
未定義則將回退至__index__()
。如果沒有實參,則返回
0.0
。示例:
>>>
>>> float('+1.23') 1.23 >>> float(' -12345\n') -12345.0 >>> float('1e-003') 0.001 >>> float('+1E6') 1000000.0 >>> float('-Infinity') -inf數字類型 --- int, float, complex 描述了浮點類型。
在 3.6 版更改: 您可以使用下劃線將代碼文字中的數字進行分組。
在 3.7 版更改: x 現在只能作為位置參數。
在 3.8 版更改: 如果
__float__()
未定義則回退至__index__()
。
format(value[, format_spec])
將 value 轉換為“格式化後”的形式,格式由 format_spec 進行控制。format_spec 的解釋方式取決於 value 參數的類型;但大多數內置類型使用一種標准的格式化語法: 格式規格迷你語言。
默認的 format_spec 是一個空字符串,它通常給出與調用 str(value) 相同的結果。
調用
format(value, format_spec)
會轉換成type(value).__format__(value, format_spec)
,所以實例字典中的__format__()
方法將不會調用。如果方法搜索回退到 object 類但 format_spec 不為空,或者如果 format_spec 或返回值不是字符串,則會觸發 TypeError 異常。在 3.4 版更改: 當 format_spec 不是空字符串時,
object().__format__(format_spec)
會觸發 TypeError。
class frozenset([iterable])
返回一個新的 frozenset 對象,它包含可選參數 iterable 中的元素。
frozenset
是一個內置的類。有關此類的文檔,請參閱 frozenset 和 集合類型 --- set, frozenset。請參閱內建的 set、list、tuple 和 dict 類,以及 collections 模塊來了解其它的容器。
getattr(object, name[, default])
返回對象命名屬性的值。name 必須是字符串。如果該字符串是對象的屬性之一,則返回該屬性的值。例如,
getattr(x, 'foobar')
等同於x.foobar
。如果指定的屬性不存在,且提供了 default 值,則返回它,否則觸發 AttributeError。備注
由於 私有名稱混合 發生在編譯時,因此必須 手動混合私有屬性(以兩個下劃線打頭的屬性)名稱以使使用 getattr() 來提取它。
globals()
返回實現當前模塊命名空間的字典。對於函數內的代碼,這是在定義函數時設置的,無論函數在哪裡被調用都保持不變。
hasattr(object, name)
該實參是一個對象和一個字符串。如果字符串是對象的屬性之一的名稱,則返回
True
,否則返回False
。(此功能是通過調用getattr(object, name)
看是否有 AttributeError 異常來實現的。)
hash(object)
返回該對象的哈希值(如果它有的話)。哈希值是整數。它們在字典查找元素時用來快速比較字典的鍵。相同大小的數字變量有相同的哈希值(即使它們類型不同,如 1 和 1.0)。
備注
如果對象實現了自己的
__hash__()
方法,請注意,hash() 根據機器的字長來截斷返回值。另請參閱__hash__()
。
help([object])
啟動內置的幫助系統(此函數主要在交互式中使用)。如果沒有實參,解釋器控制台裡會啟動交互式幫助系統。如果實參是一個字符串,則在模塊、函數、類、方法、關鍵字或文檔主題中搜索該字符串,並在控制台上打印幫助信息。如果實參是其他任意對象,則會生成該對象的幫助頁。
請注意,如果在調用 help() 時,目標函數的形參列表中存在斜槓(/),則意味著斜槓之前的參數只能是位置參數。詳情請參閱 有關僅限位置形參的 FAQ 條目。
該函數通過 site 模塊加入到內置命名空間。
在 3.4 版更改: pydoc 和 inspect 的變更使得可調用對象的簽名信息更加全面和一致。
hex(x)
將整數轉換為以“0x”為前綴的小寫十六進制字符串。如果 x 不是 Python int 對象,則必須定義返回整數的
__index__()
方法。一些例子:>>>
>>> hex(255) '0xff' >>> hex(-42) '-0x2a'如果要將整數轉換為大寫或小寫的十六進制字符串,並可選擇有無“0x”前綴,則可以使用如下方法:
>>>
>>> '%#x' % 255, '%x' % 255, '%X' % 255 ('0xff', 'ff', 'FF') >>> format(255, '#x'), format(255, 'x'), format(255, 'X') ('0xff', 'ff', 'FF') >>> f'{255:#x}', f'{255:x}', f'{255:X}' ('0xff', 'ff', 'FF')另見 format() 獲取更多信息。
另請參閱 int() 將十六進制字符串轉換為以 16 為基數的整數。
備注
如果要獲取浮點數的十六進制字符串形式,請使用 float.hex() 方法。
id(object)
返回對象的“標識值”。該值是一個整數,在此對象的生命周期中保證是唯一且恆定的。兩個生命期不重疊的對象可能具有相同的 id() 值。
CPython implementation detail: This is the address of the object in memory.
引發一個 審計事件
builtins.id
,附帶參數id
。
input([prompt])
如果存在 prompt 實參,則將其寫入標准輸出,末尾不帶換行符。接下來,該函數從輸入中讀取一行,將其轉換為字符串(除了末尾的換行符)並返回。當讀取到 EOF 時,則觸發 EOFError。例如:
>>>
>>> s = input('--> ') --> Monty Python's Flying Circus >>> s "Monty Python's Flying Circus"如果加載了 readline 模塊,input() 將使用它來提供復雜的行編輯和歷史記錄功能。
引發一個 審計事件
builtins.input
附帶參數prompt
。在成功讀取輸入之後引發一個 審計事件
builtins.input/result
附帶結果。
class int([x])
class int(x, base=10)
返回一個基於數字或字符串 x 構造的整數對象,或者在未給出參數時返回
0
。 如果 x 定義了__int__()
,int(x)
將返回x.__int__()
。 如果 x 定義了__index__()
,它將返回x.__index__()
。 如果 x 定義了__trunc__()
,它將返回x.__trunc__()
。 對於浮點數,它將向零捨入。如果 x 不是數字,或者有 base 參數,x 必須是字符串、bytes、表示進制為 base 的 整數字面值 的 bytearray 實例。該文字前可以有
+
或-
(中間不能有空格),前後可以有空格。一個進制為 n 的數字包含 0 到 n-1 的數,其中a
到z
(或A
到Z
)表示 10 到 35。默認的 base 為 10 ,允許的進制有 0、2-36。2、8、16 進制的數字可以在代碼中用0b
/0B
、0o
/0O
、0x
/0X
前綴來表示。進制為 0 將安照代碼的字面量來精確解釋,最後的結果會是 2、8、10、16 進制中的一個。所以int('010', 0)
是非法的,但int('010')
和int('010', 8)
是合法的。整數類型定義請參閱 數字類型 --- int, float, complex 。
在 3.4 版更改: 如果 base 不是 int 的實例,但 base 對象有 base.__index__ 方法,則會調用該方法來獲取進制數。以前的版本使用 base.__int__ 而不是 base.__index__。
在 3.6 版更改: 您可以使用下劃線將代碼文字中的數字進行分組。
在 3.7 版更改: x 現在只能作為位置參數。
在 3.8 版更改: 如果
__int__()
未定義則回退至__index__()
。在 3.11 版更改: The delegation to
__trunc__()
is deprecated.
isinstance(object, classinfo)
Return
True
if the object argument is an instance of the classinfo argument, or of a (direct, indirect, or virtual) subclass thereof. If object is not an object of the given type, the function always returnsFalse
. If classinfo is a tuple of type objects (or recursively, other such tuples) or a union 類型 of multiple types, returnTrue
if object is an instance of any of the types. If classinfo is not a type or tuple of types and such tuples, a TypeError exception is raised. TypeError may not be raised for an invalid type if an earlier check succeeds.在 3.10 版更改: classinfo 可以是一個 union 類型。
issubclass(class, classinfo)
如果 class 是 classinfo 的子類(直接、間接或 虛的 ),則返回
True
。類將視為自己的子類。classinfo 可為類對象的元組(或遞歸地,其他這樣的元組)或 union 類型,這時如果 class 是 classinfo 中任何條目的子類,則返回True
。任何其他情況都會觸發 TypeError 異常。在 3.10 版更改: classinfo 可以是一個 union 類型。
iter(object[, sentinel])
返回一個 iterator 對象。根據是否存在第二個實參,第一個實參的解釋是非常不同的。如果沒有第二個實參,object 必須是支持 iterable 協議(有
__iter__()
方法)的集合對象,或必須支持序列協議(有__getitem__()
方法,且數字參數從0
開始)。如果它不支持這些協議,會觸發 TypeError。如果有第二個實參 sentinel,那麼 object 必須是可調用的對象。這種情況下生成的迭代器,每次迭代調用它的 __next__() 方法時都會不帶實參地調用 object;如果返回的結果是 sentinel 則觸發 StopIteration,否則返回調用結果。另請參閱 迭代器類型。
適合 iter() 的第二種形式的應用之一是構建塊讀取器。 例如,從二進制數據庫文件中讀取固定寬度的塊,直至到達文件的末尾:
from functools import partial with open('mydata.db', 'rb') as f: for block in iter(partial(f.read, 64), b''): process_block(block)
len(s)
返回對象的長度(元素個數)。實參可以是序列(如 string、bytes、tuple、list 或 range 等)或集合(如 dictionary、set 或 frozen set 等)。
CPython implementation detail:
len
對於大於 sys.maxsize 的長度如 range(2 ** 100) 會引發 OverflowError。
class list([iterable])
雖然被稱為函數,list 實際上是一種可變序列類型,詳情請參閱 列表 和 序列類型 --- list, tuple, range。
locals()
更新並返回表示當前本地符號表的字典。 在函數代碼塊但不是類代碼塊中調用 locals() 時將返回自由變量。 請注意在模塊層級上,locals() 和 globals() 是同一個字典。
備注
不要更改此字典的內容;更改不會影響解釋器使用的局部變量或自由變量的值。
map(function, iterable, ...)
返回一個將 function 應用於 iterable 中每一項並輸出其結果的迭代器。 如果傳入了額外的 iterable 參數,function 必須接受相同個數的實參並被應用於從所有可迭代對象中並行獲取的項。 當有多個可迭代對象時,最短的可迭代對象耗盡則整個迭代就將結束。 對於函數的輸入已經是參數元組的情況,請參閱 itertools.starmap()。
max(iterable, *[, key, default])
max(arg1, arg2, *args[, key])
返回可迭代對象中最大的元素,或者返回兩個及以上實參中最大的。
如果只提供了一個位置參數,它必須是非空 iterable,返回可迭代對象中最大的元素;如果提供了兩個及以上的位置參數,則返回最大的位置參數。
有兩個可選只能用關鍵字的實參。key 實參指定排序函數用的參數,如傳給 list.sort() 的。default 實參是當可迭代對象為空時返回的值。如果可迭代對象為空,並且沒有給 default ,則會觸發 ValueError。
如果有多個最大元素,則此函數將返回第一個找到的。這和其他穩定排序工具如
sorted(iterable, key=keyfunc, reverse=True)[0]
和heapq.nlargest(1, iterable, key=keyfunc)
保持一致。3.4 新版功能: keyword-only 實參 default 。
在 3.8 版更改: key 可以為
None
。
class memoryview(object)
返回由給定實參創建的“內存視圖”對象。有關詳細信息,請參閱 內存視圖。
min(iterable, *[, key, default])
min(arg1, arg2, *args[, key])
返回可迭代對象中最小的元素,或者返回兩個及以上實參中最小的。
如果只提供了一個位置參數,它必須是 iterable,返回可迭代對象中最小的元素;如果提供了兩個及以上的位置參數,則返回最小的位置參數。
有兩個可選只能用關鍵字的實參。key 實參指定排序函數用的參數,如傳給 list.sort() 的。default 實參是當可迭代對象為空時返回的值。如果可迭代對象為空,並且沒有給 default ,則會觸發 ValueError。
如果有多個最小元素,則此函數將返回第一個找到的。這和其他穩定排序工具如
sorted(iterable, key=keyfunc)[0]
和heapq.nsmallest(1, iterable, key=keyfunc)
保持一致。3.4 新版功能: keyword-only 實參 default 。
在 3.8 版更改: key 可以為
None
。
next(iterator[, default])
通過調用 iterator 的 __next__() 方法獲取下一個元素。如果迭代器耗盡,則返回給定的 default,如果沒有默認值則觸發 StopIteration。
class object
返回一個不帶特征的新對象。object 是所有類的基類。它帶有所有 Python 類實例均通用的方法。本函數不接受任何參數。
備注
由於 object 沒有 __dict__,因此無法將任意屬性賦給 object 的實例。
oct(x)
將一個整數轉變為一個前綴為“0o”的八進制字符串。結果是一個合法的 Python 表達式。如果 x 不是 Python 的 int 對象,那它需要定義
__index__()
方法返回一個整數。一些例子:>>>
>>> oct(8) '0o10' >>> oct(-56) '-0o70'若要將整數轉換為八進制字符串,並可選擇是否帶有“0o”前綴,可采用如下方法:
>>>
>>> '%#o' % 10, '%o' % 10 ('0o12', '12') >>> format(10, '#o'), format(10, 'o') ('0o12', '12') >>> f'{10:#o}', f'{10:o}' ('0o12', '12')另見 format() 獲取更多信息。
open(file, mode='r', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
打開 file 並返回對應的 file object。 如果該文件不能被打開,則引發 OSError。 請參閱 讀寫文件 獲取此函數的更多用法示例。
file 是一個 path-like object,表示將要打開的文件的路徑(絕對路徑或者相對當前工作目錄的路徑),也可以是要封裝文件對應的整數類型文件描述符。(如果給出的是文件描述符,則當返回的 I/O 對象關閉時它也會關閉,除非將 closefd 設為
False
。)mode is an optional string that specifies the mode in which the file is opened. It defaults to
'r'
which means open for reading in text mode. Other common values are'w'
for writing (truncating the file if it already exists),'x'
for exclusive creation, and'a'
for appending (which on some Unix systems, means that all writes append to the end of the file regardless of the current seek position). In text mode, if encoding is not specified the encoding used is platform-dependent: locale.getencoding() is called to get the current locale encoding. (For reading and writing raw bytes use binary mode and leave encoding unspecified.) The available modes are:字符
含意
'r'
讀取(默認)
'w'
寫入,並先截斷文件
'x'
排它性創建,如果文件已存在則失敗
'a'
打開文件用於寫入,如果文件存在則在末尾追加
'b'
二進制模式
't'
文本模式(默認)
'+'
打開用於更新(讀取與寫入)
默認模式為
'r'
(打開文件用於讀取文本,與'rt'
同義)。'w+'
和'w+b'
模式將打開文件並清空內容。而'r+'
和'r+b'
模式將打開文件但不清空內容。正如在 概述 中提到的,Python區分二進制和文本I/O。以二進制模式打開的文件(包括 mode 參數中的
'b'
)返回的內容為 bytes 對象,不進行任何解碼。在文本模式下(默認情況下,或者在 mode 參數中包含't'
)時,文件內容返回為 str ,首先使用指定的 encoding (如果給定)或者使用平台默認的的字節編碼解碼。備注
Python不依賴於底層操作系統的文本文件概念;所有處理都由Python本身完成,因此與平台無關。
buffering 是一個可選的整數,用於設置緩沖策略。 傳入 0 來關閉緩沖(只允許在二進制模式下),傳入 1 來選擇行緩沖(只在文本模式下可用),傳入一個整數 > 1 來表示固定大小的塊緩沖區的字節大小。注意,這樣指定緩沖區的大小適用於二進制緩沖的 I/O ,但
TextIOWrapper
(即用mode='r+'
打開的文件)會有另一種緩沖。要禁用在TextIOWrapper
緩沖,考慮使用 io.TextIOWrapper.reconfigure() 的write_through
標志來。當沒有給出 buffering 參數時,默認的緩沖策略工作如下。
二進制文件以固定大小的塊進行緩沖;使用啟發式方法選擇緩沖區的大小,嘗試確定底層設備的“塊大小”或使用 io.DEFAULT_BUFFER_SIZE。在許多系統上,緩沖區的長度通常為4096或8192字節。
“交互式”文本文件( isatty() 返回
True
的文件)使用行緩沖。其他文本文件使用上述策略用於二進制文件。encoding is the name of the encoding used to decode or encode the file. This should only be used in text mode. The default encoding is platform dependent (whatever locale.getencoding() returns), but any text encoding supported by Python can be used. See the codecs module for the list of supported encodings.
errors 是一個可選的字符串參數,用於指定如何處理編碼和解碼錯誤 - 這不能在二進制模式下使用。可以使用各種標准錯誤處理程序(列在 錯誤處理方案 ),但是使用 codecs.register_error() 注冊的任何錯誤處理名稱也是有效的。標准名稱包括:
如果存在編碼錯誤,
'strict'
會引發 ValueError 異常。 默認值None
具有相同的效果。
'ignore'
忽略錯誤。請注意,忽略編碼錯誤可能會導致數據丟失。
'replace'
會將替換標記(例如'?'
)插入有錯誤數據的地方。
'surrogateescape'
將把任何不正確的字節表示為 U+DC80 至 U+DCFF 范圍內的下方替代碼位。 當在寫入數據時使用surrogateescape
錯誤處理句柄時這些替代碼位會被轉回到相同的字節。 這適用於處理具有未知編碼格式的文件。只有在寫入文件時才支持
'xmlcharrefreplace'
。編碼不支持的字符將替換為相應的XML字符引用&#nnn;
。
'backslashreplace'
用Python的反向轉義序列替換格式錯誤的數據。
'namereplace'
(也只在編寫時支持)用\N{...}
轉義序列替換不支持的字符。newline 控制 universal newlines 模式如何生效(它僅適用於文本模式)。它可以是
None
,''
,'\n'
,'\r'
和'\r\n'
。它的工作原理:
從流中讀取輸入時,如果 newline 為
None
,則啟用通用換行模式。輸入中的行可以以'\n'
,'\r'
或'\r\n'
結尾,這些行被翻譯成'\n'
在返回呼叫者之前。如果它是''
,則啟用通用換行模式,但行結尾將返回給調用者未翻譯。如果它具有任何其他合法值,則輸入行僅由給定字符串終止,並且行結尾將返回給未調用的調用者。將輸出寫入流時,如果 newline 為
None
,則寫入的任何'\n'
字符都將轉換為系統默認行分隔符 os.linesep。如果 newline 是''
或'\n'
,則不進行翻譯。如果 newline 是任何其他合法值,則寫入的任何'\n'
字符將被轉換為給定的字符串。如果 closefd 為
False
且給出的不是文件名而是文件描述符,那麼當文件關閉時,底層文件描述符將保持打開狀態。如果給出的是文件名,則 closefd 必須為True
(默認值),否則將觸發錯誤。可以通過傳遞可調用的 opener 來使用自定義開啟器。然後通過使用參數( file,flags )調用 opener 獲得文件對象的基礎文件描述符。 opener 必須返回一個打開的文件描述符(使用 os.open as opener 時與傳遞
None
的效果相同)。新創建的文件是 不可繼承的。
下面的示例使用 os.open() 函數的 dir_fd 的形參,從給定的目錄中用相對路徑打開文件:
>>>
>>> import os >>> dir_fd = os.open('somedir', os.O_RDONLY) >>> def opener(path, flags): ... return os.open(path, flags, dir_fd=dir_fd) ... >>> with open('spamspam.txt', 'w', opener=opener) as f: ... print('This will be written to somedir/spamspam.txt', file=f) ... >>> os.close(dir_fd) # don't leak a file descriptoropen() 函數所返回的 file object 類型取決於所用模式。 當使用 open() 以文本模式 (
'w'
,'r'
,'wt'
,'rt'
等) 打開文件時,它將返回 io.TextIOBase (具體為 io.TextIOWrapper) 的一個子類。 當使用緩沖以二進制模式打開文件時,返回的類是 io.BufferedIOBase 的一個子類。 具體的類會有多種:在只讀的二進制模式下,它將返回 io.BufferedReader;在寫入二進制和追加二進制模式下,它將返回 io.BufferedWriter,而在讀/寫模式下,它將返回 io.BufferedRandom。 當禁用緩沖時,則會返回原始流,即 io.RawIOBase 的一個子類 io.FileIO。另請參閱文件操作模塊,如 fileinput、io (聲明了 open())、os、os.path、tempfile 和 shutil。
引發一個 審計事件
open
附帶參數file
,mode
,flags
。
mode
與flags
參數可以在原始調用的基礎上被修改或傳遞。在 3.3 版更改:
增加了 opener 形參。
增加了
'x'
模式。過去觸發的 IOError,現在是 OSError 的別名。
如果文件已存在但使用了排它性創建模式(
'x'
),現在會觸發 FileExistsError。在 3.4 版更改:
文件現在禁止繼承。
在 3.5 版更改:
如果系統調用被中斷,但信號處理程序沒有觸發異常,此函數現在會重試系統調用,而不是觸發 InterruptedError 異常 (原因詳見 PEP 475)。
增加了
'namereplace'
錯誤處理接口。在 3.6 版更改:
增加對實現了 os.PathLike 對象的支持。
在 Windows 上,打開一個控制台緩沖區將返回 io.RawIOBase 的子類,而不是 io.FileIO。
在 3.11 版更改: The
'U'
mode has been removed.
ord(c)
對表示單個 Unicode 字符的字符串,返回代表它 Unicode 碼點的整數。例如
ord('a')
返回整數97
,ord('€')
(歐元符號)返回8364
。這是 chr() 的逆函數。
pow(base, exp[, mod])
返回 base 的 exp 次冪;如果 mod 存在,則返回 base 的 exp 次冪對 mod 取余(比
pow(base, exp) % mod
更高效)。 兩參數形式pow(base, exp)
等價於乘方運算符:base**exp
。參數必須為數值類型。 對於混用的操作數類型,則適用二元算術運算符的類型強制轉換規則。 對於 int 操作數,結果具有與操作數相同的類型(轉換後),除非第二個參數為負值;在這種情況下,所有參數將被轉換為浮點數並輸出浮點數結果。 例如,
pow(10, 2)
返回100
,但pow(10, -2)
返回0.01
。 對於 int 或 float 類型的負基和一個非整數的指數,會產生一個復雜的結果。 例如,pow(-9, 0.5)
返回一個接近於3j
的值。對於 int 操作數 base 和 exp,如果給出 mod,則 mod 必須為整數類型並且 mod 必須不為零。 如果給出 mod 並且 exp 為負值,則 base 必須相對於 mod 不可整除。 在這種情況下,將會返回
pow(inv_base, -exp, mod)
,其中 inv_base 為 base 的倒數對 mod 取余。下面的例子是
38
的倒數對97
取余:>>>
>>> pow(38, -1, mod=97) 23 >>> 23 * 38 % 97 == 1 True在 3.8 版更改: 對於 int 操作數,三參數形式的
pow
現在允許第二個參數為負值,即可以計算倒數的余數。在 3.8 版更改: 允許關鍵字參數。 之前只支持位置參數。
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
將 objects 打印輸出至 file 指定的文本流,以 sep 分隔並在末尾加上 end。 sep 、 end 、 file 和 flush 必須以關鍵字參數的形式給出。
所有非關鍵字參數都會被轉換為字符串,就像是執行了 str() 一樣,並會被寫入到流,以 sep 且在末尾加上 end。 sep 和 end 都必須為字符串;它們也可以為
None
,這意味著使用默認值。 如果沒有給出 objects,則 print() 將只寫入 end。file 參數必須是一個具有
write(string)
方法的對象;如果參數不存在或為None
,則將使用 sys.stdout。 由於要打印的參數會被轉換為文本字符串,因此 print() 不能用於二進制模式的文件對象。 對於這些對象,應改用file.write(...)
。輸出是否緩存通常取決於 file,但如果 flush 關鍵字參數為 True,輸出流會被強制刷新。
在 3.3 版更改: 增加了 flush 關鍵字參數。
class property(fget=None, fset=None, fdel=None, doc=None)
返回 property 屬性。
fget 是獲取屬性值的函數。 fset 是用於設置屬性值的函數。 fdel 是用於刪除屬性值的函數。並且 doc 為屬性對象創建文檔字符串。
一個典型的用法是定義一個托管屬性
x
:class C: def __init__(self): self._x = None def getx(self): return self._x def setx(self, value): self._x = value def delx(self): del self._x x = property(getx, setx, delx, "I'm the 'x' property.")如果 c 為 C 的實例,
c.x
將調用 getter,c.x = value
將調用 setter,del c.x
將調用 deleter。如果給出,doc 將成為該 property 屬性的文檔字符串。 否則該 property 將拷貝 fget 的文檔字符串(如果存在)。 這令使用 property() 作為 decorator 來創建只讀的特征屬性可以很容易地實現:
class Parrot: def __init__(self): self._voltage = 100000 @property def voltage(self): """Get the current voltage.""" return self._voltage以上
@property
裝飾器會將voltage()
方法轉化為一個具有相同名稱的只讀屬性的 "getter",並將 voltage 的文檔字符串設置為 "Get the current voltage."特征屬性對象具有
getter
,setter
以及deleter
方法,它們可用作裝飾器來創建該特征屬性的副本,並將相應的訪問函數設為所裝飾的函數。 這最好是用一個例子來解釋:class C: def __init__(self): self._x = None @property def x(self): """I'm the 'x' property.""" return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x上述代碼與第一個例子完全等價。 注意一定要給附加函數與原始的特征屬性相同的名稱 (在本例中為
x
。)返回的特征屬性對象同樣具有與構造器參數相對應的屬性
fget
,fset
和fdel
。在 3.5 版更改: 特征屬性對象的文檔字符串現在是可寫的。
class range(stop)
class range(start, stop[, step])
雖然被稱為函數,但 range 實際上是一個不可變的序列類型,參見在 range 對象 與 序列類型 --- list, tuple, range 中的文檔說明。
repr(object)
返回對象的可打印形式字符串。對於很多類型而言,本函數試圖返回的字符串,會與將對象傳給 eval() 所生成的結果相同;不然,結果就是一個尖括號包裹的字符串,包含了對象類型名稱及其附加信息,附加信息通常包括對象的名稱和內存地址。通過定義
__repr__()
方法,類可以控制本函數將為實例返回的內容。
reversed(seq)
返回一個反向的 iterator。 seq 必須是一個具有
__reversed__()
方法的對象或者是支持該序列協議(具有從0
開始的整數類型參數的__len__()
方法和__getitem__()
方法)。
round(number[, ndigits])
返回 number 捨入到小數點後 ndigits 位精度的值。 如果 ndigits 被省略或為
None
,則返回最接近輸入值的整數。對於支持 round() 方法的內置類型,結果值會捨入至最接近的 10 的負 ndigits 次冪的倍數;如果與兩個倍數同樣接近,則選用偶數。因此,
round(0.5)
和round(-0.5)
均得出0
而round(1.5)
則為2
。ndigits 可為任意整數值(正數、零或負數)。如果省略了 ndigits 或為None
,則返回值將為整數。否則返回值與 number 的類型相同。對於一般的 Python 對象
number
,round
將委托給number.__round__
。備注
對浮點數執行 round() 的行為可能會令人驚訝:例如,
round(2.675, 2)
將給出2.67
而不是期望的2.68
。 這不算是程序錯誤:這一結果是由於大多數十進制小數實際上都不能以浮點數精確地表示。 請參閱 浮點算術:爭議和限制 了解更多信息。
class set([iterable])
返回一個新的 set 對象,可以選擇帶有從 iterable 獲取的元素。
set
是一個內置類型。 請查看 set 和 集合類型 --- set, frozenset 獲取關於這個類的文檔。有關其他容器請參看內置的 frozenset, list, tuple 和 dict 類,以及 collections 模塊。
setattr(object, name, value)
本函數與 getattr() 相對應。其參數為一個對象、一個字符串和一個任意值。字符串可以為某現有屬性的名稱,或為新屬性。只要對象允許,函數會將值賦給屬性。如
setattr(x, 'foobar', 123)
等價於x.foobar = 123
。備注
由於 私有名稱混合 發生在編譯時,因此必須手動混合私有屬性(以兩個下劃線打頭的屬性)名稱以便使用 setattr() 來設置它。
class slice(stop)
class slice(start, stop[, step])
返回一個 slice 對象,代表由
range(start, stop, step)
指定索引集的切片。 其中參數 start 和 step 的默認值為None
。切片對象具有只讀數據屬性start
、stop
和step
,只是返回對應的參數值(或默認值)。這幾個屬性沒有其他明確的功能;不過 NumPy 和其他第三方擴展會用到。在使用擴展索引語法時,也會生成切片對象。例如:a[start:stop:step]
或a[start:stop, i]
。 另一種方案是返回迭代器對象,可參閱 itertools.islice() 。
sorted(iterable, /, *, key=None, reverse=False)
根據 iterable 中的項返回一個新的已排序列表。
具有兩個可選參數,它們都必須指定為關鍵字參數。
key 指定帶有單個參數的函數,用於從 iterable 的每個元素中提取用於比較的鍵 (例如
key=str.lower
)。 默認值為None
(直接比較元素)。reverse 為一個布爾值。 如果設為
True
,則每個列表元素將按反向順序比較進行排序。使用 functools.cmp_to_key() 可將老式的 cmp 函數轉換為 key 函數。
內置的 sorted() 確保是穩定的。 如果一個排序確保不會改變比較結果相等的元素的相對順序就稱其為穩定的 --- 這有利於進行多重排序(例如先按部門、再按薪級排序)。
排序算法只使用
<
在項目之間比較。 雖然定義一個 __lt__() 方法就足以進行排序,但 PEP 8 建議實現所有六個 富比較 。 這將有助於避免在與其他排序工具(如 max() )使用相同的數據時出現錯誤,這些工具依賴於不同的底層方法。實現所有六個比較也有助於避免混合類型比較的混亂,因為混合類型比較可以調用反射到 __gt__() 的方法。有關排序示例和簡要排序教程,請參閱 排序指南 。
@staticmethod
將方法轉換為靜態方法。
靜態方法不會接收隱式的第一個參數。要聲明一個靜態方法,請使用此語法
class C: @staticmethod def f(arg1, arg2, ...): ...
@staticmethod
這樣的形式稱為函數的 decorator -- 詳情參閱 函數定義。靜態方法既可以由類中調用(如
C.f()
),也可以由實例中調用(如```C().f()``)。此外,還可以作為普通的函數進行調用(如``f()``)。Python 的靜態方法與 Java 或 C++ 類似。另請參閱 classmethod() ,可用於創建另一種類構造函數。
像所有裝飾器一樣,也可以像常規函數一樣調用
staticmethod
,並對其結果執行某些操作。比如某些情況下需要從類主體引用函數並且您希望避免自動轉換為實例方法。對於這些情況,請使用此語法:def regular_function(): ... class C: method = staticmethod(regular_function)
想了解更多有關靜態方法的信息,請參閱 標准類型層級結構 。
在 3.10 版更改: 靜態方法繼承了方法的多個屬性(
__module__
、__name__
、__qualname__
、__doc__
和__annotations__
),還擁有一個新的``__wrapped__`` 屬性,並且現在還可以作為普通函數進行調用。
class str(object='')
class str(object=b'', encoding='utf-8', errors='strict')
返回一個 str 版本的 object 。有關詳細信息,請參閱 str() 。
str
是內置字符串 class 。更多關於字符串的信息查看 文本序列類型 --- str。
sum(iterable, /, start=0)
從 start 開始自左向右對 iterable 的項求和並返回總計值。 iterable 的項通常為數字,而 start 值則不允許為字符串。
對某些用例來說,存在 sum() 的更好替代。 拼接字符串序列的更好更快方式是調用
''.join(sequence)
。 要以擴展精度對浮點值求和,請參閱 math.fsum()。 要拼接一系列可迭代對象,請考慮使用 itertools.chain()。在 3.8 版更改: start 形參可用關鍵字參數形式來指定。
class super([type[, object-or-type]])
返回一個代理對象,它會將方法調用委托給 type 的父類或兄弟類。 這對於訪問已在類中被重載的繼承方法很有用。
object-or-type 確定用於搜索的 method resolution order。 搜索會從 type 之後的類開始。
舉例來說,如果 object-or-type 的 __mro__ 為
D -> B -> C -> A -> object
並且 type 的值為B
,則 super() 將會搜索C -> A -> object
。object-or-type 的 __mro__ 屬性列出了 getattr() 和 super() 所共同使用的方法解析搜索順序。 該屬性是動態的,可以在任何繼承層級結構發生更新的時候被改變。
如果省略第二個參數,則返回的超類對象是未綁定的。 如果第二個參數為一個對象,則
isinstance(obj, type)
必須為真值。 如果第二個參數為一個類型,則issubclass(type2, type)
必須為真值(這適用於類方法)。super 有兩個典型用例。 在具有單繼承的類層級結構中,super 可用來引用父類而不必顯式地指定它們的名稱,從而令代碼更易維護。 這種用法與其他編程語言中 super 的用法非常相似。
第二個用例是在動態執行環境中支持協作多重繼承。 此用例為 Python 所獨有而不存在於靜態編碼語言或僅支持單繼承的語言當中。 這使用實現“菱形圖”成為可能,即有多個基類實現相同的方法。 好的設計強制要求這樣的方法在每個情況下都具有相同的調用簽名(因為調用順序是在運行時確定的,也因為這個順序要適應類層級結構的更改,還因為這個順序可能包括在運行時之前未知的兄弟類)。
對於以上兩個用例,典型的超類調用看起來是這樣的:
class C(B): def method(self, arg): super().method(arg) # This does the same thing as: # super(C, self).method(arg)
除了方法查找之外,super() 也可用於屬性查找。 一個可能的應用場合是在上級或同級類中調用 描述器。
請注意 super() 是作為顯式加點屬性查找的綁定過程的一部分來實現的,例如
super().__getitem__(name)
。 它做到這一點是通過實現自己的__getattribute__()
方法,這樣就能以可預測的順序搜索類,並且支持協作多重繼承。 對應地,super() 在像super()[name]
這樣使用語句或操作符進行隱式查找時則未被定義。還要注意的是,除了零個參數的形式以外,super() 並不限於在方法內部使用。 兩個參數的形式明確指定參數並進行相應的引用。 零個參數的形式僅適用於類定義內部,因為編譯器需要填入必要的細節以正確地檢索到被定義的類,還需要讓普通方法訪問當前實例。
對於有關如何使用 super() 來如何設計協作類的實用建議,請參閱 使用 super() 的指南。
class tuple([iterable])
雖然被稱為函數,但 tuple 實際上是一個不可變的序列類型,參見在 元組 與 序列類型 --- list, tuple, range 中的文檔說明。
class type(object)
class type(name, bases, dict, **kwds)
傳入一個參數時,返回 object 的類型。 返回值是一個 type 對象,通常與 object.__class__ 所返回的對象相同。
推薦使用 isinstance() 內置函數來檢測對象的類型,因為它會考慮子類的情況。
傳入三個參數時,返回一個新的 type 對象。 這在本質上是 class 語句的一種動態形式,name 字符串即類名並會成為 __name__ 屬性;bases 元組包含基類並會成為 __bases__ 屬性;如果為空則會添加所有類的終極基類 object。 dict 字典包含類主體的屬性和方法定義;它在成為 __dict__ 屬性之前可能會被拷貝或包裝。 下面兩條語句會創建相同的 type 對象:
>>> >>> class X: ... a = 1 ... >>> X = type('X', (), dict(a=1))
另請參閱 類型對象。
提供給三參數形式的關鍵字參數會被傳遞給適當的元類機制 (通常為 __init_subclass__()),相當於類定義中關鍵字 (除了 metaclass) 的行為方式。
另請參閱 自定義類創建。
在 3.6 版更改: type 的子類如果未重載
type.__new__
,將不再能使用一個參數的形式來獲取對象的類型。
vars([object])
返回模塊、類、實例或任何其它具有 __dict__ 屬性的對象的 __dict__ 屬性。
模塊和實例這樣的對象具有可更新的 __dict__ 屬性;但是,其它對象的 __dict__ 屬性可能會設為限制寫入(例如,類會使用 types.MappingProxyType 來防止直接更新字典)。
不帶參數時,vars() 的行為類似 locals()。 請注意,locals 字典僅對於讀取起作用,因為對 locals 字典的更新會被忽略。
如果指定了一個對象但它沒有 __dict__ 屬性(例如,當它所屬的類定義了 __slots__ 屬性時)則會引發 TypeError 異常。
zip(*iterables, strict=False)
在多個迭代器上並行迭代,從每個迭代器返回一個數據項組成元組。
示例:
>>>
>>> for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']): ... print(item) ... (1, 'sugar') (2, 'spice') (3, 'everything nice')
更正式的說法: zip() 返回元組的迭代器,其中第 i 個元組包含的是每個參數迭代器的第 i 個元素。
不妨換一種方式認識 zip() :它會把行變成列,把列變成行。這類似於 矩陣轉置 。
zip() 是延遲執行的:直至迭代時才會對元素進行處理,比如
for
循環或放入 list 中。值得考慮的是,傳給 zip() 的可迭代對象可能長度不同;有時是有意為之,有時是因為准備這些對象的代碼存在錯誤。Python 提供了三種不同的處理方案:
默認情況下,zip() 在最短的迭代完成後停止。較長可迭代對象中的剩余項將被忽略,結果會裁切至最短可迭代對象的長度:
>>> >>> list(zip(range(3), ['fee', 'fi', 'fo', 'fum'])) [(0, 'fee'), (1, 'fi'), (2, 'fo')]
通常 zip() 用於可迭代對象等長的情況下。這時建議用
strict=True
的選項。輸出與普通的 zip() 相同:。>>> >>> list(zip(('a', 'b', 'c'), (1, 2, 3), strict=True)) [('a', 1), ('b', 2), ('c', 3)]
與默認行為不同的是,它會檢查可迭代對象的長度是否相同,如果不相同則觸發 ValueError 。
>>> >>> list(zip(range(3), ['fee', 'fi', 'fo', 'fum'], strict=True)) Traceback (most recent call last): ... ValueError: zip() argument 2 is longer than argument 1
如果未指定
strict=True
參數,所有導致可迭代對象長度不同的錯誤都會被抑制,這可能會在程序的其他地方表現為難以發現的錯誤。為了讓所有的可迭代對象具有相同的長度,長度較短的可用常量進行填充。這可由 itertools.zip_longest() 來完成。
極端例子是只有一個可迭代對象參數,zip() 會返回一個一元組的迭代器。如果未給出參數,則返回一個空的迭代器。
小技巧:
可確保迭代器的求值順序是從左到右的。這樣就能用
zip(*[iter(s)]*n, strict=True)
將數據列表按長度 n 進行分組。這將重復 相同 的迭代器n
次,輸出的每個元組都包含n
次調用迭代器的結果。這樣做的效果是把輸入拆分為長度為 n 的塊。zip() 與
*
運算符相結合可以用來拆解一個列表:>>> >>> x = [1, 2, 3] >>> y = [4, 5, 6] >>> list(zip(x, y)) [(1, 4), (2, 5), (3, 6)] >>> x2, y2 = zip(*zip(x, y)) >>> x == list(x2) and y == list(y2) True
在 3.10 版更改: 增加了
strict
參數。
__import__(name, globals=None, locals=None, fromlist=(), level=0)
備注
與 importlib.import_module() 不同,這是一個日常 Python 編程中不需要用到的高級函數。
此函數會由 import 語句發起調用。 它可以被替換 (通過導入 builtins 模塊並賦值給
builtins.__import__
) 以便修改import
語句的語義,但是 強烈 不建議這樣做,因為使用導入鉤子 (參見 PEP 302) 通常更容易實現同樣的目標,並且不會導致代碼問題,因為許多代碼都會假定所用的是默認實現。 同樣也不建議直接使用 __import__() 而應該用 importlib.import_module()。本函數會導入模塊 name,利用 globals 和 locals 來決定如何在包的上下文中解釋該名稱。fromlist 給出了應從 name 模塊中導入的對象或子模塊的名稱。標准的實現代碼完全不會用到 locals 參數,只用到了 globals 用於確定 import 語句所在的包上下文。
level 指定是使用絕對還是相對導入。
0
(默認值) 意味著僅執行絕對導入。 level 為正數值表示相對於模塊調用 __import__() 的目錄,將要搜索的父目錄層數 (詳情參見 PEP 328)。當 name 變量的形式為
package.module
時,通常將會返回最高層級的包(第一個點號之前的名稱),而 不是 以 name 命名的模塊。 但是,當給出了非空的 fromlist 參數時,則將返回以 name 命名的模塊。例如,語句
import spam
的結果將為與以下代碼作用相同的字節碼:spam = __import__('spam', globals(), locals(), [], 0)語句
import spam.ham
的結果將為以下調用:spam = __import__('spam.ham', globals(), locals(), [], 0)請注意在這裡 __import__() 是如何返回頂層模塊的,因為這是通過 import 語句被綁定到特定名稱的對象。
另一方面,語句
from spam.ham import eggs, sausage as saus
的結果將為_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0) eggs = _temp.eggs saus = _temp.sausage在這裡,
spam.ham
模塊會由 __import__() 返回。 要導入的對象將從此對象中提取並賦值給它們對應的名稱。如果您只想按名稱導入模塊(可能在包中),請使用 importlib.import_module()
在 3.3 版更改: level 的值不再支持負數(默認值也修改為0)。
在 3.9 版更改: 當使用了命令行參數 -E 或 -I 時,環境變量 PYTHONCASEOK 現在將被忽略。
下面的例子以是否顯示提示符(>>">>>> 與 ...)區分輸入與輸出:輸入例子中的代碼時,要鍵入以提示符開頭的行中提示符後的所有內容;未以提示符開頭的行是解釋器的輸出。注意,例子中的某行出現的第二個提示符是用來結束多行命令的,此時,要鍵入一個空白行。
你可以通過在示例方塊右上角的 >>>
上點擊來切換顯示提示符和輸出。 如果你隱藏了一個示例的提示符和輸出,那麼你可以方便地將輸入行復制並粘貼到你的解釋器中。
本手冊中的許多例子,甚至交互式命令都包含注釋。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."
現在,嘗試一些簡單的 Python 命令。啟動解釋器,等待主提示符(>>>
)出現。
解釋器像一個簡單的計算器:輸入表達式,就會給出答案。表達式的語法很直接:運算符 +
、-
、*
、/
的用法和其他大部分語言一樣(比如,Pascal 或 C);括號 (()
) 用來分組。例如:
>>>
>>> 2 + 2 4 >>> 50 - 5*6 20 >>> (50 - 5*6) / 4 5.0 >>> 8 / 5 # division always returns a floating point number 1.6
整數(如,2
、4
、20
)的類型是 int,帶小數(如,5.0
、1.6
)的類型是 float。本教程後半部分將介紹更多數字類型。
除法運算(/
)返回浮點數。用 //
運算符執行 floor 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 # floored quotient * 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
Python 全面支持浮點數;混合類型運算數的運算會把整數轉換為浮點數:
>>>
>>> 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 還支持其他數字類型,例如 Decimal 或 Fraction。Python 還內置支持 復數,後綴 j
或 J
用於表示虛數(例如 3+5j
)。
除了數字,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
字符串字面值可以包含多行。 一種實現方式是使用三重引號:"""..."""
或 '''...'''
。 字符串中將自動包括行結束符,但也可以在換行的地方添加一個 \
來避免此情況。 參見以下示例:
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 File "<stdin>", line 1 prefix 'thon' ^ SyntaxError: invalid syntax >>> ('un' * 3) 'ium' File "<stdin>", line 1 ('un' * 3) 'ium' ^ SyntaxError: invalid syntax
合並多個變量,或合並變量與字面值,要用 +
:
>>>
>>> prefix + 'thon' 'Python'
字符串支持 索引 (下標訪問),第一個字符的索引是 0。單字符沒有專用的類型,就是長度為一的字符串:
>>>
>>> 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'
切片索引的默認值很有用;省略開始索引時,默認值為 0,省略結束索引時,默認為到字符串的結尾:
>>>
>>> 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'
注意,輸出結果包含切片開始,但不包含切片結束。因此,s[:i] + s[i:]
總是等於 s
:
>>>
>>> word[:2] + word[2:] 'Python' >>> word[:4] + word[4:] 'Python'
還可以這樣理解切片,索引指向的是字符 之間 ,第一個字符的左側標為 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 的切片由 i 和 j 之間所有對應的字符組成。
對於使用非負索引的切片,如果兩個索引都不越界,切片長度就是起止索引之差。例如, 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 字符串不能修改,是 immutable 的。因此,為字符串中某個索引位置賦值會報錯:
>>>
>>> word[0] = 'J' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' object does not support item assignment >>> word[2:] = 'py' Traceback (most recent call last): File "<stdin>", line 1, in <module> 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 風格的字符串格式化
這裡詳述了用 %
運算符格式化字符串的操作。
Python 支持多種 復合 數據類型,可將不同值組合在一起。最常用的 列表 ,是用方括號標注,逗號分隔的一組值。列表 可以包含不同類型的元素,但一般情況下,各個元素的類型相同:
>>>
>>> squares = [1, 4, 9, 16, 25] >>> squares [1, 4, 9, 16, 25]
和字符串(及其他內置 sequence 類型)一樣,列表也支持索引和切片:
>>>
>>> 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]
與 immutable 字符串不同, 列表是 mutable 類型,其內容可以改變:
>>>
>>> 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'
當然,Python 還可以完成比二加二更復雜的任務。 例如,可以編寫 斐波那契數列 的初始子序列,如下所示:
>>>
>>> # Fibonacci series: ... # the sum of two elements defines the next ... a, b = 0, 1 >>> while a < 10: ... print(a) ... a, b = b, a+b ... 0 1 1 2 3 5 8
本例引入了幾個新功能。
第一行中的 多重賦值:變量 a
和 b
同時獲得新值 0 和 1。最後一行又用了一次多重賦值,這體現在右表達式在賦值前就已經求值了。右表達式求值順序為從左到右。
while 循環只要條件(這裡指:a < 10
)保持為真就會一直執行。Python 和 C 一樣,任何非零整數都為真,零為假。這個條件也可以是字符串或列表的值,事實上,任何序列都可以;長度非零就為真,空序列則為假。示例中的判斷只是最簡單的比較。比較操作符的標准寫法和 C 語言一樣: <
(小於)、 >
(大於)、 ==
(等於)、 <=
(小於等於)、 >=
(大於等於)及 !=
(不等於)。
循環體 是 縮進的 :縮進是 Python 組織語句的方式。在交互式命令行裡,得為每個縮輸入制表符或空格。使用文本編輯器可以實現更復雜的輸入方式;所有像樣的文本編輯器都支持自動縮進。交互式輸入復合語句時, 要在最後輸入空白行表示結束(因為解析器不知道哪一行代碼是最後一行)。注意,同一塊語句的每一行的縮進相同。
print() 函數輸出給定參數的值。與表達式不同(比如,之前計算器的例子),它能處理多個參數,包括浮點數與字符串。它輸出的字符串不帶引號,且各參數項之間會插入一個空格,這樣可以實現更好的格式化操作:
>>>>>> i = 256*256 >>> print('The value of i is', i) The value of i is 65536
關鍵字參數 end 可以取消輸出後面的換行, 或用另一個字符串結尾:
>>>>>> a, b = 0, 1 >>> while a < 1000: ... print(a, end=',') ... a, b = b, a+b ... 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,
備注
1
**
比 -
的優先級更高, 所以 -3**2
會被解釋成 -(3**2)
,因此,結果是 -9
。要避免這個問題,並且得到 9
, 可以用 (-3)**2
。
2
和其他語言不一樣,特殊字符如 \n
在單引號('...'
)和雙引號("..."
)裡的意義一樣。這兩種引號唯一的區別是,不需要在單引號裡轉義雙引號 "
,但必須把單引號轉義成 \'
,反之亦然。
除了上一章介紹的 while 語句,Python 還支持其他語言中常見的流程控制語句,只是稍有不同。
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
if 語句包含零個或多個 elif 子句及可選的 else 子句。關鍵字 'elif
' 是 'else if' 的縮寫,適用於避免過多的縮進。if
... elif
... elif
... 序列可以當作其他語言中 switch
或 case
語句的替代品。
如果要把一個值與多個常量進行比較,或者檢查特定類型或屬性,match
語句更實用。詳見 match 語句。
for
語句Python 的 for 語句與 C 或 Pascal 中的不同。Python 的 for
語句不迭代算術遞增數值(如 Pascal),或是給予用戶定義迭代步驟和暫停條件的能力(如 C),而是迭代列表或字符串等任意序列,元素的迭代順序與在序列中出現的順序一致。 例如:
>>>
>>> # Measure some strings: ... words = ['cat', 'window', 'defenestrate'] >>> for w in words: ... print(w, len(w)) ... cat 3 window 6 defenestrate 12
遍歷集合時修改集合的內容,會很容易生成錯誤的結果。因此不能直接進行循環,而是應遍歷該集合的副本或創建新的集合:
# Create a sample collection users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'} # Strategy: Iterate over a copy for user, status in users.copy().items(): if status == 'inactive': del users[user] # Strategy: Create a new collection active_users = {} for user, status in users.items(): if status == 'active': active_users[user] = status
內置函數 range() 常用於遍歷數字序列,該函數可以生成算術級數:
>>>
>>> for i in range(5): ... print(i) ... 0 1 2 3 4
生成的序列不包含給定的終止數值;range(10)
生成 10 個值,這是一個長度為 10 的序列,其中的元素索引都是合法的。range 可以不從 0 開始,還可以按指定幅度遞增(遞增幅度稱為 '步進',支持負數):
>>>
>>> list(range(5, 10)) [5, 6, 7, 8, 9] >>> list(range(0, 10, 3)) [0, 3, 6, 9] >>> list(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() 函數更便捷,詳見 循環的技巧 。
如果只輸出 range,會出現意想不到的結果:
>>>
>>> range(10) range(0, 10)
range() 返回對象的操作和列表很像,但其實這兩種對象不是一回事。迭代時,該對象基於所需序列返回連續項,並沒有生成真正的列表,從而節省了空間。
這種對象稱為可迭代對象 iterable,函數或程序結構可通過該對象獲取連續項,直到所有元素全部迭代完畢。for 語句就是這樣的架構,sum() 是一種把可迭代對象作為參數的函數:
>>>
>>> sum(range(4)) # 0 + 1 + 2 + 3 6
下文將介紹更多返回可迭代對象或把可迭代對象當作參數的函數。 在 數據結構 這一章節中,我們將討論有關 list() 的更多細節。
break
、continue
語句及 else
子句break 語句和 C 中的類似,用於跳出最近的 for 或 while 循環。
循環語句支持 else
子句;for 循環中,可迭代對象中的元素全部循環完畢,或 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 語句。)
與 if 語句相比,循環的 else
子句更像 try 的 else
子句: 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 an odd number", num) ... Found an even number 2 Found an odd number 3 Found an even number 4 Found an odd number 5 Found an even number 6 Found an odd number 7 Found an even number 8 Found an odd number 9
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! ...
match
語句A match statement takes an expression and compares its value to successive patterns given as one or more case blocks. This is superficially similar to a switch statement in C, Java or JavaScript (and many other languages), but it's more similar to pattern matching in languages like Rust or Haskell. Only the first pattern that matches gets executed and it can also extract components (sequence elements or object attributes) from the value into variables.
最簡單的形式是將一個目標值與一個或多個字面值進行比較:
def http_error(status): match status: case 400: return "Bad request" case 404: return "Not found" case 418: return "I'm a teapot" case _: return "Something's wrong with the internet"
注意最後一個代碼塊:“變量名” _
被作為 通配符 並必定會匹配成功。 如果沒有 case 語句匹配成功,則不會執行任何分支。
使用 |
(“ or ”)在一個模式中可以組合多個字面值:
case 401 | 403 | 404: return "Not allowed"
模式的形式類似解包賦值,並可被用於綁定變量:
# point is an (x, y) tuple match point: case (0, 0): print("Origin") case (0, y): print(f"Y={y}") case (x, 0): print(f"X={x}") case (x, y): print(f"X={x}, Y={y}") case _: raise ValueError("Not a point")
請仔細研究此代碼! 第一個模式有兩個字面值,可以看作是上面所示字面值模式的擴展。但接下來的兩個模式結合了一個字面值和一個變量,而變量 綁定 了一個來自目標的值(point
)。第四個模式捕獲了兩個值,這使得它在概念上類似於解包賦值 (x, y) = point
。
如果使用類實現數據結構,可在類名後加一個類似於構造器的參數列表,這樣做可以把屬性放到變量裡:
class Point: x: int y: int def where_is(point): match point: case Point(x=0, y=0): print("Origin") case Point(x=0, y=y): print(f"Y={y}") case Point(x=x, y=0): print(f"X={x}") case Point(): print("Somewhere else") case _: print("Not a point")
可在 dataclass 等支持屬性排序的內置類中使用位置參數。還可在類中設置 __match_args__
特殊屬性為模式的屬性定義指定位置。如果它被設為 ("x", "y"),則以下模式均為等價的,並且都把 y
屬性綁定到 var
變量:
Point(1, var) Point(1, y=var) Point(x=1, y=var) Point(y=var, x=1)
讀取模式的推薦方式是將它們看做是你會在賦值操作左側放置的內容的擴展形式,以便理解各個變量將會被設置的值。 只有單獨的名稱(例如上面的 var
)會被 match 語句所賦值。 帶點號的名稱 (例如 foo.bar
)、屬性名稱(例如上面的 x=
和 y=
)或類名稱(通過其後的 "(...)" 來識別,例如上面的 Point
)都絕不會被賦值。
模式可以任意地嵌套。例如,如果有一個由點組成的短列表,則可使用如下方式進行匹配:
match points: case []: print("No points") case [Point(0, 0)]: print("The origin") case [Point(x, y)]: print(f"Single point {x}, {y}") case [Point(0, y1), Point(0, y2)]: print(f"Two on the Y axis at {y1}, {y2}") case _: print("Something else")
為模式添加成為守護項的 if
子句。如果守護項的值為假,則 match
繼續匹配下一個 case 語句塊。注意,值的捕獲發生在守護項被求值之前:
match point: case Point(x, y) if x == y: print(f"Y=X at {x}") case Point(x, y): print(f"Not on the diagonal")
match 語句的其他特性:
與解包賦值類似,元組和列表模式具有完全相同的含義,並且實際上能匹配任意序列。 但它們不能匹配迭代器或字符串。
序列模式支持擴展解包操作:[x, y, *rest]
和 (x, y, *rest)
的作用類似於解包賦值。 在 *
之後的名稱也可以為 _
,因此,(x, y, *_)
可以匹配包含至少兩個條目的序列,而不必綁定其余的條目。
映射模式:{"bandwidth": b, "latency": l}
從字典中捕獲 "bandwidth"
和 "latency"
的值。與序列模式不同,額外的鍵會被忽略。**rest
等解包操作也支持。但 **_
是冗余的,不允許使用。
使用 as
關鍵字可以捕獲子模式:
case (Point(x1, y1), Point(x2, y2) as p2): ...
將把輸入的第二個元素捕獲為 p2
(只要輸入是包含兩個點的序列)
大多數字面值是按相等性比較的,但是單例對象 True
, False
和 None
則是按標識號比較的。
模式可以使用命名常量。 這些命名常量必須為帶點號的名稱以防止它們被解讀為捕獲變量:
from enum import Enum class Color(Enum): RED = 'red' GREEN = 'green' BLUE = 'blue' color = Color(input("Enter your choice of 'red', 'blue' or 'green': ")) match color: case Color.RED: print("I see red!") case Color.GREEN: print("Grass is green") case Color.BLUE: print("I'm feeling the blues :(")
要獲取更詳細的說明和額外的示例,你可以參閱以教程格式撰寫的 PEP 636。
下列代碼創建一個可以輸出限定數值內的斐波那契數列函數:
>>>
>>> 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,詳見 文檔字符串。利用文檔字符串可以自動生成在線文檔或打印版文檔,還可以讓開發者在浏覽代碼時直接查閱文檔;Python 開發者最好養成在代碼中加入文檔字符串的好習慣。
函數在 執行 時使用函數局部變量符號表,所有函數變量賦值都存在局部符號表中;引用變量時,首先,在局部符號表裡查找變量,然後,是外層函數局部符號表,再是全局符號表,最後是內置名稱符號表。因此,盡管可以引用全局變量和外層函數的變量,但最好不要在函數內直接賦值(除非是 global 語句定義的全局變量,或 nonlocal 語句定義的外層函數變量)。
在調用函數時會將實際參數(實參)引入到被調用函數的局部符號表中;因此,實參是使用 按值調用 來傳遞的(其中的 值 始終是對象的 引用 而不是對象的值)。 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
編寫不直接輸出斐波那契數列運算結果,而是返回運算結果列表的函數也非常簡單:
>>>
>>> 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)
語句調用了列表對象 result
的 方法 。方法是“從屬於”對象的函數,命名為 obj.methodname
,obj
是對象(也可以是表達式),methodname
是對象類型定義的方法名。不同類型定義不同的方法,不同類型的方法名可以相同,且不會引起歧義。(用 類 可以自定義對象類型和方法,詳見 類 )示例中的方法 append()
是為列表對象定義的,用於在列表末尾添加新元素。本例中,該方法相當於 result = result + [a]
,但更有效。
函數定義支持可變數量的參數。這裡列出三種可以組合使用的形式。
為參數指定默認值是非常有用的方式。調用函數時,可以使用比定義時更少的參數,例如:
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
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
)和三個可選參數(state
, action
和 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 argument 'a'
最後一個形參為 **name
形式時,接收一個字典(詳見 映射類型 --- dict),該字典包含與函數中已定義形參對應之外的所有關鍵字參數。**name
形參可以與 *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
注意,關鍵字參數在輸出結果中的順序與調用函數時的順序一致。
默認情況下,參數可以按位置或顯式關鍵字傳遞給 Python 函數。為了讓代碼易讀、高效,最好限制參數的傳遞方式,這樣,開發者只需查看函數定義,即可確定參數項是僅按位置、按位置或關鍵字,還是僅按關鍵字傳遞。
函數定義如下:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): ----------- ---------- ---------- | | | | Positional or keyword | | - Keyword only -- Positional only
/
和 *
是可選的。這些符號表明形參如何把參數值傳遞給函數:位置、位置或關鍵字、關鍵字。關鍵字形參也叫作命名形參。
4.8.3.1. 位置或關鍵字參數
函數定義中未使用 /
和 *
時,參數可以按位置或關鍵字傳遞給函數。
4.8.3.2. 僅位置參數
此處再介紹一些細節,特定形參可以標記為 僅限位置。僅限位置 時,形參的順序很重要,且這些形參不能用關鍵字傳遞。僅限位置形參應放在 /
(正斜槓)前。/
用於在邏輯上分割僅限位置形參與其它形參。如果函數定義中沒有 /
,則表示沒有僅限位置形參。
/
後可以是 位置或關鍵字 或 僅限關鍵字 形參。
4.8.3.3. 僅限關鍵字參數
把形參標記為 僅限關鍵字,表明必須以關鍵字參數形式傳遞該形參,應在參數列表中第一個 僅限關鍵字 形參前添加 *
。
4.8.3.4. 函數示例
請看下面的函數定義示例,注意 /
和 *
標記:
>>>
>>> def standard_arg(arg): ... print(arg) ... >>> def pos_only_arg(arg, /): ... print(arg) ... >>> def kwd_only_arg(*, arg): ... print(arg) ... >>> def combined_example(pos_only, /, standard, *, kwd_only): ... print(pos_only, standard, kwd_only)
第一個函數定義 standard_arg
是最常見的形式,對調用方式沒有任何限制,可以按位置也可以按關鍵字傳遞參數:
>>>
>>> standard_arg(2) 2 >>> standard_arg(arg=2) 2
第二個函數 pos_only_arg
的函數定義中有 /
,僅限使用位置形參:
>>>
>>> pos_only_arg(1) 1 >>> pos_only_arg(arg=1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'
第三個函數 kwd_only_args
的函數定義通過 *
表明僅限關鍵字參數:
>>>
>>> kwd_only_arg(3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given >>> kwd_only_arg(arg=3) 3
最後一個函數在同一個函數定義中,使用了全部三種調用慣例:
>>>
>>> combined_example(1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: combined_example() takes 2 positional arguments but 3 were given >>> combined_example(1, 2, kwd_only=3) 1 2 3 >>> combined_example(1, standard=2, kwd_only=3) 1 2 3 >>> combined_example(pos_only=1, standard=2, kwd_only=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'
下面的函數定義中,kwds
把 name
當作鍵,因此,可能與位置參數 name
產生潛在沖突:
def foo(name, **kwds): return 'name' in kwds
調用該函數不可能返回 True
,因為關鍵字 'name'
總與第一個形參綁定。例如:
>>>
>>> foo(1, **{'name': 2}) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: foo() got multiple values for argument 'name' >>>
加上 /
(僅限位置參數)後,就可以了。此時,函數定義把 name
當作位置參數,'name'
也可以作為關鍵字參數的鍵:
def foo(name, /, **kwds): return 'name' in kwds >>> foo(1, **{'name': 2}) True
換句話說,僅限位置形參的名稱可以在 **kwds
中使用,而不產生歧義。
4.8.3.5. 小結
以下用例決定哪些形參可以用於函數定義:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
說明:
使用僅限位置形參,可以讓用戶無法使用形參名。形參名沒有實際意義時,強制調用函數的實參順序時,或同時接收位置形參和關鍵字時,這種方式很有用。
當形參名有實際意義,且顯式名稱可以讓函數定義更易理解時,阻止用戶依賴傳遞實參的位置時,才使用關鍵字。
對於 API,使用僅限位置形參,可以防止未來修改形參名時造成破壞性的 API 變動。
調用函數時,使用任意數量的實參是最少見的選項。這些實參包含在元組中(詳見 元組和序列 )。在可變數量的實參之前,可能有若干個普通參數:
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'
函數調用要求獨立的位置參數,但實參在列表或元組裡時,要執行相反的操作。例如,內置的 range() 函數要求獨立的 start 和 stop 實參。如果這些參數不是獨立的,則要在調用函數時,用 *
操作符把實參從列表或元組解包出來:
>>>
>>> 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]
同樣,字典可以用 **
操作符傳遞關鍵字參數:
>>>
>>> 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 !
lambda 關鍵字用於創建小巧的匿名函數。lambda a, b: a+b
函數返回兩個參數的和。Lambda 函數可用於任何需要函數對象的地方。在語法上,匿名函數只能是單個表達式。在語義上,它只是常規函數定義的語法糖。與嵌套函數定義一樣,lambda 函數可以引用包含作用域中的變量:
>>>
>>> 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')]
以下是文檔字符串內容和格式的約定。
第一行應為對象用途的簡短摘要。為保持簡潔,不要在這裡顯式說明對象名或類型,因為可通過其他方式獲取這些信息(除非該名稱碰巧是描述函數操作的動詞)。這一行應以大寫字母開頭,以句點結尾。
文檔字符串為多行時,第二行應為空白行,在視覺上將摘要與其余描述分開。後面的行可包含若干段落,描述對象的調用約定、副作用等。
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.
函數注解 是可選的用戶自定義函數類型的元數據完整信息(詳見 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'
本章深入講解之前學過的一些內容,同時,還增加了新的知識點。
列表數據類型支持很多方法,列表對象的所有方法所示如下:
list.append(x)
在列表末尾添加一個元素,相當於 a[len(a):] = [x]
。
list.extend(iterable)
用可迭代對象的元素擴展列表。相當於 a[len(a):] = iterable
。
list.insert(i, x)
在指定位置插入元素。第一個參數是插入元素的索引,因此,a.insert(0, x)
在列表開頭插入元素, a.insert(len(a), x)
等同於 a.append(x)
。
list.remove(x)
從列表中刪除第一個值為 x 的元素。未找到指定元素時,觸發 ValueError 異常。
list.pop([i])
刪除列表中指定位置的元素,並返回被刪除的元素。未指定位置時,a.pop()
刪除並返回列表的最後一個元素。(方法簽名中 i 兩邊的方括號表示該參數是可選的,不是要求輸入方括號。這種表示法常見於 Python 參考庫)。
list.clear()
刪除列表裡的所有元素,相當於 del a[:]
。
list.index(x[, start[, end]])
返回列表中第一個值為 x 的元素的零基索引。未找到指定元素時,觸發 ValueError 異常。
可選參數 start 和 end 是切片符號,用於將搜索限制為列表的特定子序列。返回的索引是相對於整個序列的開始計算的,而不是 start 參數。
list.count(x)
返回列表中元素 x 出現的次數。
list.sort(*, key=None, reverse=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'
insert
、remove
、sort
等方法只修改列表,不輸出返回值——返回的默認值為 None
。1 這是所有 Python 可變數據結構的設計原則。
還有,不是所有數據都可以排序或比較。例如,[None, 'hello', 10]
就不可排序,因為整數不能與字符串對比,而 None 不能與其他類型對比。有些類型根本就沒有定義順序關系,例如,3+4j < 5+7j
這種對比操作就是無效的。
使用列表方法實現堆棧非常容易,最後插入的最先取出(“後進先出”)。把元素添加到堆棧的頂端,使用 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]
列表也可以用作隊列,最先加入的元素,最先取出(“先進先出”);然而,列表作為隊列的效率很低。因為,在列表末尾添加和刪除元素非常快,但在列表開頭插入或移除元素卻很慢(因為所有其他元素都必須移動一位)。
實現隊列最好用 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'])
列表推導式創建列表的方式更簡潔。常見的用法為,對序列或可迭代對象中的每個元素應用某種操作,用生成的結果創建新的列表;或用滿足特定條件的元素創建子序列。
例如,創建平方值的列表:
>>>
>>> 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
子句,然後,是零個或多個 for
或 if
子句。結果是由表達式依據 for
和 if
子句求值計算而得出一個新列表。 舉例來說,以下列表推導式將兩個列表中不相等的元素組合起來:
>>>
>>> [(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)]
注意,上面兩段代碼中,for 和 if 的順序相同。
表達式是元組(例如上例的 (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']
列表推導式中的初始表達式可以是任何表達式,甚至可以是另一個列表推導式。
下面這個 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]]
As we saw in the previous section, the inner list comprehension is evaluated in the context of the for that follows it, so this example is equivalent to:
>>>
>>> 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)]
關於本行中星號的詳細說明,參見 解包實參列表。
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 的其他用法。
列表和字符串有很多共性,例如,索引和切片操作。這兩種數據類型是 序列 (參見 序列類型 --- list, tuple, range)。隨著 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])
輸出時,元組都要由圓括號標注,這樣才能正確地解釋嵌套元組。輸入時,圓括號可有可無,不過經常是必須的(如果元組是更大的表達式的一部分)。不允許為元組中的單個元素賦值,當然,可以創建含列表等可變對象的元組。
雖然,元組與列表很像,但使用場景不同,用途也不同。元組是 immutable (不可變的),一般可包含異質元素序列,通過解包(見本節下文)或索引訪問(如果是 namedtuples,可以屬性訪問)。列表是 mutable (可變的),列表元素一般為同質類型,可迭代訪問。
構造 0 個或 1 個元素的元組比較特殊:為了適應這種情況,對句法有一些額外的改變。用一對空圓括號就可以創建空元組;只有一個元素的元組可以通過在這個元素後添加逗號來構建(圓括號裡只有一個值的話不夠明確)。丑陋,但是有效。例如:
>>>
>>> empty = () >>> singleton = 'hello', # <-- note trailing comma >>> len(empty) 0 >>> len(singleton) 1 >>> singleton ('hello',)
語句 t = 12345, 54321, 'hello!'
是 元組打包 的例子:值 12345
, 54321
和 'hello!'
一起被打包進元組。逆操作也可以:
>>>
>>> x, y, z = t
稱之為 序列解包 也是妥妥的,適用於右側的任何序列。序列解包時,左側變量與右側序列元素的數量應相等。注意,多重賦值其實只是元組打包和序列解包的組合。
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'}
字典 (參見 映射類型 --- dict) 也是一種常用的 Python 內置數據類型。其他語言可能把字典稱為 聯合內存 或 聯合數組。與以連續整數為索引的序列不同,字典以 關鍵字 為索引,關鍵字通常是字符串或數字,也可以是其他任意不可變類型。只包含字符串、數字、元組的元組,也可以用作關鍵字。但如果元組直接或間接地包含了可變對象,就不能用作關鍵字。列表不能當關鍵字,因為列表可以用索引、切片、append()
、extend()
等方法修改。
可以把字典理解為 鍵值對 的集合,但字典的鍵必須是唯一的。花括號 {}
用於創建空字典。另一種初始化字典的方式是,在花括號裡輸入逗號分隔的鍵值對,這也是字典的輸出方式。
字典的主要用途是通過關鍵字存儲、提取值。用 del
可以刪除鍵值對。用已存在的關鍵字存儲值,與該關鍵字關聯的舊值會被取代。通過不存在的鍵提取值,則會報錯。
對字典執行 list(d)
操作,返回該字典中所有鍵的列表,按插入次序排列(如需排序,請使用 sorted(d)
)。檢查字典裡是否存在某個鍵,使用關鍵字 in。
以下是一些字典的簡單示例:
>>>
>>> tel = {'jack': 4098, 'sape': 4139} >>> tel['guido'] = 4127 >>> tel {'jack': 4098, 'sape': 4139, 'guido': 4127} >>> tel['jack'] 4098 >>> del tel['sape'] >>> tel['irv'] = 4127 >>> tel {'jack': 4098, 'guido': 4127, 'irv': 4127} >>> list(tel) ['jack', 'guido', 'irv'] >>> sorted(tel) ['guido', 'irv', 'jack'] >>> 'guido' in tel True >>> 'jack' not in tel False
dict() 構造函數可以直接用鍵值對序列創建字典:
>>>
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) {'sape': 4139, 'guido': 4127, 'jack': 4098}
字典推導式可以用任意鍵值表達式創建字典:
>>>
>>> {x: x**2 for x in (2, 4, 6)} {2: 4, 4: 16, 6: 36}
關鍵字是比較簡單的字符串時,直接用關鍵字參數指定鍵值對更便捷:
>>>
>>> dict(sape=4139, guido=4127, jack=4098) {'sape': 4139, 'guido': 4127, 'jack': 4098}
在字典中循環時,用 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 i in sorted(basket): ... print(i) ... apple apple banana orange orange pear
使用 set() 去除序列中的重復元素。使用 sorted() 加 set() 則按排序後的順序,循環遍歷序列中的唯一元素:
>>>
>>> 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]
while
和 if
條件句不只可以進行比較,還可以使用任意運算符。
比較運算符 in
和 not in
用於執行確定一個值是否存在(或不存在)於某個容器中的成員檢測。 運算符 is
和 is not
用於比較兩個對象是否是同一個對象。 所有比較運算符的優先級都一樣,且低於任何數值運算符。
比較操作支持鏈式操作。例如,a < b == c
校驗 a
是否小於 b
,且 b
是否等於 c
。
比較操作可以用布爾運算符 and
和 or
組合,並且,比較操作(或其他布爾運算)的結果都可以用 not
取反。這些操作符的優先級低於比較操作符;not
的優先級最高, or
的優先級最低,因此,A and not B or C
等價於 (A and (not B)) or C
。與其他運算符操作一樣,此處也可以用圓括號表示想要的組合。
布爾運算符 and
和 or
也稱為 短路 運算符:其參數從左至右解析,一旦可以確定結果,解析就會停止。例如,如果 A
和 C
為真,B
為假,那麼 A and B and C
不會解析 C
。用作普通值而不是布爾值時,短路操作符返回的值通常是最後一個變量。
還可以把比較操作或邏輯表達式的結果賦值給變量,例如:
>>>
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance' >>> non_null = string1 or string2 or string3 >>> non_null 'Trondheim'
注意,Python 與 C 不同,在表達式內部賦值必須顯式使用 海象運算符 :=
。 這避免了 C 程序中常見的問題:要在表達式中寫 ==
時,卻寫成了 =
。
序列對象可以與相同序列類型的其他對象比較。這種比較使用 字典式 順序:首先,比較前兩個對應元素,如果不相等,則可確定比較結果;如果相等,則比較之後的兩個元素,以此類推,直到其中一個序列結束。如果要比較的兩個元素本身是相同類型的序列,則遞歸地執行字典式順序比較。如果兩個序列中所有的對應元素都相等,則兩個序列相等。如果一個序列是另一個的初始子序列,則較短的序列可被視為較小(較少)的序列。 對於字符串來說,字典式順序使用 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 異常。
程序輸出有幾種顯示方式;數據既可以輸出供人閱讀的形式,也可以寫入文件備用。本章探討一些可用的方式。
至此,我們已學習了兩種寫入值的方法:表達式語句 和 print() 函數。第三種方法是使用文件對象的 write()
方法;標准輸出文件稱為 sys.stdout
。詳見標准庫參考。
對輸出格式的控制不只是打印空格分隔的值,還需要更多方式。格式化輸出包括以下幾種方法。
使用 格式化字符串字面值 ,要在字符串開頭的引號/三引號前添加 f
或 F
。在這種字符串中,可以在 {
和 }
字符之間輸入引用的變量,或字面值的 Python 表達式。
>>> year = 2016 >>> event = 'Referendum' >>> f'Results of the {year} {event}' 'Results of the 2016 Referendum'
字符串的 str.format() 方法需要更多手動操作。該方法也用 {
和 }
標記替換變量的位置,雖然這種方法支持詳細的格式化指令,但需要提供格式化信息。
>>> yes_votes = 42_572_654 >>> no_votes = 43_132_495 >>> percentage = yes_votes / (yes_votes + no_votes) >>> '{:-9} YES votes {:2.2%}'.format(yes_votes, percentage) ' 42572654 YES votes 49.67%'
最後,還可以用字符串切片和合並操作完成字符串處理操作,創建任何排版布局。字符串類型還支持將字符串按給定列寬進行填充,這些方法也很有用。
如果不需要花哨的輸出,只想快速顯示變量進行調試,可以用 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'))"
string 模塊包含 Template 類,提供了將值替換為字符串的另一種方法。該類使用 $x
占位符,並用字典的值進行替換,但對格式控制的支持比較有限。
格式化字符串字面值 (簡稱為 f-字符串)在字符串前加前綴 f
或 F
,通過 {expression}
表達式,把 Python 表達式的值添加到字符串內。
格式說明符是可選的,寫在表達式後面,可以更好地控制格式化值的方式。下例將 pi 捨入到小數點後三位:
>>>
>>> import math >>> print(f'The value of pi is approximately {math.pi:.3f}.') The value of pi is approximately 3.142.
在 ':'
後傳遞整數,為該字段設置最小字符寬度,常用於列對齊:
>>>
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678} >>> for name, phone in table.items(): ... print(f'{name:10} ==> {phone:10d}') ... Sjoerd ==> 4127 Jack ==> 4098 Dcab ==> 7678
還有一些修飾符可以在格式化前轉換值。 '!a'
應用 ascii() ,'!s'
應用 str(),'!r'
應用 repr():
>>>
>>> animals = 'eels' >>> print(f'My hovercraft is full of {animals}.') My hovercraft is full of eels. >>> print(f'My hovercraft is full of {animals!r}.') My hovercraft is full of 'eels'.
格式規范參考詳見參考指南 格式規格迷你語言。
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.
如果不想分拆較長的格式字符串,最好按名稱引用變量進行格式化,不要按位置。這項操作可以通過傳遞字典,並用方括號 '[]'
訪問鍵來完成。
>>>
>>> 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
This could also be done by passing the table
dictionary as keyword arguments with the **
notation.
>>>
>>> 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() 結合使用時,這種方式非常實用,可以返回包含所有局部變量的字典。
例如,下面的代碼生成一組整齊的列,包含給定整數及其平方與立方:
>>>
>>> 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
str.format() 進行字符串格式化的完整概述詳見 格式字符串語法 。
下面是使用手動格式化方式實現的同一個平方和立方的表:
>>>
>>> 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
(注意,每列之間的空格是通過使用 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'
% 運算符(求余符)也可用於字符串格式化。給定 'string' % values
,則 string
中的 %
實例會以零個或多個 values
元素替換。此操作被稱為字符串插值。例如:
>>>
>>> import math >>> print('The value of pi is approximately %5.3f.' % math.pi) The value of pi is approximately 3.142.
printf 風格的字符串格式化 小節介紹更多相關內容。
open() 返回一個 file object ,最常使用的是兩個位置參數和一個關鍵字參數:open(filename, mode, encoding=None)
>>>
>>> f = open('workfile', 'w', encoding="utf-8")
第一個實參是文件名字符串。第二個實參是包含描述文件使用方式字符的字符串。mode 的值包括 'r'
,表示文件只能讀取;'w'
表示只能寫入(現有同名文件會被覆蓋);'a'
表示打開文件並追加內容,任何寫入的數據會自動添加到文件末尾。'r+'
表示打開文件進行讀寫。mode 實參是可選的,省略時的默認值為 'r'
。
通常情況下,文件是以 text mode 打開的,也就是說,你從文件中讀寫字符串,這些字符串是以特定的 encoding 編碼的。如果沒有指定 encoding ,默認的是與平台有關的(見 open() )。因為 UTF-8 是現代事實上的標准,除非你知道你需要使用一個不同的編碼,否則建議使用 encoding="utf-8"
。在模式後面加上一個 'b'
,可以用 binary mode 打開文件。二進制模式的數據是以 bytes 對象的形式讀寫的。在二進制模式下打開文件時,你不能指定 encoding 。
在文本模式下讀取文件時,默認把平台特定的行結束符(Unix 上為 \n
, Windows 上為 \r\n
)轉換為 \n
。在文本模式下寫入數據時,默認把 \n
轉換回平台特定結束符。這種操作方式在後台修改文件數據對文本文件來說沒有問題,但會破壞 JPEG
或 EXE
等二進制文件中的數據。注意,在讀寫此類文件時,一定要使用二進制模式。
在處理文件對象時,最好使用 with 關鍵字。優點是,子句體結束後,文件會正確關閉,即便觸發異常也可以。而且,使用 with
相比等效的 try-finally 代碼塊要簡短得多:
>>>
>>> with open('workfile', encoding="utf-8") as f: ... read_data = f.read() >>> # We can check that the file has been automatically closed. >>> f.closed True
如果沒有使用 with 關鍵字,則應調用 f.close()
關閉文件,即可釋放文件占用的系統資源。
警告
調用 f.write()
時,未使用 with
關鍵字,或未調用 f.close()
,即使程序正常退出,也**可能** 導致 f.write()
的參數沒有完全寫入磁盤。
通過 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.
本節下文中的例子假定已創建 f
文件對象。
f.read(size)
可用於讀取文件內容,它會讀取一些數據,並返回字符串(文本模式),或字節串對象(在二進制模式下)。 size 是可選的數值參數。省略 size 或 size 為負數時,讀取並返回整個文件的內容;文件大小是內存的兩倍時,會出現問題。size 取其他值時,讀取並返回最多 size 個字符(文本模式)或 size 個字節(二進制模式)。如已到達文件末尾,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()
返回整數,給出文件對象在文件中的當前位置,表示為二進制模式下時從文件開始的字節數,以及文本模式下的意義不明的數字。
f.seek(offset, whence)
可以改變文件對象的位置。通過向參考點添加 offset 計算位置;參考點由 whence 參數指定。 whence 值為 0 時,表示從文件開頭計算,1 表示使用當前文件位置,2 表示使用文件末尾作為參考點。省略 whence 時,其默認值為 0,即使用文件開頭作為參考點。
>>>
>>> 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)
搜索到文件末尾是個例外),唯一有效的 offset 值是能從 f.tell()
中返回的,或 0。其他 offset 值都會產生未定義的行為。
文件對象還支持 isatty()
和 truncate()
等方法,但不常用;文件對象的完整指南詳見庫參考。
從文件寫入或讀取字符串很簡單,數字則稍顯麻煩,因為 read()
方法只返回字符串,這些字符串必須傳遞給 int() 這樣的函數,接受 '123'
這樣的字符串,並返回數字值 123。保存嵌套列表、字典等復雜數據類型時,手動解析和序列化的操作非常復雜。
Python 支持 JSON (JavaScript Object Notation) 這種流行數據交換格式,用戶無需沒完沒了地編寫、調試代碼,才能把復雜的數據類型保存到文件。json 標准模塊采用 Python 數據層次結構,並將之轉換為字符串表示形式;這個過程稱為 serializing (序列化)。從字符串表示中重建數據稱為 deserializing (解序化)。在序列化和解序化之間,表示對象的字符串可能已經存儲在文件或數據中,或通過網絡連接發送到遠方 的機器。
備注
JSON 格式通常用於現代應用程序的數據交換。程序員早已對它耳熟能詳,可謂是交互操作的不二之選。
只需一行簡單的代碼即可查看某個對象的 JSON 字符串表現形式:
>>>
>>> import json >>> x = [1, 'simple', 'list'] >>> json.dumps(x) '[1, "simple", "list"]'
dumps() 函數還有一個變體, dump() ,它只將對象序列化為 text file 。因此,如果 f
是 text file 對象,可以這樣做:
json.dump(x, f)
要再次解碼對象,如果 f
是已打開、供讀取的 binary file 或 text file 對象:
x = json.load(f)
備注
JSON文件必須以UTF-8編碼。當打開JSON文件作為一個 text file 用於讀寫時,使用 encoding="utf-8"
。
這種簡單的序列化技術可以處理列表和字典,但在 JSON 中序列化任意類的實例,則需要付出額外努力。json 模塊的參考包含對此的解釋。
參見
pickle - 封存模塊
與 JSON 不同,pickle 是一種允許對復雜 Python 對象進行序列化的協議。因此,它為 Python 所特有,不能用於與其他語言編寫的應用程序通信。默認情況下它也是不安全的:如果解序化的數據是由手段高明的攻擊者精心設計的,這種不受信任來源的 pickle 數據可以執行任意代碼。
至此,本教程還未深入介紹錯誤信息,但如果您輸入過本教程前文中的例子,應該已經看到過一些錯誤信息。目前,(至少)有兩種不同錯誤:句法錯誤 和 異常。
句法錯誤又稱解析錯誤,是學習 Python 時最常見的錯誤:
>>>
>>> while True print('Hello world') File "<stdin>", line 1 while True print('Hello world') ^ SyntaxError: invalid syntax
解析器會復現出現句法錯誤的代碼行,並用小“箭頭”指向行裡檢測到的第一個錯誤。錯誤是由箭頭 上方 的 token 觸發的(至少是在這裡檢測出的):本例中,在 print() 函數中檢測到錯誤,因為,在它前面缺少冒號(':'
) 。錯誤信息還輸出文件名與行號,在使用腳本文件時,就可以知道去哪裡查錯。
即使語句或表達式使用了正確的語法,執行時仍可能觸發錯誤。執行時檢測到的錯誤稱為 異常,異常不一定導致嚴重的後果:很快我們就能學會如何處理 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 only concatenate str (not "int") to str
錯誤信息的最後一行說明程序遇到了什麼類型的錯誤。異常有不同的類型,而類型名稱會作為錯誤信息的一部分中打印出來:上述示例中的異常類型依次是:ZeroDivisionError, NameError 和 TypeError。作為異常類型打印的字符串是發生的內置異常的名稱。對於所有內置異常都是如此,但對於用戶定義的異常則不一定如此(雖然這種規范很有用)。標准的異常類型是內置的標識符(不是保留關鍵字)。
此行其余部分根據異常類型,結合出錯原因,說明錯誤細節。
錯誤信息開頭用堆棧回溯形式展示發生異常的語境。一般會列出源代碼行的堆棧回溯;但不會顯示從標准輸入讀取的行。
內置異常 列出了內置異常及其含義。
可以編寫程序處理選定的異常。下例會要求用戶一直輸入內容,直到輸入有效的整數,但允許用戶中斷程序(使用 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 代碼塊之後繼續執行。
如果發生的異常與 except 子句 中指定的異常不匹配,則它會被傳遞到外部的 try 語句中;如果沒有找到處理程序,則它是一個 未處理異常 且執行將終止並輸出如上所示的消息。
try 語句可以有多個 except 子句 來為不同的異常指定處理程序。 但最多只有一個處理程序會被執行。 處理程序只處理對應的 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 子句 的順序(把 except B
放在最前),則會輸出 B, B, B --- 即觸發了第一個匹配的 except 子句。
When an exception occurs, it may have associated values, also known as the exception's arguments. The presence and types of the arguments depend on the exception type.
The except clause may specify a variable after the exception name. The variable is bound to the exception instance which typically has an args
attribute that stores the arguments. For convenience, builtin exception types define __str__()
to print all the arguments without explicitly accessing .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
The exception's __str__()
output is printed as the last part ('detail') of the message for unhandled exceptions.
BaseException is the common base class of all exceptions. One of its subclasses, Exception, is the base class of all the non-fatal exceptions. Exceptions which are not subclasses of Exception are not typically handled, because they are used to indicate that the program should terminate. They include SystemExit which is raised by sys.exit() and KeyboardInterrupt which is raised when a user wishes to interrupt the program.
Exception can be used as a wildcard that catches (almost) everything. However, it is good practice to be as specific as possible with the types of exceptions that we intend to handle, and to allow any unexpected exceptions to propagate on.
The most common pattern for handling Exception is to print or log the exception and then re-raise it (allowing a caller to handle the exception as well):
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error:", err) except ValueError: print("Could not convert data to an integer.") except Exception as err: print(f"Unexpected {err=}, {type(err)=}") raise
try ... except 語句具有可選的 else 子句,該子句如果存在,它必須放在所有 except 子句 之後。 它適用於 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
語句保護的代碼觸發的異常。
Exception handlers do not handle only exceptions that occur immediately in the try clause, but also those that occur inside functions that are called (even indirectly) in the try clause. For example:
>>>
>>> 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
raise 語句支持強制觸發指定的異常。例如:
>>>
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: HiThere
The sole argument to raise indicates the exception to be raised. This must be either an exception instance or an exception class (a class that derives from BaseException, such as Exception or one of its subclasses). If an exception class is passed, it will be implicitly instantiated by calling its constructor with no arguments:
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
raise 語句支持可選的 from 子句,該子句用於啟用鏈式異常。 例如:
# exc must be exception instance or None. raise RuntimeError from exc
轉換異常時,這種方式很有用。例如:
>>>
>>> def func(): ... raise ConnectionError ... >>> try: ... func() ... except ConnectionError as exc: ... raise RuntimeError('Failed to open database') from exc ... Traceback (most recent call last): File "<stdin>", line 2, in <module> File "<stdin>", line 2, in func ConnectionError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 4, in <module> RuntimeError: Failed to open database
異常鏈會在 except 或 finally 子句內部引發異常時自動生成。 這可以通過使用 from None
這樣的寫法來禁用:
>>>
>>> try: ... open('database.sqlite') ... except OSError: ... raise RuntimeError from None ... Traceback (most recent call last): File "<stdin>", line 4, in <module> RuntimeError
異常鏈機制詳見 內置異常。
程序可以通過創建新的異常類命名自己的異常(Python 類的內容詳見 類)。不論是以直接還是間接的方式,異常都應從 Exception 類派生。
異常類可以被定義成能做其他類所能做的任何事,但通常應當保持簡單,它往往只提供一些屬性,允許相應的異常處理程序提取有關錯誤的信息。
大多數異常命名都以 “Error” 結尾,類似標准異常的命名。
Many standard modules define their own exceptions to report errors that may occur in functions they define.
try 語句還有一個可選子句,用於定義在所有情況下都必須要執行的清理操作。例如:
>>>
>>> try: ... raise KeyboardInterrupt ... finally: ... print('Goodbye, world!') ... Goodbye, world! KeyboardInterrupt Traceback (most recent call last): File "<stdin>", line 2, in <module>
如果存在 finally 子句,則 finally
子句是 try 語句結束前執行的最後一項任務。不論 try
語句是否觸發異常,都會執行 finally
子句。以下內容介紹了幾種比較復雜的觸發異常情景:
如果執行 try
子句期間觸發了某個異常,則某個 except 子句應處理該異常。如果該異常沒有 except
子句處理,在 finally
子句執行後會被重新觸發。
except
或 else
子句執行期間也會觸發異常。 同樣,該異常會在 finally
子句執行之後被重新觸發。
如果 finally
子句中包含 break、continue 或 return 等語句,異常將不會被重新引發。
如果執行 try
語句時遇到 break,、continue 或 return 語句,則 finally
子句在執行 break
、continue
或 return
語句之前執行。
如果 finally
子句中包含 return
語句,則返回值來自 finally
子句的某個 return
語句的返回值,而不是來自 try
子句的 return
語句的返回值。
例如:
>>>
>>> def bool_return(): ... try: ... return True ... finally: ... return False ... >>> bool_return() False
這是一個比較復雜的例子:
>>>
>>> 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 子句。except 子句不處理兩個字符串相除觸發的 TypeError,因此會在 finally
子句執行後被重新觸發。
在實際應用程序中,finally 子句對於釋放外部資源(例如文件或者網絡連接)非常有用,無論是否成功使用資源。
某些對象定義了不需要該對象時要執行的標准清理操作。無論使用該對象的操作是否成功,都會執行清理操作。比如,下例要打開一個文件,並輸出文件內容:
for line in open("myfile.txt"): print(line, end="")
這個代碼的問題在於,執行完代碼後,文件在一段不確定的時間內處於打開狀態。在簡單腳本中這沒有問題,但對於較大的應用程序來說可能會出問題。with 語句支持以及時、正確的清理的方式使用文件對象:
with open("myfile.txt") as f: for line in f: print(line, end="")
語句執行完畢後,即使在處理行時遇到問題,都會關閉文件 f。和文件一樣,支持預定義清理操作的對象會在文檔中指出這一點。
There are situations where it is necessary to report several exceptions that have occurred. This it often the case in concurrency frameworks, when several tasks may have failed in parallel, but there are also other use cases where it is desirable to continue execution and collect multiple errors rather than raise the first exception.
The builtin ExceptionGroup wraps a list of exception instances so that they can be raised together. It is an exception itself, so it can be caught like any other exception.
>>>
>>> def f(): ... excs = [OSError('error 1'), SystemError('error 2')] ... raise ExceptionGroup('there were problems', excs) ... >>> f() + Exception Group Traceback (most recent call last): | File "<stdin>", line 1, in <module> | File "<stdin>", line 3, in f | ExceptionGroup: there were problems +-+---------------- 1 ---------------- | OSError: error 1 +---------------- 2 ---------------- | SystemError: error 2 +------------------------------------ >>> try: ... f() ... except Exception as e: ... print(f'caught {type(e)}: e') ... caught <class 'ExceptionGroup'>: e >>>
By using except*
instead of except
, we can selectively handle only the exceptions in the group that match a certain type. In the following example, which shows a nested exception group, each except*
clause extracts from the group exceptions of a certain type while letting all other exceptions propagate to other clauses and eventually to be reraised.
>>>
>>> def f(): ... raise ExceptionGroup("group1", ... [OSError(1), ... SystemError(2), ... ExceptionGroup("group2", ... [OSError(3), RecursionError(4)])]) ... >>> try: ... f() ... except* OSError as e: ... print("There were OSErrors") ... except* SystemError as e: ... print("There were SystemErrors") ... There were OSErrors There were SystemErrors + Exception Group Traceback (most recent call last): | File "<stdin>", line 2, in <module> | File "<stdin>", line 2, in f | ExceptionGroup: group1 +-+---------------- 1 ---------------- | ExceptionGroup: group2 +-+---------------- 1 ---------------- | RecursionError: 4 +------------------------------------ >>>
Note that the exceptions nested in an exception group must be instances, not types. This is because in practice the exceptions would typically be ones that have already been raised and caught by the program, along the following pattern:
>>>
>>> excs = [] ... for test in tests: ... try: ... test.run() ... except Exception as e: ... excs.append(e) ... >>> if excs: ... raise ExceptionGroup("Test Failures", excs) ...
When an exception is created in order to be raised, it is usually initialized with information that describes the error that has occurred. There are cases where it is useful to add information after the exception was caught. For this purpose, exceptions have a method add_note(note)
that accepts a string and adds it to the exception's notes list. The standard traceback rendering includes all notes, in the order they were added, after the exception.
>>>
>>> try: ... raise TypeError('bad type') ... except Exception as e: ... e.add_note('Add some information') ... e.add_note('Add some more information') ... raise ... Traceback (most recent call last): File "<stdin>", line 2, in <module> TypeError: bad type Add some information Add some more information >>>
For example, when collecting exceptions into an exception group, we may want to add context information for the individual errors. In the following each exception in the group has a note indicating when this error has occurred.
>>>
>>> def f(): ... raise OSError('operation failed') ... >>> excs = [] >>> for i in range(3): ... try: ... f() ... except Exception as e: ... e.add_note(f'Happened in Iteration {i+1}') ... excs.append(e) ... >>> raise ExceptionGroup('We have some problems', excs) + Exception Group Traceback (most recent call last): | File "<stdin>", line 1, in <module> | ExceptionGroup: We have some problems (3 sub-exceptions) +-+---------------- 1 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | File "<stdin>", line 2, in f | OSError: operation failed | Happened in Iteration 1 +---------------- 2 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | File "<stdin>", line 2, in f | OSError: operation failed | Happened in Iteration 2 +---------------- 3 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | File "<stdin>", line 2, in f | OSError: operation failed | Happened in Iteration 3 +------------------------------------ >>>
類把數據與功能綁定在一起。創建新類就是創建新的對象 類型,從而創建該類型的新 實例 。類實例支持維持自身狀態的屬性,還支持(由類定義的)修改自身狀態的方法。
和其他編程語言相比,Python 的類只使用了很少的新語法和語義。Python 的類有點類似於 C++ 和 Modula-3 中類的結合體,而且支持面向對象編程(OOP)的所有標准特性:類的繼承機制支持多個基類、派生的類能覆蓋基類的方法、類的方法能調用基類中的同名方法。對象可包含任意數量和類型的數據。和模塊一樣,類也支持 Python 動態特性:在運行時創建,創建後還可以修改。
如果用 C++ 術語來描述的話,類成員(包括數據成員)通常為 public (例外的情況見下文 私有變量),所有成員函數都是 virtual。與在 Modula-3 中一樣,沒有用於從對象的方法中引用對象成員的簡寫形式:方法函數在聲明時,有一個顯式的參數代表本對象,該參數由調用隱式提供。 與在 Smalltalk 中一樣,Python 的類也是對象,這為導入和重命名提供了語義支持。與 C++ 和 Modula-3 不同,Python 的內置類型可以用作基類,供用戶擴展。 此外,與 C++ 一樣,算術運算符、下標等具有特殊語法的內置運算符都可以為類實例而重新定義。
由於缺乏關於類的公認術語,本章中偶爾會使用 Smalltalk 和 C++ 的術語。本章還會使用 Modula-3 的術語,Modula-3 的面向對象語義比 C++ 更接近 Python,但估計聽說過這門語言的讀者很少。
對象之間相互獨立,多個名稱(在多個作用域內)可以綁定到同一個對象。 其他語言稱之為別名。Python 初學者通常不容易理解這個概念,處理數字、字符串、元組等不可變基本類型時,可以不必理會。 但是,對涉及可變對象,如列表、字典等大多數其他類型的 Python 代碼的語義,別名可能會產生意料之外的效果。這樣做,通常是為了讓程序受益,因為別名在某些方面就像指針。例如,傳遞對象的代價很小,因為實現只傳遞一個指針;如果函數修改了作為參數傳遞的對象,調用者就可以看到更改 --- 無需 Pascal 用兩個不同參數的傳遞機制。
在介紹類前,首先要介紹 Python 的作用域規則。類定義對命名空間有一些巧妙的技巧,了解作用域和命名空間的工作機制有利於加強對類的理解。並且,即便對於高級 Python 程序員,這方面的知識也很有用。
接下來,我們先了解一些定義。
namespace (命名空間)是映射到對象的名稱。現在,大多數命名空間都使用 Python 字典實現,但除非涉及到優化性能,我們一般不會關注這方面的事情,而且將來也可能會改變這種方式。命名空間的幾個常見示例: abs() 函數、內置異常等的內置函數集合;模塊中的全局名稱;函數調用中的局部名稱。對象的屬性集合也算是一種命名空間。關於命名空間的一個重要知識點是,不同命名空間中的名稱之間絕對沒有關系;例如,兩個不同的模塊都可以定義 maximize
函數,且不會造成混淆。用戶使用函數時必須要在函數名前面附加上模塊名。
點號之後的名稱是 屬性。例如,表達式 z.real
中,real
是對象 z
的屬性。嚴格來說,對模塊中名稱的引用是屬性引用:表達式 modname.funcname
中,modname
是模塊對象,funcname
是模塊的屬性。模塊屬性和模塊中定義的全局名稱之間存在直接的映射:它們共享相同的命名空間! 1
屬性可以是只讀或者可寫的。如果可寫,則可對屬性賦值。模塊屬性是可寫時,可以使用 modname.the_answer = 42
。del 語句可以刪除可寫屬性。例如, del modname.the_answer
會刪除 modname
對象中的 the_answer
屬性。
命名空間是在不同時刻創建的,且擁有不同的生命周期。內置名稱的命名空間是在 Python 解釋器啟動時創建的,永遠不會被刪除。模塊的全局命名空間在讀取模塊定義時創建;通常,模塊的命名空間也會持續到解釋器退出。從腳本文件讀取或交互式讀取的,由解釋器頂層調用執行的語句是 __main__ 模塊調用的一部分,也擁有自己的全局命名空間。內置名稱實際上也在模塊裡,即 builtins 。
函數的本地命名空間在調用該函數時創建,並在函數返回或拋出不在函數內部處理的錯誤時被刪除。 (實際上,用“遺忘”來描述實際發生的情況會更好一些。) 當然,每次遞歸調用都會有自己的本地命名空間。
作用域 是命名空間可直接訪問的 Python 程序的文本區域。 “可直接訪問” 的意思是,對名稱的非限定引用會在命名空間中查找名稱。
作用域雖然是靜態確定的,但會被動態使用。執行期間的任何時刻,都會有 3 或 4 個命名空間可被直接訪問的嵌套作用域:
最內層作用域,包含局部名稱,並首先在其中進行搜索
封閉函數的作用域,包含非局部名稱和非全局名稱,從最近的封閉作用域開始搜索
倒數第二個作用域,包含當前模塊的全局名稱
最外層的作用域,包含內置名稱的命名空間,最後搜索
如果把名稱聲明為全局變量,則所有引用和賦值將直接指向包含該模塊的全局名稱的中間作用域。重新綁定在最內層作用域以外找到的變量,使用 nonlocal 語句把該變量聲明為非局部變量。未聲明為非局部變量的變量是只讀的,(寫入只讀變量會在最內層作用域中創建一個 新的 局部變量,而同名的外部變量保持不變。)
通常,當前局部作用域將(按字面文本)引用當前函數的局部名稱。在函數之外,局部作用域引用與全局作用域一致的命名空間:模塊的命名空間。 類定義在局部命名空間內再放置另一個命名空間。
劃重點,作用域是按字面文本確定的:模塊內定義的函數的全局作用域就是該模塊的命名空間,無論該函數從什麼地方或以什麼別名被調用。另一方面,實際的名稱搜索是在運行時動態完成的。但是,Python 正在朝著“編譯時靜態名稱解析”的方向發展,因此不要過於依賴動態名稱解析!(局部變量已經是被靜態確定了。)
Python 有一個特殊規定。如果不存在生效的 global 或 nonlocal 語句,則對名稱的賦值總是會進入最內層作用域。賦值不會復制數據,只是將名稱綁定到對象。刪除也是如此:語句 del x
從局部作用域引用的命名空間中移除對 x
的綁定。所有引入新名稱的操作都是使用局部作用域:尤其是 import 語句和函數定義會在局部作用域中綁定模塊或函數名稱。
global 語句用於表明特定變量在全局作用域裡,並應在全局作用域中重新綁定;nonlocal 語句表明特定變量在外層作用域中,並應在外層作用域中重新綁定。
下例演示了如何引用不同作用域和名稱空間,以及 global 和 nonlocal 對變量綁定的影響:
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 對 spam 的綁定。 nonlocal 賦值會改變 scope_test 對 spam 的綁定,而 global 賦值會改變模塊層級的綁定。
而且,global 賦值前沒有 spam 的綁定。
類引入了一點新語法,三種新的對象類型和一些新語義。
最簡單的類定義形式如下:
class ClassName: <statement-1> . . . <statement-N>
與函數定義 (def 語句) 一樣,類定義必須先執行才能生效。把類定義放在 if 語句的分支裡或函數內部試試。
在實踐中,類定義內的語句通常都是函數定義,但也可以是其他語句。這部分內容稍後再討論。類裡的函數定義一般是特殊的參數列表,這是由方法調用的約定規范所指明的 --- 同樣,稍後再解釋。
當進入類定義時,將創建一個新的命名空間,並將其用作局部作用域 --- 因此,所有對局部變量的賦值都是在這個新命名空間之內。 特別的,函數定義會綁定到這裡的新函數名稱。
當(從結尾處)正常離開類定義時,將創建一個 類對象。 這基本上是一個包圍在類定義所創建命名空間內容周圍的包裝器;我們將在下一節了解有關類對象的更多信息。 原始的(在進入類定義之前起作用的)局部作用域將重新生效,類對象將在這裡被綁定到類定義頭所給出的類名稱 (在這個示例中為 ClassName
)。
類對象支持兩種操作:屬性引用和實例化。
屬性引用 使用 Python 中所有屬性引用所使用的標准語法: obj.name
。 有效的屬性名稱是類對象被創建時存在於類命名空間中的所有名稱。 因此,如果類定義是這樣的:
class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world'
那麼 MyClass.i
和 MyClass.f
就是有效的屬性引用,將分別返回一個整數和一個函數對象。 類屬性也可以被賦值,因此可以通過賦值來更改 MyClass.i
的值。 __doc__
也是一個有效的屬性,將返回所屬類的文檔字符串: "A simple example class"
。
類的 實例化 使用函數表示法。 可以把類對象視為是返回該類的一個新實例的不帶參數的函數。 舉例來說(假設使用上述的類):
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)
現在我們能用實例對象做什麼? 實例對象所能理解的唯一操作是屬性引用。 有兩種有效的屬性名稱:數據屬性和方法。
數據屬性 對應於 Smalltalk 中的“實例變量”,以及 C++ 中的“數據成員”。 數據屬性不需要聲明;像局部變量一樣,它們將在第一次被賦值時產生。 例如,如果 x
是上面創建的 MyClass
的實例,則以下代碼段將打印數值 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
並不是一回事 --- 它是一個 方法對象,不是函數對象。
通常,方法在綁定後立即被調用:
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 個參數的方法就相當於調用再多一個參數的對應函數,這個參數值為方法所屬實例對象,位置在其他參數之前。
如果你仍然無法理解方法的運作原理,那麼查看實現細節可能會弄清楚問題。 當一個實例的非數據屬性被引用時,將搜索實例所屬的類。 如果被引用的屬性名稱表示一個有效的類屬性中的函數對象,會通過打包(指向)查找到的實例對象和函數對象到一個抽象對象的方式來創建方法對象:這個抽象對象就是方法對象。 當附帶參數列表調用方法對象時,將基於實例對象和參數列表構建一個新的參數列表,並使用這個新參數列表調用相應的函數對象。
一般來說,實例變量用於每個實例的唯一數據,而類變量用於類的所有實例共享的屬性和方法:
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'
正如 名稱和對象 中已討論過的,共享數據可能在涉及 mutable 對象例如列表和字典的時候導致令人驚訝的結果。 例如以下代碼中的 tricks 列表不應該被用作類變量,因為所有的 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']
如果同樣的屬性名稱同時出現在實例和類中,則屬性查找會優先選擇實例:
>>>
>>> class Warehouse: ... purpose = 'storage' ... region = 'west' ... >>> w1 = Warehouse() >>> print(w1.purpose, w1.region) storage west >>> w2 = Warehouse() >>> w2.region = 'east' >>> print(w2.purpose, w2.region) storage east
數據屬性可以被方法以及一個對象的普通用戶(“客戶端”)所引用。 換句話說,類不能用於實現純抽象數據類型。 實際上,在 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
現在 f
, g
和 h
都是 C
類的引用函數對象的屬性,因而它們就都是 C
的實例的方法 --- 其中 h
完全等同於 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__
。
當然,如果不支持繼承,語言特性就不值得稱為“類”。派生類定義的語法如下所示:
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)
僅會在 obj.__class__
為 int 或某個派生自 int 的類時為 True
。
使用 issubclass() 來檢查類的繼承關系: issubclass(bool, int)
為 True
,因為 bool 是 int 的子類。 但是,issubclass(float, int)
為 False
,因為 float 不是 int 的子類。
Python 也支持一種多重繼承。 帶有多個基類的類定義語句如下所示:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
對於多數應用來說,在最簡單的情況下,你可以認為搜索從父類所繼承屬性的操作是深度優先、從左至右的,當層次結構中存在重疊時不會在同一個類中搜索兩次。 因此,如果某一屬性在 DerivedClassName
中未找到,則會到 Base1
中搜索它,然後(遞歸地)到 Base1
的基類中搜索,如果在那裡未找到,再到 Base2
中搜索,依此類推。
真實情況比這個更復雜一些;方法解析順序會動態改變以支持對 super() 的協同調用。 這種方式在某些其他多重繼承型語言中被稱為後續方法調用,它比單繼承型語言中的 super 調用更強大。
動態改變順序是有必要的,因為所有多重繼承的情況都會顯示出一個或更多的菱形關聯(即至少有一個父類可通過多條路徑被最底層類所訪問)。 例如,所有類都是繼承自 object,因此任何多重繼承的情況都提供了一條以上的路徑可以通向 object。 為了確保基類不會被訪問一次以上,動態算法會用一種特殊方式將搜索順序線性化, 保留每個類所指定的從左至右的順序,只調用每個父類一次,並且保持單調(即一個類可以被子類化而不影響其父類的優先順序)。 總而言之,這些特性使得設計具有多重繼承的可靠且可擴展的類成為可能。 要了解更多細節,請參閱 The Python 2.3 Method Resolution Order | Python.org。
那種僅限從一個對象內部訪問的“私有”實例變量在 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)
上面的示例即使在 MappingSubclass
引入了一個 __update
標識符的情況下也不會出錯,因為它會在 Mapping
類中被替換為 _Mapping__update
而在 MappingSubclass
類中被替換為 _MappingSubclass__update
。
請注意,改寫規則的設計主要是為了避免意外沖突;訪問或修改被視為私有的變量仍然是可能的。這在特殊情況下甚至會很有用,例如在調試器中。
請注意傳遞給 exec()
或 eval()
的代碼不會將發起調用類的類名視作當前類;這類似於 global
語句的效果,因此這種效果僅限於同時經過字節碼編譯的代碼。 同樣的限制也適用於 getattr()
, setattr()
和 delattr()
,以及對於 __dict__
的直接引用。
有時會需要使用類似於 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__
則是該方法所對應的函數對象。
到目前為止,您可能已經注意到大多數容器對象都可以使用 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 <str_iterator object at 0x10c90e650> >>> 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
生成器 是一個用於創建迭代器的簡單而強大的工具。 它們的寫法類似於標准的函數,但當它們要返回數據時會使用 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。 這些特性結合在一起,使得創建迭代器能與編寫常規函數一樣容易。
某些簡單的生成器可以寫成簡潔的表達式代碼,所用語法類似列表推導式,但外層為圓括號而非方括號。 這種表達式被設計用於生成器將立即被外層函數所使用的情況。 生成器表達式相比完整的生成器更緊湊但較不靈活,相比等效的列表推導式則更為節省內存。
示例:
>>>
>>> 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 >>> 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']
備注
1
存在一個例外。 模塊對象有一個秘密的只讀屬性 __dict__,它返回用於實現模塊命名空間的字典;__dict__ 是屬性但不是全局名稱。 顯然,使用這個將違反命名空間實現的抽象,應當僅被用於事後調試器之類的場合。
os 模塊提供了許多與操作系統交互的函數:
>>>
>>> import os >>> os.getcwd() # Return the current working directory 'C:\\Python311' >>> os.chdir('/server/accesslogs') # Change current working directory >>> os.system('mkdir today') # Run the command mkdir in the system shell 0
一定要使用 import os
而不是 from os import *
。這將避免內建的 open() 函數被 os.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'
glob 模塊提供了一個在目錄中使用通配符搜索創建文件列表的函數:
>>>
>>> import glob >>> glob.glob('*.py') ['primes.py', 'random.py', 'quote.py']
通用實用程序腳本通常需要處理命令行參數。這些參數作為列表存儲在 sys 模塊的 argv 屬性中。例如,以下輸出來自在命令行運行 python demo.py one two three
>>>
>>> import sys >>> print(sys.argv) ['demo.py', 'one', 'two', 'three']
argparse 模塊提供了一種更復雜的機制來處理命令行參數。 以下腳本可提取一個或多個文件名,並可選擇要顯示的行數:
import argparse parser = argparse.ArgumentParser( prog='top', description='Show top lines from each file') parser.add_argument('filenames', nargs='+') parser.add_argument('-l', '--lines', type=int, default=10) args = parser.parse_args() print(args)
當在通過 python top.py --lines=5 alpha.txt beta.txt
在命令行運行時,該腳本會將 args.lines
設為 5
並將 args.filenames
設為 ['alpha.txt', 'beta.txt']
。
sys 模塊還具有 stdin , stdout 和 stderr 的屬性。後者對於發出警告和錯誤消息非常有用,即使在 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()
。
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'
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> 有許多其他模塊用於數值計算。
有許多模塊可用於訪問互聯網和處理互聯網協議。其中兩個最簡單的 urllib.request 用於從URL檢索數據,以及 smtplib 用於發送郵件:
>>>
>>> from urllib.request import urlopen >>> with urlopen('http://worldtimeapi.org/api/timezone/etc/UTC.txt') as response: ... for line in response: ... line = line.decode() # Convert bytes to a str ... if line.startswith('datetime'): ... print(line.rstrip()) # Remove trailing newline ... datetime: 2022-01-01T01:36:47.689215+00:00 >>> import smtplib >>> server = smtplib.SMTP('localhost') >>> server.sendmail('[email protected]', '[email protected]', ... """To: [email protected] ... From: [email protected] ... ... Beware the Ides of March. ... """) >>> server.quit()
(請注意,第二個示例需要在localhost上運行的郵件服務器。)
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
常見的數據存檔和壓縮格式由模塊直接支持,包括:zlib, gzip, bz2, lzma, zipfile 和 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
一些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 模塊提供了用於在較大的代碼塊中識別時間關鍵部分的工具。
開發高質量軟件的一種方法是在開發過程中為每個函數編寫測試,並在開發過程中經常運行這些測試。
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
Python有“自帶電池”的理念。通過其包的復雜和強大功能可以最好地看到這一點。例如:
xmlrpc.client 和 xmlrpc.server 模塊使得實現遠程過程調用變成了小菜一碟。 盡管存在於模塊名稱中,但用戶不需要直接了解或處理 XML。
email 包是一個用於管理電子郵件的庫,包括MIME和其他符合 RFC 2822 規范的郵件文檔。與 smtplib 和 poplib 不同(它們實際上做的是發送和接收消息),電子郵件包提供完整的工具集,用於構建或解碼復雜的消息結構(包括附件)以及實現互聯網編碼和標頭協議。
json 包為解析這種流行的數據交換格式提供了強大的支持。 csv 模塊支持以逗號分隔值格式直接讀取和寫入文件,這種格式通常為數據庫和電子表格所支持。 XML 處理由 xml.etree.ElementTree , xml.dom 和 xml.sax 包支持。這些模塊和軟件包共同大大簡化了 Python 應用程序和其他工具之間的數據交換。
sqlite3 模塊是 SQLite 數據庫庫的包裝器,提供了一個可以使用稍微非標准的 SQL 語法更新和訪問的持久數據庫。
國際化由許多模塊支持,包括 gettext , locale ,以及 codecs 包。
第二部分涵蓋了專業編程所需要的更高級的模塊。這些模塊很少用在小腳本中。
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 模塊的 format 函數包含一個 grouping 屬性,可直接將數字格式化為帶有組分隔符的樣式:
>>>
>>> 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'
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.'
Template 的子類可以自定義分隔符。例如,以下是某個照片浏覽器的批量重命名功能,采用了百分號作為日期、照片序號和照片格式的占位符:
>>>
>>> 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 網絡報表使用自定義模板成為可能。
struct 模塊提供了 pack() 和 unpack() 函數,用於處理不定長度的二進制記錄格式。下面的例子展示了在不使用 zipfile 模塊的情況下,如何循環遍歷一個 ZIP 文件的所有頭信息。Pack 代碼 "H"
和 "I"
分別代表兩字節和四字節無符號整數。"<"
代表它們是標准尺寸的小端字節序:
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
線程是一種對於非順序依賴的多個任務進行解耦的技術。多線程可以提高應用的響應效率,當接收用戶輸入的同時,保持其他任務在後台運行。一個有關的應用場景是,將 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.')
多線程應用面臨的主要挑戰是,相互協調的多個線程之間需要共享數據或其他資源。為此,threading 模塊提供了多個同步操作原語,包括線程鎖、事件、條件變量和信號量。
盡管這些工具非常強大,但微小的設計錯誤卻可以導致一些難以復現的問題。因此,實現多任務協作的首選方法是將所有對資源的請求集中到一個線程中,然後使用 queue 模塊向該線程供應來自其他線程的請求。 應用程序使用 Queue 對象進行線程間通信和協調,更易於設計,更易讀,更可靠。
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
默認情況下,informational 和 debugging 消息被壓制,輸出會發送到標准錯誤流。其他輸出選項包括將消息轉發到電子郵件,數據報,套接字或 HTTP 服務器。新的過濾器可以根據消息優先級選擇不同的路由方式:DEBUG
,INFO
,WARNING
,ERROR
,和 CRITICAL
。
日志系統可以直接從 Python 配置,也可以從用戶配置文件加載,以便自定義日志記錄而無需更改應用程序。
Python 會自動進行內存管理(對大多數對象進行引用計數並使用 garbage collection 來清除循環引用)。 當某個對象的最後一個引用被移除後不久就會釋放其所占用的內存。
此方式對大多數應用來說都適用,但偶爾也必須在對象持續被其他對象所使用時跟蹤它們。 不幸的是,跟蹤它們將創建一個會令其永久化的引用。 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:/python311/lib/weakref.py", line 46, in __getitem__ o = self.data[key]() KeyError: 'primary'
許多對於數據結構的需求可以通過內置列表類型來滿足。 但是,有時也會需要具有不同效費比的替代實現。
array 模塊提供了一種 array() 對象,它類似於列表,但只能存儲類型一致的數據且存儲密集更高。 下面的例子演示了一個以兩個字節為存儲單元的無符號二進制數值的數組 (類型碼為 "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]
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 表示的結果會保留尾部的零,並根據具有兩個有效位的被乘數自動推出四個有效位。 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')
本章將解釋 Python 中組成表達式的各種元素的的含義。
語法注釋: 在本章和後續章節中,會使用擴展 BNF 標注來描述語法而不是詞法分析。 當(某種替代的)語法規則具有如下形式
name ::= othername
並且沒有給出語義,則這種形式的 name
在語法上與 othername
相同。
當對下述某個算術運算符的描述中使用了“數值參數被轉換為普通類型”這樣的說法,這意味著內置類型的運算符實現采用了如下運作方式:
如果任一參數為復數,另一參數會被轉換為復數;
否則,如果任一參數為浮點數,另一參數會被轉換為浮點數;
否則,兩者應該都為整數,不需要進行轉換。
某些附加規則會作用於特定運算符(例如,字符串作為 '%' 運算符的左運算參數)。 擴展必須定義它們自己的轉換行為。
“原子”指表達式的最基本構成元素。 最簡單的原子是標識符和字面值。 以圓括號、方括號或花括號包括的形式在語法上也被歸類為原子。 原子的句法為:
atom ::= identifier | literal | enclosure enclosure ::= parenth_form | list_display | dict_display | set_display | generator_expression | yield_atom
作為原子出現的標識符叫做名稱。 請參看 標識符和關鍵字 一節了解其詞法定義,以及 命名與綁定 獲取有關命名與綁定的文檔。
當名稱被綁定到一個對象時,對該原子求值將返回相應對象。 當名稱未被綁定時,嘗試對其求值將引發 NameError 異常。
私有名稱轉換: 當以文本形式出現在類定義中的一個標識符以兩個或更多下劃線開頭並且不以兩個或更多下劃線結尾,它會被視為該類的 私有名稱。 私有名稱會在為其生成代碼之前被轉換為一種更長的形式。 轉換時會插入類名,移除打頭的下劃線再在名稱前增加一個下劃線。 例如,出現在一個名為 Ham
的類中的標識符 __spam
會被轉換為 _Ham__spam
。 這種轉換獨立於標識符所使用的相關句法。 如果轉換後的名稱太長(超過 255 個字符),可能發生由具體實現定義的截斷。 如果類名僅由下劃線組成,則不會進行轉換。
Python 支持字符串和字節串字面值,以及幾種數字字面值:
literal ::= stringliteral | bytesliteral | integer | floatnumber | imagnumber
對字面值求值將返回一個該值所對應類型的對象(字符串、字節串、整數、浮點數、復數)。 對於浮點數和虛數(復數)的情況,該值可能為近似值。 詳情參見 字面值。
所有字面值都對應與不可變數據類型,因此對象標識的重要性不如其實際值。 多次對具有相同值的字面值求值(不論是發生在程序文本的相同位置還是不同位置)可能得到相同對象或是具有相同值的不同對象。
帶圓括號的形式是包含在圓括號中的可選表達式列表。
parenth_form ::= "(" [starred_expression] ")"
帶圓括號的表達式列表將返回該表達式列表所產生的任何東西:如果該列表包含至少一個逗號,它會產生一個元組;否則,它會產生該表達式列表所對應的單一表達式。
一對內容為空的圓括號將產生一個空的元組對象。 由於元組是不可變對象,因此適用與字面值相同的規則(即兩次出現的空元組產生的對象可能相同也可能不同)。
請注意元組並不是由圓括號構建,實際起作用的是逗號操作符。 例外情況是空元組,這時圓括號 才是 必須的 --- 允許在表達式中使用不帶圓括號的 "空" 會導致歧義,並會造成常見輸入錯誤無法被捕獲。
為了構建列表、集合或字典,Python 提供了名為“顯示”的特殊句法,每個類型各有兩種形式:
第一種是顯式地列出容器內容
第二種是通過一組循環和篩選指令計算出來,稱為 推導式。
推導式的常用句法元素為:
comprehension ::= assignment_expression comp_for comp_for ::= ["async"] "for" target_list "in" or_test [comp_iter] comp_iter ::= comp_for | comp_if comp_if ::= "if" or_test [comp_iter]
推導式的結構是一個單獨表達式後面加至少一個 for
子句以及零個或更多個 for
或 if
子句。 在這種情況下,新容器的元素產生方式是將每個 for
或 if
子句視為一個代碼塊,按從左至右的順序嵌套,然後每次到達最內層代碼塊時就對表達式進行求值以產生一個元素。
不過,除了最左邊 for
子句中的可迭代表達式,推導式是在另一個隱式嵌套的作用域內執行的。 這能確保賦給目標列表的名稱不會“洩露”到外層的作用域。
最左邊的 for
子句中的可迭代對象表達式會直接在外層作用域中被求值,然後作為一個參數被傳給隱式嵌套的作用域。 後續的 for
子句以及最左側 for
子句中的任何篩選條件不能在外層作用域中被求值,因為它們可能依賴於從最左側可迭代對象中獲得的值。 例如: [x*y for x in range(10) for y in range(x, x+10)]
。
為了確保推導式得出的結果總是一個類型正確的容器,在隱式嵌套作用域內禁止使用 yield
和 yield from
表達式。
Since Python 3.6, in an async def function, an async for
clause may be used to iterate over a asynchronous iterator. A comprehension in an async def
function may consist of either a for
or async for
clause following the leading expression, may contain additional for
or async for
clauses, and may also use await expressions. If a comprehension contains either async for
clauses or await
expressions or other asynchronous comprehensions it is called an asynchronous comprehension. An asynchronous comprehension may suspend the execution of the coroutine function in which it appears. See also PEP 530.
3.6 新版功能: 引入了異步推導式。
在 3.8 版更改: yield
和 yield from
在隱式嵌套的作用域中已被禁用。
在 3.11 版更改: Asynchronous comprehensions are now allowed inside comprehensions in asynchronous functions. Outer comprehensions implicitly become asynchronous.
列表顯示是一個用方括號括起來的可能為空的表達式系列:
list_display ::= "[" [starred_list | comprehension] "]"
列表顯示會產生一個新的列表對象,其內容通過一系列表達式或一個推導式來指定。 當提供由逗號分隔的一系列表達式時,其元素會從左至右被求值並按此順序放入列表對象。 當提供一個推導式時,列表會根據推導式所產生的結果元素進行構建。
集合顯示是用花括號標明的,與字典顯示的區別在於沒有冒號分隔的鍵和值:
set_display ::= "{" (starred_list | comprehension) "}"
集合顯示會產生一個新的可變集合對象,其內容通過一系列表達式或一個推導式來指定。 當提供由逗號分隔的一系列表達式時,其元素會從左至右被求值並加入到集合對象。 當提供一個推導式時,集合會根據推導式所產生的結果元素進行構建。
空集合不能用 {}
來構建;該字面值所構建的是一個空字典。
字典顯示是一個用花括號括起來的可能為空的鍵/數據對系列:
dict_display ::= "{" [key_datum_list | dict_comprehension] "}" key_datum_list ::= key_datum ("," key_datum)* [","] key_datum ::= expression ":" expression | "**" or_expr dict_comprehension ::= expression ":" expression comp_for
字典顯示會產生一個新的字典對象。
如果給出一個由逗號分隔的鍵/數據對序列,它們會從左至右被求值以定義字典的條目:每個鍵對象會被用作在字典中存放相應數據的鍵。 這意味著你可以在鍵/數據對序列中多次指定相同的鍵,最終字典的值將由最後一次給出的鍵決定。
雙星號 **
表示 字典拆包。 它的操作數必須是一個 mapping。 每個映射項被會加入新的字典。 後續的值會替代先前的鍵/數據對和先前的字典拆包所設置的值。
3.5 新版功能: 拆包到字典顯示,最初由 PEP 448 提出。
字典推導式與列表和集合推導式有所不同,它需要以冒號分隔的兩個表達式,後面帶上標准的 "for" 和 "if" 子句。 當推導式被執行時,作為結果的鍵和值元素會按它們的產生順序被加入新的字典。
對鍵取值類型的限制已列在之前的 標准類型層級結構 一節中。 (總的說來,鍵的類型應該為 hashable,這就把所有可變對象都排除在外。) 重復鍵之間的沖突不會被檢測;指定鍵所保存的最後一個數據 (即在顯示中排最右邊的文本) 為最終有效數據。
在 3.8 版更改: 在 Python 3.8 之前的字典推導式中,並沒有定義好鍵和值的求值順序。 在 CPython 中,值會先於鍵被求值。 根據 PEP 572 的提議,從 3.8 開始,鍵會先於值被求值。
生成器表達式是用圓括號括起來的緊湊形式生成器標注。
generator_expression ::= "(" expression comp_for ")"
生成器表達式會產生一個新的生成器對象。 其句法與推導式相同,區別在於它是用圓括號而不是用方括號或花括號括起來的。
在生成器表達式中使用的變量會在為生成器對象調用 __next__() 方法的時候以惰性方式被求值(即與普通生成器相同的方式)。 但是,最左側 for
子句內的可迭代對象是會被立即求值的,因此它所造成的錯誤會在生成器表達式被定義時被檢測到,而不是在獲取第一個值時才出錯。 後續的 for
子句以及最左側 for
子句內的任何篩選條件無法在外層作用域內被求值,因為它們可能會依賴於從最左側可迭代對象獲取的值。 例如: (x*y for x in range(10) for y in range(x, x+10))
.
圓括號在只附帶一個參數的調用中可以被省略。 詳情參見 調用 一節。
為了避免干擾到生成器表達式本身的預期操作,禁止在隱式定義的生成器中使用 yield
和 yield from
表達式。
如果生成器表達式包含 async for
子句或 await 表達式,則稱為 異步生成器表達式。 異步生成器表達式會返回一個新的異步生成器對象,此對象屬於異步迭代器 (參見 異步迭代器)。
3.6 新版功能: 引入了異步生成器表達式。
在 3.7 版更改: 在 Python 3.7 之前,異步生成器表達式只能在 async def 協和中出現。 從 3.7 開始,任何函數都可以使用異步生成器表達式。
在 3.8 版更改: yield
和 yield from
在隱式嵌套的作用域中已被禁用。
yield_atom ::= "(" yield_expression ")" yield_expression ::= "yield" [expression_list | "from" expression]
The yield expression is used when defining a generator function or an asynchronous generator function and thus can only be used in the body of a function definition. Using a yield expression in a function's body causes that function to be a generator function, and using it in an async def function's body causes that coroutine function to be an asynchronous generator function. For example:
def gen(): # defines a generator function yield 123 async def agen(): # defines an asynchronous generator function yield 123
由於它們會對外層作用域造成附帶影響,yield
表達式不被允許作為用於實現推導式和生成器表達式的隱式定義作用域的一部分。
在 3.8 版更改: 禁止在實現推導式和生成器表達式的隱式嵌套作用域中使用 yield 表達式。
下面是對生成器函數的描述,異步生成器函數會在 異步生成器函數 一節中單獨介紹。
When a generator function is called, it returns an iterator known as a generator. That generator then controls the execution of the generator function. The execution starts when one of the generator's methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of expression_list to the generator's caller. By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. When the execution is resumed by calling one of the generator's methods, the function can proceed exactly as if the yield expression were just another external call. The value of the yield expression after resuming depends on the method which resumed the execution. If __next__() is used (typically via either a for or the next() builtin) then the result is None. Otherwise, if send() is used, then the result will be the value passed in to that method.
所有這些使生成器函數與協程非常相似;它們 yield 多次,它們具有多個入口點,並且它們的執行可以被掛起。唯一的區別是生成器函數不能控制在它在 yield 後交給哪裡繼續執行;控制權總是轉移到生成器的調用者。
在 try 結構中的任何位置都允許yield表達式。如果生成器在(因為引用計數到零或是因為被垃圾回收)銷毀之前沒有恢復執行,將調用生成器-迭代器的 close() 方法. close 方法允許任何掛起的 finally 子句執行。
當使用 yield from <expr>
時,所提供的表達式必須是一個可迭代對象。 迭代該可迭代對象所產生的值會被直接傳遞給當前生成器方法的調用者。 任何通過 send() 傳入的值以及任何通過 throw() 傳入的異常如果有適當的方法則會被傳給下層迭代器。 如果不是這種情況,那麼 send() 將引發 AttributeError 或 TypeError,而 throw() 將立即引發所轉入的異常。
當下層迭代器完成時,被引發的 StopIteration 實例的 value
屬性會成為 yield 表達式的值。 它可以在引發 StopIteration 時被顯式地設置,也可以在子迭代器是一個生成器時自動地設置(通過從子生成器返回一個值)。
在 3.3 版更改: 添加
yield from <expr>
以委托控制流給一個子迭代器。
當yield表達式是賦值語句右側的唯一表達式時,括號可以省略。
參見
PEP 255 - 簡單生成器
在 Python 中加入生成器和 yield 語句的提議。
PEP 342 - 通過增強型生成器實現協程
增強生成器 API 和語法的提議,使其可以被用作簡單的協程。
PEP 380 - 委托給子生成器的語法
The proposal to introduce the yield_from
syntax, making delegation to subgenerators easy.
PEP 525 - 異步生成器
通過給協程函數加入生成器功能對 PEP 492 進行擴展的提議。
6.2.9.1. 生成器-迭代器的方法
這個子小節描述了生成器迭代器的方法。 它們可被用於控制生成器函數的執行。
請注意在生成器已經在執行時調用以下任何方法都會引發 ValueError 異常。
generator.__next__()
Starts the execution of a generator function or resumes it at the last executed yield expression. When a generator function is resumed with a __next__() method, the current yield expression always evaluates to None. The execution then continues to the next yield expression, where the generator is suspended again, and the value of the expression_list is returned to __next__()'s caller. If the generator exits without yielding another value, a StopIteration exception is raised.
此方法通常是隱式地調用,例如通過 for 循環或是內置的 next() 函數。
generator.send(value)
恢復執行並向生成器函數“發送”一個值。 value 參數將成為當前 yield 表達式的結果。 send() 方法會返回生成器所產生的下一個值,或者如果生成器沒有產生下一個值就退出則會引發 StopIteration。 當調用 send() 來啟動生成器時,它必須以 None 作為調用參數,因為這時沒有可以接收值的 yield 表達式。
generator.throw(value)
generator.throw(type[, value[, traceback]])
Raises an exception at the point where the generator was paused, and returns the next value yielded by the generator function. If the generator exits without yielding another value, a StopIteration exception is raised. If the generator function does not catch the passed-in exception, or raises a different exception, then that exception propagates to the caller.
In typical use, this is called with a single exception instance similar to the way the raise keyword is used.
For backwards compatibility, however, the second signature is supported, following a convention from older versions of Python. The type argument should be an exception class, and value should be an exception instance. If the value is not provided, the type constructor is called to get an instance. If traceback is provided, it is set on the exception, otherwise any existing __traceback__
attribute stored in value may be cleared.
generator.close()
在生成器函數暫停的位置引發 GeneratorExit。 如果之後生成器函數正常退出、關閉或引發 GeneratorExit (由於未捕獲該異常) 則關閉並返回其調用者。 如果生成器產生了一個值,關閉會引發 RuntimeError。 如果生成器引發任何其他異常,它會被傳播給調用者。 如果生成器已經由於異常或正常退出則 close() 不會做任何事。
6.2.9.2. 例子
這裡是一個簡單的例子,演示了生成器和生成器函數的行為:
>>>
>>> def echo(value=None): ... print("Execution starts when 'next()' is called for the first time.") ... try: ... while True: ... try: ... value = (yield value) ... except Exception as e: ... value = e ... finally: ... print("Don't forget to clean up when 'close()' is called.") ... >>> generator = echo(1) >>> print(next(generator)) Execution starts when 'next()' is called for the first time. 1 >>> print(next(generator)) None >>> print(generator.send(2)) 2 >>> generator.throw(TypeError, "spam") TypeError('spam',) >>> generator.close() Don't forget to clean up when 'close()' is called.
對於 yield from
的例子, 參見“Python 有什麼新變化”中的 PEP 380: 委托給子生成器的語法。
6.2.9.3. 異步生成器函數
在一個使用 async def 定義的函數或方法中出現的 yield 表達式會進一步將該函數定義為一個 asynchronous generator 函數。
當一個異步生成器函數被調用時,它會返回一個名為異步生成器對象的異步迭代器。 此對象將在之後控制該生成器函數的執行。 異步生成器對象通常被用在協程函數的 async for 語句中,類似於在 for 語句中使用生成器對象。
Calling one of the asynchronous generator's methods returns an awaitable object, and the execution starts when this object is awaited on. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of expression_list to the awaiting coroutine. As with a generator, suspension means that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. When the execution is resumed by awaiting on the next object returned by the asynchronous generator's methods, the function can proceed exactly as if the yield expression were just another external call. The value of the yield expression after resuming depends on the method which resumed the execution. If __anext__() is used then the result is None. Otherwise, if asend() is used, then the result will be the value passed in to that method.
如果一個異步生成器恰好因 break、調用方任務被取消,或是其他異常而提前退出,生成器的異步清理代碼將會運行並可能引發異常或訪問意外上下文中的上下文變量 -- 也許是在它所依賴的任務的生命周期之後,或是在異步生成器垃圾回收鉤子被調用時的事件循環關閉期間。 為了防止這種情況,調用方必須通過調用 aclose() 方法來顯式地關閉異步生成器以終結生成器並最終從事件循環中將其分離。
在異步生成器函數中,yield 表達式允許出現在 try 結構的任何位置。 但是,如果一個異步生成器在其被終結(由於引用計數達到零或被作為垃圾回收)之前未被恢復,則then a yield expression within a try
結構中的 yield 表達式可能導致掛起的 finally 子句執行失敗。 在此情況下,應由運行該異步生成器的事件循環或任務調度器來負責調用異步生成器-迭代器的 aclose() 方法並運行所返回的協程對象,從而允許任何掛起的 finally
子句得以執行。
為了能在事件循環終結時執行最終化處理,事件循環應當定義一個 終結器 函數,它接受一個異步生成器迭代器並將調用 aclose() 且執行該協程。 這個 終結器 可以通過調用 sys.set_asyncgen_hooks() 來注冊。 當首次迭代時,異步生成器迭代器將保存已注冊的 終結器 以便在最終化時調用。 有關 終結器 方法的參考示例請查看在 Lib/asyncio/base_events.py 的中的 asyncio.Loop.shutdown_asyncgens
實現。
yield from <expr>
表達式如果在異步生成器函數中使用會引發語法錯誤。
6.2.9.4. 異步生成器-迭代器方法
這個子小節描述了異步生成器迭代器的方法,它們可被用於控制生成器函數的執行。
coroutine agen.__anext__()
Returns an awaitable which when run starts to execute the asynchronous generator or resumes it at the last executed yield expression. When an asynchronous generator function is resumed with an __anext__() method, the current yield expression always evaluates to None in the returned awaitable, which when run will continue to the next yield expression. The value of the expression_list of the yield expression is the value of the StopIteration exception raised by the completing coroutine. If the asynchronous generator exits without yielding another value, the awaitable instead raises a StopAsyncIteration exception, signalling that the asynchronous iteration has completed.
此方法通常是通過 async for 循環隱式地調用。
coroutine agen.asend(value)
返回一個可等待對象,它在運行時會恢復該異步生成器的執行。 與生成器的 send() 方法一樣,此方法會“發送”一個值給異步生成器函數,其 value 參數會成為當前 yield 表達式的結果值。 asend() 方法所返回的可等待對象將返回生成器產生的下一個值,其值為所引發的 StopIteration,或者如果異步生成器沒有產生下一個值就退出則引發 StopAsyncIteration。 當調用 asend() 來啟動異步生成器時,它必須以 None 作為調用參數,因為這時沒有可以接收值的 yield 表達式。
coroutine agen.athrow(type[, value[, traceback]])
返回一個可等待對象,它會在異步生成器暫停的位置引發 type
類型的異常,並返回該生成器函數所產生的下一個值,其值為所引發的 StopIteration 異常。 如果異步生成器沒有產生下一個值就退出,則將由該可等待對象引發 StopAsyncIteration 異步。 如果生成器函數沒有捕獲傳入的異常,或引發了另一個異常,則當可等待對象運行時該異常會被傳播給可等待對象的調用者。
coroutine agen.aclose()
返回一個可等待對象,它會在運行時向異步生成器函數暫停的位置拋入一個 GeneratorExit。 如果該異步生成器函數正常退出、關閉或引發 GeneratorExit (由於未捕獲該異常) 則返回的可等待對象將引發 StopIteration 異常。 後續調用異步生成器所返回的任何其他可等待對象將引發 StopAsyncIteration 異常。 如果異步生成器產生了一個值,該可等待對象會引發 RuntimeError。 如果異步生成器引發任何其他異常,它會被傳播給可等待對象的調用者。 如果異步生成器已經由於異常或正常退出則後續調用 aclose() 將返回一個不會做任何事的可等待對象。
原型代表編程語言中最緊密綁定的操作。 它們的句法如下:
primary ::= atom | attributeref | subscription | slicing | call
屬性引用是後面帶有一個句點加一個名稱的原型:
attributeref ::= primary "." identifier
此原型必須求值為一個支持屬性引用的類型的對象,多數對象都支持屬性引用。 隨後該對象會被要求產生以指定標識符為名稱的屬性。 這個產生過程可通過重載 __getattr__()
方法來自定義。 如果這個屬性不可用,則將引發 AttributeError 異常。 否則的話,所產生對象的類型和值會根據該對象來確定。 對同一屬性引用的多次求值可能產生不同的對象。
The subscription of an instance of a container class will generally select an element from the container. The subscription of a generic class will generally return a GenericAlias object.
subscription ::= primary "[" expression_list "]"
When an object is subscripted, the interpreter will evaluate the primary and the expression list.
The primary must evaluate to an object that supports subscription. An object may support subscription through defining one or both of __getitem__() and __class_getitem__(). When the primary is subscripted, the evaluated result of the expression list will be passed to one of these methods. For more details on when __class_getitem__
is called instead of __getitem__
, see __class_getitem__ versus __getitem__.
If the expression list contains at least one comma, it will evaluate to a tuple containing the items of the expression list. Otherwise, the expression list will evaluate to the value of the list's sole member.
For built-in objects, there are two types of objects that support subscription via __getitem__():
Mappings. If the primary is a mapping, the expression list must evaluate to an object whose value is one of the keys of the mapping, and the subscription selects the value in the mapping that corresponds to that key. An example of a builtin mapping class is the dict class.
Sequences. If the primary is a sequence, the expression list must evaluate to an int or a slice (as discussed in the following section). Examples of builtin sequence classes include the str, list and tuple classes.
The formal syntax makes no special provision for negative indices in sequences. However, built-in sequences all provide a __getitem__() method that interprets negative indices by adding the length of the sequence to the index so that, for example, x[-1]
selects the last item of x
. The resulting value must be a nonnegative integer less than the number of items in the sequence, and the subscription selects the item whose index is that value (counting from zero). Since the support for negative indices and slicing occurs in the object's __getitem__()
method, subclasses overriding this method will need to explicitly add that support.
A string is a special kind of sequence whose items are characters. A character is not a separate data type but a string of exactly one character.
切片就是在序列對象(字符串、元組或列表)中選擇某個范圍內的項。 切片可被用作表達式以及賦值或 del 語句的目標。 切片的句法如下:
slicing ::= primary "[" slice_list "]" slice_list ::= slice_item ("," slice_item)* [","] slice_item ::= expression | proper_slice proper_slice ::= [lower_bound] ":" [upper_bound] [ ":" [stride] ] lower_bound ::= expression upper_bound ::= expression stride ::= expression
此處的正式句法中存在一點歧義:任何形似表達式列表的東西同樣也會形似切片列表,因此任何抽取操作也可以被解析為切片。 為了不使句法更加復雜,於是通過定義將此情況解析為抽取優先於解析為切片來消除這種歧義(切片列表未包含正確的切片就屬於此情況)。
切片的語義如下所述。 元型通過一個根據下面的切片列表來構造的鍵進行索引(與普通抽取一樣使用 __getitem__()
方法)。 如果切片列表包含至少一個逗號,則鍵將是一個包含切片項轉換的元組;否則的話,鍵將是單個切片項的轉換。 切片項如為一個表達式,則其轉換就是該表達式。 一個正確切片的轉換就是一個切片對象(參見 標准類型層級結構 一節),該對象的 start
, stop
和 step
屬性將分別為表達式所給出的下界、上界和步長值,省略的表達式將用 None
來替換。
所謂調用就是附帶可能為空的一系列 參數 來執行一個可調用對象 (例如 function):
call ::= primary "(" [argument_list [","] | comprehension] ")" argument_list ::= positional_arguments ["," starred_and_keywords] ["," keywords_arguments] | starred_and_keywords ["," keywords_arguments] | keywords_arguments positional_arguments ::= positional_item ("," positional_item)* positional_item ::= assignment_expression | "*" expression starred_and_keywords ::= ("*" expression | keyword_item) ("," "*" expression | "," keyword_item)* keywords_arguments ::= (keyword_item | "**" expression) ("," keyword_item | "," "**" expression)* keyword_item ::= identifier "=" expression
一個可選項為在位置和關鍵字參數後加上逗號而不影響語義。
此原型必須求值為一個可調用對象(用戶定義的函數,內置函數,內置對象的方法,類對象,類實例的方法以及任何具有 __call__()
方法的對象都是可調用對象)。 所有參數表達式將在嘗試調用前被求值。 請參閱 函數定義 一節了解正式的 parameter 列表句法。
If keyword arguments are present, they are first converted to positional arguments, as follows. First, a list of unfilled slots is created for the formal parameters. If there are N positional arguments, they are placed in the first N slots. Next, for each keyword argument, the identifier is used to determine the corresponding slot (if the identifier is the same as the first formal parameter name, the first slot is used, and so on). If the slot is already filled, a TypeError exception is raised. Otherwise, the argument is placed in the slot, filling it (even if the expression is None
, it fills the slot). When all arguments have been processed, the slots that are still unfilled are filled with the corresponding default value from the function definition. (Default values are calculated, once, when the function is defined; thus, a mutable object such as a list or dictionary used as default value will be shared by all calls that don't specify an argument value for the corresponding slot; this should usually be avoided.) If there are any unfilled slots for which no default value is specified, a TypeError exception is raised. Otherwise, the list of filled slots is used as the argument list for the call.
CPython implementation detail: 某些實現可能提供位置參數沒有名稱的內置函數,即使它們在文檔說明的場合下有“命名”,因此不能以關鍵字形式提供參數。 在 CPython 中,以 C 編寫並使用 PyArg_ParseTuple() 來解析其參數的函數實現就屬於這種情況。
如果存在比正式參數空位多的位置參數,將會引發 TypeError 異常,除非有一個正式參數使用了 *identifier
句法;在此情況下,該正式參數將接受一個包含了多余位置參數的元組(如果沒有多余位置參數則為一個空元組)。
如果任何關鍵字參數沒有與之對應的正式參數名稱,將會引發 TypeError 異常,除非有一個正式參數使用了 **identifier
句法,該正式參數將接受一個包含了多余關鍵字參數的字典(使用關鍵字作為鍵而參數值作為與鍵對應的值),如果沒有多余關鍵字參數則為一個(新的)空字典。
如果函數調用中出現了 *expression
句法,expression
必須求值為一個 iterable。 來自該可迭代對象的元素會被當作是額外的位置參數。 對於 f(x1, x2, *y, x3, x4)
調用,如果 y 求值為一個序列 y1, ..., yM,則它就等價於一個帶有 M+4 個位置參數 x1, x2, y1, ..., yM, x3, x4 的調用。
這樣做的一個後果是雖然 *expression
句法可能出現於顯式的關鍵字參數 之後,但它會在關鍵字參數(以及任何 **expression
參數 -- 見下文) 之前 被處理。 因此:
>>>
>>> def f(a, b): ... print(a, b) ... >>> f(b=1, *(2,)) 2 1 >>> f(a=1, *(2,)) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f() got multiple values for keyword argument 'a' >>> f(1, *(2,)) 1 2
It is unusual for both keyword arguments and the *expression
syntax to be used in the same call, so in practice this confusion does not often arise.
如果函數調用中出現了 **expression
句法,expression
必須求值為一個 mapping,其內容會被當作是額外的關鍵字參數。 如果一個關鍵字已存在(作為顯式關鍵字參數,或來自另一個拆包),則將引發 TypeError 異常。
使用 *identifier
或 **identifier
句法的正式參數不能被用作位置參數空位或關鍵字參數名稱。
在 3.5 版更改: 函數調用接受任意數量的 *
和 **
拆包,位置參數可能跟在可迭代對象拆包 (*
) 之後,而關鍵字參數可能跟在字典拆包 (**
) 之後。 由 PEP 448 發起最初提議。
除非引發了異常,調用總是會有返回值,返回值也可能為 None
。 返回值的計算方式取決於可調用對象的類型。
如果類型為---
用戶自定義函數:
函數的代碼塊會被執行,並向其傳入參數列表。 代碼塊所做的第一件事是將正式形參綁定到對應參數;相關描述參見 函數定義 一節。 當代碼塊執行 return 語句時,由其指定函數調用的返回值。
內置函數或方法:
具體結果依賴於解釋器;有關內置函數和方法的描述參見 內置函數。
類對象:
返回該類的一個新實例。
類實例方法:
調用相應的用戶自定義函數,向其傳入的參數列表會比調用的參數列表多一項:該實例將成為第一個參數。
類實例:
該類必須定義有 __call__()
方法;作用效果將等價於調用該方法。
掛起 coroutine 的執行以等待一個 awaitable 對象。 只能在 coroutine function 內部使用。
await_expr ::= "await" primary
3.5 新版功能.
冪運算符的綁定比在其左側的一元運算符更緊密;但綁定緊密程度不及在其右側的一元運算符。 句法如下:
power ::= (await_expr | primary) ["**" u_expr]
因此,在一個未加圓括號的冪運算符和單目運算符序列中,運算符將從右向左求值(這不會限制操作數的求值順序): -1**2
結果將為 -1
。
冪運算符與附帶兩個參數調用內置 pow() 函數具有相同的語義:結果為對其左參數進行其右參數所指定冪次的乘方運算。 數值參數會先轉換為相同類型,結果也為轉換後的類型。
對於 int 類型的操作數,結果將具有與操作數相同的類型,除非第二個參數為負數;在那種情況下,所有參數會被轉換為 float 類型並輸出 float 類型的結果。 例如,10**2
返回 100
,而 10**-2
返回 0.01
。
對 0.0
進行負數冪次運算將導致 ZeroDivisionError。 對負數進行分數冪次運算將返回 complex 數值。 (在早期版本中這將引發 ValueError。)
此運算符可使用特殊的 __pow__()
方法來自定義。
所有算術和位運算具有相同的優先級:
u_expr ::= power | "-" u_expr | "+" u_expr | "~" u_expr
一元的 -
(負值) 運算符會產生其數字參數的負值;該運算可通過 __neg__()
特殊方法來重載。
一元的 +
(正值) 運算符會原樣輸出其數字參數;該運算可通過 __pos__()
特殊方法來重載。
一元的 ~
(取反) 運算符會對其整數參數按位取反。 x
的按位取反被定義為 -(x+1)
。 它只作用於整數或是重載了 __invert__()
特殊方法的自定義對象。
在所有三種情況下,如果參數的類型不正確,將引發 TypeError 異常。
二元算術運算符遵循傳統的優先級。 請注意某些此類運算符也作用於特定的非數字類型。 除冪運算符以外只有兩個優先級別,一個作用於乘法型運算符,另一個作用於加法型運算符:
m_expr ::= u_expr | m_expr "*" u_expr | m_expr "@" m_expr | m_expr "//" u_expr | m_expr "/" u_expr | m_expr "%" u_expr a_expr ::= m_expr | a_expr "+" m_expr | a_expr "-" m_expr
運算符 *
(乘) 將輸出其參數的乘積。 兩個參數或者必須都為數字,或者一個參數必須為整數而另一個參數必須為序列。 在前一種情況下,兩個數字將被轉換為相同類型然後相乘。 在後一種情況下,將執行序列的重復;重復因子為負數將輸出空序列。
此運算可使用特殊的 __mul__()
和 __rmul__()
方法來自定義。
運算符 @
(at) 的目標是用於矩陣乘法。 沒有內置 Python 類型實現此運算符。
3.5 新版功能.
運算符 /
(除) 和 //
(整除) 將輸出其參數的商。 兩個數字參數將先被轉換為相同類型。 整數相除會輸出一個 float 值,整數相整除的結果仍是整數;整除的結果就是使用 'floor' 函數進行算術除法的結果。 除以零的運算將引發 ZeroDivisionError 異常。
This operation can be customized using the special __truediv__()
and __floordiv__()
methods.
運算符 %
(模) 將輸出第一個參數除以第二個參數的余數。 兩個數字參數將先被轉換為相同類型。 右參數為零將引發 ZeroDivisionError 異常。 參數可以為浮點數,例如 3.14%0.7
等於 0.34
(因為 3.14
等於 4*0.7 + 0.34
)。 模運算符的結果的正負總是與第二個操作數一致(或是為零);結果的絕對值一定小於第二個操作數的絕對值 1。
整除與模運算符的聯系可通過以下等式說明: x == (x//y)*y + (x%y)
。 此外整除與模也可通過內置函數 divmod() 來同時進行: divmod(x, y) == (x//y, x%y)
。 2。
除了對數字執行模運算,運算符 %
還被字符串對象重載用於執行舊式的字符串格式化(又稱插值)。 字符串格式化句法的描述參見 Python 庫參考的 printf 風格的字符串格式化 一節。
取余 運算可使用特殊的 __mod__()
方法來自定義。
整除運算符,模運算符和 divmod() 函數未被定義用於復數。 如果有必要可以使用 abs() 函數將其轉換為浮點數。
運算符 +
(addition) 將輸出其參數的和。 兩個參數或者必須都為數字,或者都為相同類型的序列。 在前一種情況下,兩個數字將被轉換為相同類型然後相加。 在後一種情況下,將執行序列拼接操作。
此運算可使用特殊的 __add__()
和 __radd__()
方法來自定義。
運算符 -
(減) 將輸出其參數的差。 兩個數字參數將先被轉換為相同類型。
此運算可使用特殊的 __sub__()
方法來自定義。
移位運算的優先級低於算術運算:
shift_expr ::= a_expr | shift_expr ("<<" | ">>") a_expr
這些運算符接受整數參數。 它們會將第一個參數左移或右移第二個參數所指定的比特位數。
此運算可使用特殊的 __lshift__()
和 __rshift__()
方法來自定義。
右移 n 位被定義為被 pow(2,n)
整除。 左移 n 位被定義為乘以 pow(2,n)
。
三種位運算具有各不相同的優先級:
and_expr ::= shift_expr | and_expr "&" shift_expr xor_expr ::= and_expr | xor_expr "^" and_expr or_expr ::= xor_expr | or_expr "|" xor_expr
&
運算符會對其參數執行按位 AND,參數必須都為整數或者其中之一必須為重載了 __and__()
或 __rand__()
特殊方法的自定義對象。
^
運算符會對其參數執行按位 XOR (異 OR),參數必須都為整數或者其中之一必須為重載了 __xor__()
或 __rxor__()
特殊方法的自定義對象。
|
運算符會對其參數執行按位 (合並) OR,參數必須都為整數或者其中之一必須為重載了 __or__()
或 __ror__()
特殊方法的自定義對象。
與 C 不同,Python 中所有比較運算的優先級相同,低於任何算術、移位或位運算。 另一個與 C 不同之處在於 a < b < c
這樣的表達式會按傳統算術法則來解讀:
comparison ::= or_expr (comp_operator or_expr)* comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!=" | "is" ["not"] | ["not"] "in"
比較運算會產生布爾值: True
或 False
。 自定義的 富比較方法 可能返回非布爾值。 在此情況下 Python 將在布爾運算上下文中對該值調用 bool()。
比較運算可以任意串連,例如 x < y <= z
等價於 x < y and y <= z
,除了 y
只被求值一次(但在兩種寫法下當 x < y
值為假時 z
都不會被求值)。
正式的說法是這樣:如果 a, b, c, ..., y, z 為表達式而 op1, op2, ..., opN 為比較運算符,則 a op1 b op2 c ... y opN z
就等價於 a op1 b and b op2 c and ... y opN z
,不同點在於每個表達式最多只被求值一次。
請注意 a op1 b op2 c
不意味著在 a 和 c 之間進行任何比較,因此,如 x < y > z
這樣的寫法是完全合法的(雖然也許不太好看)。
運算符 <
, >
, ==
, >=
, <=
和 !=
將比較兩個對象的值。 兩個對象不要求為相同類型。
對象、值與類型 一章已說明對象都有相應的值(還有類型和標識號)。 對象值在 Python 中是一個相當抽象的概念:例如,對象值並沒有一個規范的訪問方法。 而且,對象值並不要求具有特定的構建方式,例如由其全部數據屬性組成等。 比較運算符實現了一個特定的對象值概念。 人們可以認為這是通過實現對象比較間接地定義了對象值。
由於所有類型都是 object 的(直接或間接)子類型,它們都從 object 繼承了默認的比較行為。 類型可以通過實現 豐富比較方法 例如 __lt__()
來定義自己的比較行為,詳情參見 基本定制。
默認的一致性比較 (==
和 !=
) 是基於對象的標識號。 因此,具有相同標識號的實例一致性比較結果為相等,具有不同標識號的實例一致性比較結果為不等。 規定這種默認行為的動機是希望所有對象都應該是自反射的 (即 x is y
就意味著 x == y
)。
次序比較 (<
, >
, <=
和 >=
) 默認沒有提供;如果嘗試比較會引發 TypeError。 規定這種默認行為的原因是缺少與一致性比較類似的固定值。
按照默認的一致性比較行為,具有不同標識號的實例總是不相等,這可能不適合某些對象值需要有合理定義並有基於值的一致性的類型。 這樣的類型需要定制自己的比較行為,實際上,許多內置類型都是這樣做的。
以下列表描述了最主要內置類型的比較行為。
內置數值類型 (數字類型 --- int, float, complex) 以及標准庫類型 fractions.Fraction 和 decimal.Decimal 可進行類型內部和跨類型的比較,例外限制是復數不支持次序比較。 在類型相關的限制以內,它們會按數學(算法)規則正確進行比較且不會有精度損失。
非數字值 float('NaN')
和 decimal.Decimal('NaN')
屬於特例。 任何數字與非數字值的排序比較均返回假值。 還有一個反直覺的結果是非數字值不等於其自身。 舉例來說,如果 x = float('NaN')
則 3 < x
, x < 3
和 x == x
均為假值,而 x != x
則為真值。 此行為是遵循 IEEE 754 標准的。
None
和 NotImplemented
都是單例對象。 PEP 8 建議單例對象的比較應當總是通過 is
或 is not
而不是等於運算符來進行。
二進制碼序列 (bytes 或 bytearray 的實例) 可進行類型內部和跨類型的比較。 它們使用其元素的數字值按字典順序進行比較。
字符串 (str 的實例) 使用其字符的 Unicode 碼位數字值 (內置函數 ord() 的結果) 按字典順序進行比較。 3
字符串和二進制碼序列不能直接比較。
序列 (tuple, list 或 range 的實例) 只可進行類型內部的比較,range 還有一個限制是不支持次序比較。 以上對象的跨類型一致性比較結果將是不相等,跨類型次序比較將引發 TypeError。
序列比較是按字典序對相應元素進行逐個比較。 內置容器通常設定同一對象與其自身是相等的。 這使得它們能跳過同一對象的相等性檢測以提升運行效率並保持它們的內部不變性。
內置多項集間的字典序比較規則如下:
兩個多項集若要相等,它們必須為相同類型、相同長度,並且每對相應的元素都必須相等(例如,[1,2] == (1,2)
為假值,因為類型不同)。
對於支持次序比較的多項集,排序與其第一個不相等元素的排序相同(例如 [1,2,x] <= [1,2,y]
的值與``x <= y`` 相同)。 如果對應元素不存在,較短的多項集排序在前(例如 [1,2] < [1,2,3]
為真值)。
兩個映射 (dict 的實例) 若要相等,必須當且僅當它們具有相同的 (鍵, 值) 對。 鍵和值的一致性比較強制規定自反射性。
次序比較 (<
, >
, <=
和 >=
) 將引發 TypeError。
集合 (set 或 frozenset 的實例) 可進行類型內部和跨類型的比較。
它們將比較運算符定義為子集和超集檢測。 這類關系沒有定義完全排序(例如 {1,2}
和 {2,3}
兩個集合不相等,即不為彼此的子集,也不為彼此的超集。 相應地,集合不適宜作為依賴於完全排序的函數的參數(例如如果給出一個集合列表作為 min(), max() 和 sorted() 的輸入將產生未定義的結果)。
集合的比較強制規定其元素的自反射性。
大多數其他內置類型沒有實現比較方法,因此它們會繼承默認的比較行為。
在可能的情況下,用戶定義類在定制其比較行為時應當遵循一些一致性規則:
相等比較應該是自反射的。 換句話說,相同的對象比較時應該相等:
x is y
意味著x == y
比較應該是對稱的。 換句話說,下列表達式應該有相同的結果:
x == y
和y == x
x != y
和y != x
x < y
和y > x
x <= y
和y >= x
比較應該是可傳遞的。 下列(簡要的)例子顯示了這一點:
x > y and y > z
意味著x > z
x < y and y <= z
意味著x < z
反向比較應該導致布爾值取反。 換句話說,下列表達式應該有相同的結果:
x == y
和not x != y
x < y
和not x >= y
(對於完全排序)
x > y
和not x <= y
(對於完全排序)
最後兩個表達式適用於完全排序的多項集(即序列而非集合或映射)。 另請參閱 total_ordering() 裝飾器。
hash() 的結果應該與是否相等一致。 相等的對象應該或者具有相同的哈希值,或者標記為不可哈希。
Python 並不強制要求這些一致性規則。 實際上,非數字值就是一個不遵循這些規則的例子。
運算符 in 和 not in 用於成員檢測。 如果 x 是 s 的成員則 x in s
求值為 True
,否則為 False
。 x not in s
返回 x in s
取反後的值。 所有內置序列和集合類型以及字典都支持此運算,對於字典來說 in
檢測其是否有給定的鍵。 對於 list, tuple, set, frozenset, dict 或 collections.deque 這樣的容器類型,表達式 x in y
等價於 any(x is e or x == e for e in y)
。
對於字符串和字節串類型來說,當且僅當 x 是 y 的子串時 x in y
為 True
。 一個等價的檢測是 y.find(x) != -1
。 空字符串總是被視為任何其他字符串的子串,因此 "" in "abc"
將返回 True
。
對於定義了 __contains__()
方法的用戶自定義類來說,如果 y.__contains__(x)
返回真值則 x in y
返回 True
,否則返回 False
。
對於未定義 __contains__()
但定義了 __iter__()
的用戶自定義類來說,如果在對 y
進行迭代時產生了值 z
使得表達式 x is z or x == z
為真,則 x in y
為 True
。 如果在迭代期間引發了異常,則等同於 in 引發了該異常。
最後將會嘗試舊式的迭代協議:如果一個類定義了 __getitem__()
,則當且僅當存在非負整數索引號 i 使得 x is y[i] or x == y[i]
並且沒有更小的索引號引發 IndexError 異常時 x in y
為 True
。 (如果引發了任何其他異常,則等同於 in 引發了該異常)。
運算符 not in 被定義為具有與 in 相反的邏輯值。
運算符 is 和 is not 用於檢測對象的標識號:當且僅當 x 和 y 是同一對象時 x is y
為真。 一個對象的標識號可使用 id() 函數來確定。 x is not y
會產生相反的邏輯值。 4
or_test ::= and_test | or_test "or" and_test and_test ::= not_test | and_test "and" not_test not_test ::= comparison | "not" not_test
在執行布爾運算的情況下,或是當表達式被用於流程控制語句時,以下值會被解析為假值: False
, None
, 所有類型的數字零,以及空字符串和空容器(包括字符串、元組、列表、字典、集合與凍結集合)。 所有其他值都會被解析為真值。 用戶自定義對象可通過提供 __bool__()
方法來定制其邏輯值。
運算符 not 將在其參數為假值時產生 True
,否則產生 False
。
表達式 x and y
首先對 x 求值;如果 x 為假則返回該值;否則對 y 求值並返回其結果值。
表達式 x or y
首先對 x 求值;如果 x 為真則返回該值;否則對 y 求值並返回其結果值。
請注意 and 和 or 都不限制其返回的值和類型必須為 False
和 True
,而是返回最後被求值的操作數。 此行為是有必要的,例如假設 s
為一個當其為空時應被替換為某個默認值的字符串,表達式 s or 'foo'
將產生希望的值。 由於 not 必須創建一個新值,不論其參數為何種類型它都會返回一個布爾值(例如,not 'foo'
結果為 False
而非 ''
。)
assignment_expression ::= [identifier ":="] expression
An assignment expression (sometimes also called a "named expression" or "walrus") assigns an expression to an identifier, while also returning the value of the expression.
一個常見用例是在處理匹配的正則表達式的時候:
if matching := pattern.search(data): do_something(matching)
或者是在處理分塊的文件流的時候:
while chunk := file.read(9000): process(chunk)
3.8 新版功能: 請參閱 PEP 572 了解有關賦值表達式的詳情。
conditional_expression ::= or_test ["if" or_test "else" expression] expression ::= conditional_expression | lambda_expr
條件表達式(有時稱為“三元運算符”)在所有 Python 運算中具有最低的優先級。
表達式 x if C else y
首先是對條件 C 而非 x 求值。 如果 C 為真,x 將被求值並返回其值;否則將對 y 求值並返回其值。
請參閱 PEP 308 了解有關條件表達式的詳情。
lambda_expr ::= "lambda" [parameter_list] ":" expression
lambda 表達式(有時稱為 lambda 構型)被用於創建匿名函數。 表達式 lambda parameters: expression
會產生一個函數對象 。 該未命名對象的行為類似於用以下方式定義的函數:
def <lambda>(parameters): return expression
請參閱 函數定義 了解有關參數列表的句法。 請注意通過 lambda 表達式創建的函數不能包含語句或標注。
expression_list ::= expression ("," expression)* [","] starred_list ::= starred_item ("," starred_item)* [","] starred_expression ::= expression | (starred_item ",")* [starred_item] starred_item ::= assignment_expression | "*" or_expr
除了作為列表或集合顯示的一部分,包含至少一個逗號的表達式列表將生成一個元組。 元組的長度就是列表中表達式的數量。 表達式將從左至右被求值。
一個星號 *
表示 可迭代拆包。 其操作數必須為一個 iterable。 該可迭代對象將被拆解為迭代項的序列,並被包含於在拆包位置上新建的元組、列表或集合之中。
3.5 新版功能: 表達式列表中的可迭代對象拆包,最初由 PEP 448 提出。
末尾的逗號僅在創建單獨元組 (或稱 單例) 時需要;在所有其他情況下都是可選項。 沒有末尾逗號的單獨表達式不會創建一個元組,而是產生該表達式的值。 (要創建一個空元組,應使用一對內容為空的圓括號: ()
。)
Python 按從左至右的順序對表達式求值。 但注意在對賦值操作求值時,右側會先於左側被求值。
在以下幾行中,表達式將按其後綴的算術優先順序被求值。:
expr1, expr2, expr3, expr4 (expr1, expr2, expr3, expr4) {expr1: expr2, expr3: expr4} expr1 + expr2 * (expr3 - expr4) expr1(expr2, expr3, *expr4, **expr5) expr3, expr4 = expr1, expr2
下表對 Python 中運算符的優先順序進行了總結,從最高優先級(最先綁定)到最低優先級(最後綁定)。 相同單元格內的運算符具有相同優先級。 除非句法顯式地給出,否則運算符均指二元運算。 相同單元格內的運算符從左至右分組(除了冪運算是從右至左分組)。
請注意比較、成員檢測和標識號檢測均為相同優先級,並具有如 比較運算 一節所描述的從左至右串連特性。
運算符
描述
(expressions...)
,
[expressions...]
, {key: value...}
, {expressions...}
綁定或加圓括號的表達式,列表顯示,字典顯示,集合顯示
x[index]
, x[index:index]
, x(arguments...)
, x.attribute
抽取,切片,調用,屬性引用
await x
await 表達式
**
乘方 5
+x
, -x
, ~x
正,負,按位非 NOT
*
, @
, /
, //
, %
乘,矩陣乘,除,整除,取余 6
+
, -
加和減
<<
, >>
移位
&
按位與 AND
^
按位異或 XOR
|
按位或 OR
in, not in, is, is not, <
, <=
, >
, >=
, !=
, ==
比較運算,包括成員檢測和標識號檢測
not x
布爾邏輯非 NOT
and
布爾邏輯與 AND
or
布爾邏輯或 OR
if -- else
條件表達式
lambda
lambda 表達式
:=
賦值表達式
備注
1
雖然 abs(x%y) < abs(y)
在數學中必為真,但對於浮點數而言,由於捨入的存在,其在數值上未必為真。 例如,假設在某個平台上的 Python 浮點數為一個 IEEE 754 雙精度數值,為了使 -1e-100 % 1e100
具有與 1e100
相同的正負性,計算結果將是 -1e-100 + 1e100
,這在數值上正好等於 1e100
。 函數 math.fmod() 返回的結果則會具有與第一個參數相同的正負性,因此在這種情況下將返回 -1e-100
。 何種方式更適宜取決於具體的應用。
2
如果 x 恰好非常接近於 y 的整數倍,則由於捨入的存在 x//y
可能會比 (x-x%y)//y
大。 在這種情況下,Python 會返回後一個結果,以便保持令 divmod(x,y)[0] * y + x % y
盡量接近 x
.
3
Unicode 標准明確區分 碼位 (例如 U+0041) 和 抽象字符 (例如 "大寫拉丁字母 A")。 雖然 Unicode 中的大多數抽象字符都只用一個碼位來代表,但也存在一些抽象字符可使用由多個碼位組成的序列來表示。 例如,抽象字符 "帶有下加符的大寫拉丁字母 C" 可以用 U+00C7 碼位上的單個 預設字符 來表示,也可以用一個 U+0043 碼位上的 基礎字符 (大寫拉丁字母 C) 加上一個 U+0327 碼位上的 組合字符 (組合下加符) 組成的序列來表示。
對於字符串,比較運算符會按 Unicode 碼位級別進行比較。 這可能會違反人類的直覺。 例如,"\u00C7" == "\u0043\u0327"
為 False
,雖然兩個字符串都代表同一個抽象字符 "帶有下加符的大寫拉丁字母 C"。
要按抽象字符級別(即對人類來說更直觀的方式)對字符串進行比較,應使用 unicodedata.normalize()。
4
由於存在自動垃圾收集、空閒列表以及描述器的動態特性,你可能會注意到在特定情況下使用 is 運算符會出現看似不正常的行為,例如涉及到實例方法或常量之間的比較時就是如此。 更多信息請查看有關它們的文檔。
5
冪運算符 **
綁定的緊密程度低於在其右側的算術或按位一元運算符,也就是說 2**-1
為 0.5
。
6
%
運算符也被用於字符串格式化;在此場合下會使用同樣的優先級。