6. 表達式?

本章將解釋 Python 中組成表達式的各種元素的的含義。

語(yǔ)法注釋: 在本章和后續章節中,會(huì )使用擴展 BNF 標注來(lái)描述語(yǔ)法而不是詞法分析。 當(某種替代的)語(yǔ)法規則具有如下形式

name ::=  othername

并且沒(méi)有給出語(yǔ)義,則這種形式的 name 在語(yǔ)法上與 othername 相同。

6.1. 算術(shù)轉換?

當對下述某個(gè)算術(shù)運算符的描述中使用了“數值參數被轉換為普通類(lèi)型”這樣的說(shuō)法,這意味著(zhù)內置類(lèi)型的運算符實(shí)現采用了如下運作方式:

  • 如果任一參數為復數,另一參數會(huì )被轉換為復數;

  • 否則,如果任一參數為浮點(diǎn)數,另一參數會(huì )被轉換為浮點(diǎn)數;

  • 否則,兩者應該都為整數,不需要進(jìn)行轉換。

某些附加規則會(huì )作用于特定運算符(例如,字符串作為 '%' 運算符的左運算參數)。 擴展必須定義它們自己的轉換行為。

6.2. 原子?

“原子”指表達式的最基本構成元素。 最簡(jiǎn)單的原子是標識符和字面值。 以圓括號、方括號或花括號包括的形式在語(yǔ)法上也被歸類(lèi)為原子。 原子的句法為:

atom      ::=  identifier | literal | enclosure
enclosure ::=  parenth_form | list_display | dict_display | set_display
               | generator_expression | yield_atom

6.2.1. 標識符(名稱(chēng))?

作為原子出現的標識符叫做名稱(chēng)。 請參看 標識符和關(guān)鍵字 一節了解其詞法定義,以及 命名與綁定 獲取有關(guān)命名與綁定的文檔。

當名稱(chēng)被綁定到一個(gè)對象時(shí),對該原子求值將返回相應對象。 當名稱(chēng)未被綁定時(shí),嘗試對其求值將引發(fā) NameError 異常。

私有名稱(chēng)轉換: 當以文本形式出現在類(lèi)定義中的一個(gè)標識符以?xún)蓚€(gè)或更多下劃線(xiàn)開(kāi)頭并且不以?xún)蓚€(gè)或更多下劃線(xiàn)結尾,它會(huì )被視為該類(lèi)的 私有名稱(chēng)。 私有名稱(chēng)會(huì )在為其生成代碼之前被轉換為一種更長(cháng)的形式。 轉換時(shí)會(huì )插入類(lèi)名,移除打頭的下劃線(xiàn)再在名稱(chēng)前增加一個(gè)下劃線(xiàn)。 例如,出現在一個(gè)名為 Ham 的類(lèi)中的標識符 __spam 會(huì )被轉換為 _Ham__spam。 這種轉換獨立于標識符所使用的相關(guān)句法。 如果轉換后的名稱(chēng)太長(cháng)(超過(guò) 255 個(gè)字符),可能發(fā)生由具體實(shí)現定義的截斷。 如果類(lèi)名僅由下劃線(xiàn)組成,則不會(huì )進(jìn)行轉換。

6.2.2. 字面值?

Python 支持字符串和字節串字面值,以及幾種數字字面值:

literal ::=  stringliteral | bytesliteral
             | integer | floatnumber | imagnumber

對字面值求值將返回一個(gè)該值所對應類(lèi)型的對象(字符串、字節串、整數、浮點(diǎn)數、復數)。 對于浮點(diǎn)數和虛數(復數)的情況,該值可能為近似值。 詳情參見(jiàn) 字面值。

所有字面值都對應與不可變數據類(lèi)型,因此對象標識的重要性不如其實(shí)際值。 多次對具有相同值的字面值求值(不論是發(fā)生在程序文本的相同位置還是不同位置)可能得到相同對象或是具有相同值的不同對象。

6.2.3. 帶圓括號的形式?

帶圓括號的形式是包含在圓括號中的可選表達式列表。

parenth_form ::=  "(" [starred_expression] ")"

帶圓括號的表達式列表將返回該表達式列表所產(chǎn)生的任何東西:如果該列表包含至少一個(gè)逗號,它會(huì )產(chǎn)生一個(gè)元組;否則,它會(huì )產(chǎn)生該表達式列表所對應的單一表達式。

一對內容為空的圓括號將產(chǎn)生一個(gè)空的元組對象。 由于元組是不可變對象,因此適用與字面值相同的規則(即兩次出現的空元組產(chǎn)生的對象可能相同也可能不同)。

請注意元組并不是由圓括號構建,實(shí)際起作用的是逗號操作符。 例外情況是空元組,這時(shí)圓括號 才是 必須的 --- 允許在表達式中使用不帶圓括號的 "空" 會(huì )導致歧義,并會(huì )造成常見(jiàn)輸入錯誤無(wú)法被捕獲。

6.2.4. 列表、集合與字典的顯示?

為了構建列表、集合或字典,Python 提供了名為“顯示”的特殊句法,每個(gè)類(lèi)型各有兩種形式:

  • 第一種是顯式地列出容器內容

  • 第二種是通過(guò)一組循環(huán)和篩選指令計算出來(lái),稱(chēng)為 推導式。

推導式的常用句法元素為:

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]

推導式的結構是一個(gè)單獨表達式后面加至少一個(gè) for 子句以及零個(gè)或更多個(gè) forif 子句。 在這種情況下,新容器的元素產(chǎn)生方式是將每個(gè) forif 子句視為一個(gè)代碼塊,按從左至右的順序嵌套,然后每次到達最內層代碼塊時(shí)就對表達式進(jìn)行求值以產(chǎn)生一個(gè)元素。

不過(guò),除了最左邊 for 子句中的可迭代表達式,推導式是在另一個(gè)隱式嵌套的作用域內執行的。 這能確保賦給目標列表的名稱(chēng)不會(huì )“泄露”到外層的作用域。

最左邊的 for 子句中的可迭代對象表達式會(huì )直接在外層作用域中被求值,然后作為一個(gè)參數被傳給隱式嵌套的作用域。 后續的 for 子句以及最左側 for 子句中的任何篩選條件不能在外層作用域中被求值,因為它們可能依賴(lài)于從最左側可迭代對象中獲得的值。 例如: [x*y for x in range(10) for y in range(x, x+10)]。

為了確保推導式得出的結果總是一個(gè)類(lèi)型正確的容器,在隱式嵌套作用域內禁止使用 yieldyield 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 版更改: yieldyield from 在隱式嵌套的作用域中已被禁用。

在 3.11 版更改: Asynchronous comprehensions are now allowed inside comprehensions in asynchronous functions. Outer comprehensions implicitly become asynchronous.

6.2.5. 列表顯示?

列表顯示是一個(gè)用方括號括起來(lái)的可能為空的表達式系列:

list_display ::=  "[" [starred_list | comprehension] "]"

列表顯示會(huì )產(chǎn)生一個(gè)新的列表對象,其內容通過(guò)一系列表達式或一個(gè)推導式來(lái)指定。 當提供由逗號分隔的一系列表達式時(shí),其元素會(huì )從左至右被求值并按此順序放入列表對象。 當提供一個(gè)推導式時(shí),列表會(huì )根據推導式所產(chǎn)生的結果元素進(jìn)行構建。

6.2.6. 集合顯示?

集合顯示是用花括號標明的,與字典顯示的區別在于沒(méi)有冒號分隔的鍵和值:

set_display ::=  "{" (starred_list | comprehension) "}"

集合顯示會(huì )產(chǎn)生一個(gè)新的可變集合對象,其內容通過(guò)一系列表達式或一個(gè)推導式來(lái)指定。 當提供由逗號分隔的一系列表達式時(shí),其元素會(huì )從左至右被求值并加入到集合對象。 當提供一個(gè)推導式時(shí),集合會(huì )根據推導式所產(chǎn)生的結果元素進(jìn)行構建。

空集合不能用 {} 來(lái)構建;該字面值所構建的是一個(gè)空字典。

6.2.7. 字典顯示?

字典顯示是一個(gè)用花括號括起來(lái)的可能為空的鍵/數據對系列:

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

字典顯示會(huì )產(chǎn)生一個(gè)新的字典對象。

如果給出一個(gè)由逗號分隔的鍵/數據對序列,它們會(huì )從左至右被求值以定義字典的條目:每個(gè)鍵對象會(huì )被用作在字典中存放相應數據的鍵。 這意味著(zhù)你可以在鍵/數據對序列中多次指定相同的鍵,最終字典的值將由最后一次給出的鍵決定。

雙星號 ** 表示 字典拆包。 它的操作數必須是一個(gè) mapping。 每個(gè)映射項被會(huì )加入新的字典。 后續的值會(huì )替代先前的鍵/數據對和先前的字典拆包所設置的值。

3.5 新版功能: 拆包到字典顯示,最初由 PEP 448 提出。

字典推導式與列表和集合推導式有所不同,它需要以冒號分隔的兩個(gè)表達式,后面帶上標準的 "for" 和 "if" 子句。 當推導式被執行時(shí),作為結果的鍵和值元素會(huì )按它們的產(chǎn)生順序被加入新的字典。

對鍵取值類(lèi)型的限制已列在之前的 標準類(lèi)型層級結構 一節中。 (總的說(shuō)來(lái),鍵的類(lèi)型應該為 hashable,這就把所有可變對象都排除在外。) 重復鍵之間的沖突不會(huì )被檢測;指定鍵所保存的最后一個(gè)數據 (即在顯示中排最右邊的文本) 為最終有效數據。

在 3.8 版更改: 在 Python 3.8 之前的字典推導式中,并沒(méi)有定義好鍵和值的求值順序。 在 CPython 中,值會(huì )先于鍵被求值。 根據 PEP 572 的提議,從 3.8 開(kāi)始,鍵會(huì )先于值被求值。

6.2.8. 生成器表達式?

生成器表達式是用圓括號括起來(lái)的緊湊形式生成器標注。

generator_expression ::=  "(" expression comp_for ")"

生成器表達式會(huì )產(chǎn)生一個(gè)新的生成器對象。 其句法與推導式相同,區別在于它是用圓括號而不是用方括號或花括號括起來(lái)的。

在生成器表達式中使用的變量會(huì )在為生成器對象調用 __next__() 方法的時(shí)候以惰性方式被求值(即與普通生成器相同的方式)。 但是,最左側 for 子句內的可迭代對象是會(huì )被立即求值的,因此它所造成的錯誤會(huì )在生成器表達式被定義時(shí)被檢測到,而不是在獲取第一個(gè)值時(shí)才出錯。 后續的 for 子句以及最左側 for 子句內的任何篩選條件無(wú)法在外層作用域內被求值,因為它們可能會(huì )依賴(lài)于從最左側可迭代對象獲取的值。 例如: (x*y for x in range(10) for y in range(x, x+10)).

圓括號在只附帶一個(gè)參數的調用中可以被省略。 詳情參見(jiàn) 調用 一節。

為了避免干擾到生成器表達式本身的預期操作,禁止在隱式定義的生成器中使用 yieldyield from 表達式。

如果生成器表達式包含 async for 子句或 await 表達式,則稱(chēng)為 異步生成器表達式。 異步生成器表達式會(huì )返回一個(gè)新的異步生成器對象,此對象屬于異步迭代器 (參見(jiàn) 異步迭代器)。

3.6 新版功能: 引入了異步生成器表達式。

在 3.7 版更改: 在 Python 3.7 之前,異步生成器表達式只能在 async def 協(xié)和中出現。 從 3.7 開(kāi)始,任何函數都可以使用異步生成器表達式。

在 3.8 版更改: yieldyield from 在隱式嵌套的作用域中已被禁用。

6.2.9. yield 表達式?

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

由于它們會(huì )對外層作用域造成附帶影響,yield 表達式不被允許作為用于實(shí)現推導式和生成器表達式的隱式定義作用域的一部分。

在 3.8 版更改: 禁止在實(shí)現推導式和生成器表達式的隱式嵌套作用域中使用 yield 表達式。

下面是對生成器函數的描述,異步生成器函數會(huì )在 異步生成器函數 一節中單獨介紹。

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.

所有這些使生成器函數與協(xié)程非常相似;它們 yield 多次,它們具有多個(gè)入口點(diǎn),并且它們的執行可以被掛起。唯一的區別是生成器函數不能控制在它在 yield 后交給哪里繼續執行;控制權總是轉移到生成器的調用者。

try 結構中的任何位置都允許yield表達式。如果生成器在(因為引用計數到零或是因為被垃圾回收)銷(xiāo)毀之前沒(méi)有恢復執行,將調用生成器-迭代器的 close() 方法. close 方法允許任何掛起的 finally 子句執行。

當使用 yield from <expr> 時(shí),所提供的表達式必須是一個(gè)可迭代對象。 迭代該可迭代對象所產(chǎn)生的值會(huì )被直接傳遞給當前生成器方法的調用者。 任何通過(guò) send() 傳入的值以及任何通過(guò) throw() 傳入的異常如果有適當的方法則會(huì )被傳給下層迭代器。 如果不是這種情況,那么 send() 將引發(fā) AttributeErrorTypeError,而 throw() 將立即引發(fā)所轉入的異常。

當下層迭代器完成時(shí),被引發(fā)的 StopIteration 實(shí)例的 value 屬性會(huì )成為 yield 表達式的值。 它可以在引發(fā) StopIteration 時(shí)被顯式地設置,也可以在子迭代器是一個(gè)生成器時(shí)自動(dòng)地設置(通過(guò)從子生成器返回一個(gè)值)。

在 3.3 版更改: 添加 yield from <expr> 以委托控制流給一個(gè)子迭代器。

當yield表達式是賦值語(yǔ)句右側的唯一表達式時(shí),括號可以省略。

參見(jiàn)

PEP 255 - 簡(jiǎn)單生成器

在 Python 中加入生成器和 yield 語(yǔ)句的提議。

PEP 342 - 通過(guò)增強型生成器實(shí)現協(xié)程

增強生成器 API 和語(yǔ)法的提議,使其可以被用作簡(jiǎn)單的協(xié)程。

PEP 380 - 委托給子生成器的語(yǔ)法

The proposal to introduce the yield_from syntax, making delegation to subgenerators easy.

PEP 525 - 異步生成器

通過(guò)給協(xié)程函數加入生成器功能對 PEP 492 進(jìn)行擴展的提議。

6.2.9.1. 生成器-迭代器的方法?

這個(gè)子小節描述了生成器迭代器的方法。 它們可被用于控制生成器函數的執行。

請注意在生成器已經(jīng)在執行時(shí)調用以下任何方法都會(huì )引發(fā) 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.

此方法通常是隱式地調用,例如通過(guò) for 循環(huán)或是內置的 next() 函數。

generator.send(value)?

恢復執行并向生成器函數“發(fā)送”一個(gè)值。 value 參數將成為當前 yield 表達式的結果。 send() 方法會(huì )返回生成器所產(chǎn)生的下一個(gè)值,或者如果生成器沒(méi)有產(chǎn)生下一個(gè)值就退出則會(huì )引發(fā) StopIteration。 當調用 send() 來(lái)啟動(dòng)生成器時(shí),它必須以 None 作為調用參數,因為這時(shí)沒(méi)有可以接收值的 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 compatability, 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()?

在生成器函數暫停的位置引發(fā) GeneratorExit。 如果之后生成器函數正常退出、關(guān)閉或引發(fā) GeneratorExit (由于未捕獲該異常) 則關(guān)閉并返回其調用者。 如果生成器產(chǎn)生了一個(gè)值,關(guān)閉會(huì )引發(fā) RuntimeError。 如果生成器引發(fā)任何其他異常,它會(huì )被傳播給調用者。 如果生成器已經(jīng)由于異?;蛘M顺鰟t close() 不會(huì )做任何事。

6.2.9.2. 例子?

這里是一個(gè)簡(jiǎn)單的例子,演示了生成器和生成器函數的行為:

>>>
>>> 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 的例子, 參見(jiàn)“Python 有什么新變化”中的 PEP 380: 委托給子生成器的語(yǔ)法。

6.2.9.3. 異步生成器函數?

在一個(gè)使用 async def 定義的函數或方法中出現的 yield 表達式會(huì )進(jìn)一步將該函數定義為一個(gè) asynchronous generator 函數。

當一個(gè)異步生成器函數被調用時(shí),它會(huì )返回一個(gè)名為異步生成器對象的異步迭代器。 此對象將在之后控制該生成器函數的執行。 異步生成器對象通常被用在協(xié)程函數的 async for 語(yǔ)句中,類(lèi)似于在 for 語(yǔ)句中使用生成器對象。

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.

如果一個(gè)異步生成器恰好因 break、調用方任務(wù)被取消,或是其他異常而提前退出,生成器的異步清理代碼將會(huì )運行并可能引發(fā)異?;蛟L(fǎng)問(wèn)意外上下文中的上下文變量 -- 也許是在它所依賴(lài)的任務(wù)的生命周期之后,或是在異步生成器垃圾回收鉤子被調用時(shí)的事件循環(huán)關(guān)閉期間。 為了防止這種情況,調用方必須通過(guò)調用 aclose() 方法來(lái)顯式地關(guān)閉異步生成器以終結生成器并最終從事件循環(huán)中將其分離。

在異步生成器函數中,yield 表達式允許出現在 try 結構的任何位置。 但是,如果一個(gè)異步生成器在其被終結(由于引用計數達到零或被作為垃圾回收)之前未被恢復,則then a yield expression within a try 結構中的 yield 表達式可能導致掛起的 finally 子句執行失敗。 在此情況下,應由運行該異步生成器的事件循環(huán)或任務(wù)調度器來(lái)負責調用異步生成器-迭代器的 aclose() 方法并運行所返回的協(xié)程對象,從而允許任何掛起的 finally 子句得以執行。

為了能在事件循環(huán)終結時(shí)執行最終化處理,事件循環(huán)應當定義一個(gè) 終結器 函數,它接受一個(gè)異步生成器迭代器并將調用 aclose() 且執行該協(xié)程。 這個(gè) 終結器 可以通過(guò)調用 sys.set_asyncgen_hooks() 來(lái)注冊。 當首次迭代時(shí),異步生成器迭代器將保存已注冊的 終結器 以便在最終化時(shí)調用。 有關(guān) 終結器 方法的參考示例請查看在 Lib/asyncio/base_events.py 的中的 asyncio.Loop.shutdown_asyncgens 實(shí)現。

yield from <expr> 表達式如果在異步生成器函數中使用會(huì )引發(fā)語(yǔ)法錯誤。

6.2.9.4. 異步生成器-迭代器方法?

這個(gè)子小節描述了異步生成器迭代器的方法,它們可被用于控制生成器函數的執行。

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.

此方法通常是通過(guò) async for 循環(huán)隱式地調用。

coroutine agen.asend(value)?

返回一個(gè)可等待對象,它在運行時(shí)會(huì )恢復該異步生成器的執行。 與生成器的 send() 方法一樣,此方法會(huì )“發(fā)送”一個(gè)值給異步生成器函數,其 value 參數會(huì )成為當前 yield 表達式的結果值。 asend() 方法所返回的可等待對象將返回生成器產(chǎn)生的下一個(gè)值,其值為所引發(fā)的 StopIteration,或者如果異步生成器沒(méi)有產(chǎn)生下一個(gè)值就退出則引發(fā) StopAsyncIteration。 當調用 asend() 來(lái)啟動(dòng)異步生成器時(shí),它必須以 None 作為調用參數,因為這時(shí)沒(méi)有可以接收值的 yield 表達式。

coroutine agen.athrow(type[, value[, traceback]])?

返回一個(gè)可等待對象,它會(huì )在異步生成器暫停的位置引發(fā) type 類(lèi)型的異常,并返回該生成器函數所產(chǎn)生的下一個(gè)值,其值為所引發(fā)的 StopIteration 異常。 如果異步生成器沒(méi)有產(chǎn)生下一個(gè)值就退出,則將由該可等待對象引發(fā) StopAsyncIteration 異步。 如果生成器函數沒(méi)有捕獲傳入的異常,或引發(fā)了另一個(gè)異常,則當可等待對象運行時(shí)該異常會(huì )被傳播給可等待對象的調用者。

coroutine agen.aclose()?

返回一個(gè)可等待對象,它會(huì )在運行時(shí)向異步生成器函數暫停的位置拋入一個(gè) GeneratorExit。 如果該異步生成器函數正常退出、關(guān)閉或引發(fā) GeneratorExit (由于未捕獲該異常) 則返回的可等待對象將引發(fā) StopIteration 異常。 后續調用異步生成器所返回的任何其他可等待對象將引發(fā) StopAsyncIteration 異常。 如果異步生成器產(chǎn)生了一個(gè)值,該可等待對象會(huì )引發(fā) RuntimeError。 如果異步生成器引發(fā)任何其他異常,它會(huì )被傳播給可等待對象的調用者。 如果異步生成器已經(jīng)由于異?;蛘M顺鰟t后續調用 aclose() 將返回一個(gè)不會(huì )做任何事的可等待對象。

6.3. 原型?

原型代表編程語(yǔ)言中最緊密綁定的操作。 它們的句法如下:

primary ::=  atom | attributeref | subscription | slicing | call

6.3.1. 屬性引用?

屬性引用是后面帶有一個(gè)句點(diǎn)加一個(gè)名稱(chēng)的原型:

attributeref ::=  primary "." identifier

此原型必須求值為一個(gè)支持屬性引用的類(lèi)型的對象,多數對象都支持屬性引用。 隨后該對象會(huì )被要求產(chǎn)生以指定標識符為名稱(chēng)的屬性。 這個(gè)產(chǎn)生過(guò)程可通過(guò)重載 __getattr__() 方法來(lái)自定義。 如果這個(gè)屬性不可用,則將引發(fā) AttributeError 異常。 否則的話(huà),所產(chǎn)生對象的類(lèi)型和值會(huì )根據該對象來(lái)確定。 對同一屬性引用的多次求值可能產(chǎn)生不同的對象。

6.3.2. 抽取?

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__():

  1. 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.

  2. 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.

6.3.3. 切片?

切片就是在序列對象(字符串、元組或列表)中選擇某個(gè)范圍內的項。 切片可被用作表達式以及賦值或 del 語(yǔ)句的目標。 切片的句法如下:

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

此處的正式句法中存在一點(diǎn)歧義:任何形似表達式列表的東西同樣也會(huì )形似切片列表,因此任何抽取操作也可以被解析為切片。 為了不使句法更加復雜,于是通過(guò)定義將此情況解析為抽取優(yōu)先于解析為切片來(lái)消除這種歧義(切片列表未包含正確的切片就屬于此情況)。

切片的語(yǔ)義如下所述。 元型通過(guò)一個(gè)根據下面的切片列表來(lái)構造的鍵進(jìn)行索引(與普通抽取一樣使用 __getitem__() 方法)。 如果切片列表包含至少一個(gè)逗號,則鍵將是一個(gè)包含切片項轉換的元組;否則的話(huà),鍵將是單個(gè)切片項的轉換。 切片項如為一個(gè)表達式,則其轉換就是該表達式。 一個(gè)正確切片的轉換就是一個(gè)切片對象(參見(jiàn) 標準類(lèi)型層級結構 一節),該對象的 start, stopstep 屬性將分別為表達式所給出的下界、上界和步長(cháng)值,省略的表達式將用 None 來(lái)替換。

6.3.4. 調用?

所謂調用就是附帶可能為空的一系列 參數 來(lái)執行一個(gè)可調用對象 (例如 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

一個(gè)可選項為在位置和關(guān)鍵字參數后加上逗號而不影響語(yǔ)義。

此原型必須求值為一個(gè)可調用對象(用戶(hù)定義的函數,內置函數,內置對象的方法,類(lèi)對象,類(lèi)實(shí)例的方法以及任何具有 __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: 某些實(shí)現可能提供位置參數沒(méi)有名稱(chēng)的內置函數,即使它們在文檔說(shuō)明的場(chǎng)合下有“命名”,因此不能以關(guān)鍵字形式提供參數。 在 CPython 中,以 C 編寫(xiě)并使用 PyArg_ParseTuple() 來(lái)解析其參數的函數實(shí)現就屬于這種情況。

如果存在比正式參數空位多的位置參數,將會(huì )引發(fā) TypeError 異常,除非有一個(gè)正式參數使用了 *identifier 句法;在此情況下,該正式參數將接受一個(gè)包含了多余位置參數的元組(如果沒(méi)有多余位置參數則為一個(gè)空元組)。

如果任何關(guān)鍵字參數沒(méi)有與之對應的正式參數名稱(chēng),將會(huì )引發(fā) TypeError 異常,除非有一個(gè)正式參數使用了 **identifier 句法,該正式參數將接受一個(gè)包含了多余關(guān)鍵字參數的字典(使用關(guān)鍵字作為鍵而參數值作為與鍵對應的值),如果沒(méi)有多余關(guān)鍵字參數則為一個(gè)(新的)空字典。

如果函數調用中出現了 *expression 句法,expression 必須求值為一個(gè) iterable。 來(lái)自該可迭代對象的元素會(huì )被當作是額外的位置參數。 對于 f(x1, x2, *y, x3, x4) 調用,如果 y 求值為一個(gè)序列 y1, ..., yM,則它就等價(jià)于一個(gè)帶有 M+4 個(gè)位置參數 x1, x2, y1, ..., yM, x3, x4 的調用。

這樣做的一個(gè)后果是雖然 *expression 句法可能出現于顯式的關(guān)鍵字參數 之后,但它會(huì )在關(guān)鍵字參數(以及任何 **expression 參數 -- 見(jiàn)下文) 之前 被處理。 因此:

>>>
>>> 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 必須求值為一個(gè) mapping,其內容會(huì )被當作是額外的關(guān)鍵字參數。 如果一個(gè)關(guān)鍵字已存在(作為顯式關(guān)鍵字參數,或來(lái)自另一個(gè)拆包),則將引發(fā) TypeError 異常。

使用 *identifier**identifier 句法的正式參數不能被用作位置參數空位或關(guān)鍵字參數名稱(chēng)。

在 3.5 版更改: 函數調用接受任意數量的 *** 拆包,位置參數可能跟在可迭代對象拆包 (*) 之后,而關(guān)鍵字參數可能跟在字典拆包 (**) 之后。 由 PEP 448 發(fā)起最初提議。

除非引發(fā)了異常,調用總是會(huì )有返回值,返回值也可能為 None。 返回值的計算方式取決于可調用對象的類(lèi)型。

如果類(lèi)型為---

用戶(hù)自定義函數:

函數的代碼塊會(huì )被執行,并向其傳入參數列表。 代碼塊所做的第一件事是將正式形參綁定到對應參數;相關(guān)描述參見(jiàn) 函數定義 一節。 當代碼塊執行 return 語(yǔ)句時(shí),由其指定函數調用的返回值。

內置函數或方法:

具體結果依賴(lài)于解釋器;有關(guān)內置函數和方法的描述參見(jiàn) 內置函數。

類(lèi)對象:

返回該類(lèi)的一個(gè)新實(shí)例。

類(lèi)實(shí)例方法:

調用相應的用戶(hù)自定義函數,向其傳入的參數列表會(huì )比調用的參數列表多一項:該實(shí)例將成為第一個(gè)參數。

類(lèi)實(shí)例:

該類(lèi)必須定義有 __call__() 方法;作用效果將等價(jià)于調用該方法。

6.4. await 表達式?

掛起 coroutine 的執行以等待一個(gè) awaitable 對象。 只能在 coroutine function 內部使用。

await_expr ::=  "await" primary

3.5 新版功能.

6.5. 冪運算符?

冪運算符的綁定比在其左側的一元運算符更緊密;但綁定緊密程度不及在其右側的一元運算符。 句法如下:

power ::=  (await_expr | primary) ["**" u_expr]

因此,在一個(gè)未加圓括號的冪運算符和單目運算符序列中,運算符將從右向左求值(這不會(huì )限制操作數的求值順序): -1**2 結果將為 -1。

冪運算符與附帶兩個(gè)參數調用內置 pow() 函數具有相同的語(yǔ)義:結果為對其左參數進(jìn)行其右參數所指定冪次的乘方運算。 數值參數會(huì )先轉換為相同類(lèi)型,結果也為轉換后的類(lèi)型。

對于 int 類(lèi)型的操作數,結果將具有與操作數相同的類(lèi)型,除非第二個(gè)參數為負數;在那種情況下,所有參數會(huì )被轉換為 float 類(lèi)型并輸出 float 類(lèi)型的結果。 例如,10**2 返回 100,而 10**-2 返回 0.01。

0.0 進(jìn)行負數冪次運算將導致 ZeroDivisionError。 對負數進(jìn)行分數冪次運算將返回 complex 數值。 (在早期版本中這將引發(fā) ValueError。)

此運算符可使用特殊的 __pow__() 方法來(lái)自定義。

6.6. 一元算術(shù)和位運算?

所有算術(shù)和位運算具有相同的優(yōu)先級:

u_expr ::=  power | "-" u_expr | "+" u_expr | "~" u_expr

一元的 - (負值) 運算符會(huì )產(chǎn)生其數字參數的負值;該運算可通過(guò) __neg__() 特殊方法來(lái)重載。

一元的 + (正值) 運算符會(huì )原樣輸出其數字參數;該運算可通過(guò) __pos__() 特殊方法來(lái)重載。

一元的 ~ (取反) 運算符會(huì )對其整數參數按位取反。 x 的按位取反被定義為 -(x+1)。 它只作用于整數或是重載了 __invert__() 特殊方法的自定義對象。

在所有三種情況下,如果參數的類(lèi)型不正確,將引發(fā) TypeError 異常。

6.7. 二元算術(shù)運算符?

二元算術(shù)運算符遵循傳統的優(yōu)先級。 請注意某些此類(lèi)運算符也作用于特定的非數字類(lèi)型。 除冪運算符以外只有兩個(gè)優(yōu)先級別,一個(gè)作用于乘法型運算符,另一個(gè)作用于加法型運算符:

m_expr ::=  u_expr | m_expr "*" u_expr | m_expr "@" m_expr |
            m_expr "http://" u_expr | m_expr "/" u_expr |
            m_expr "%" u_expr
a_expr ::=  m_expr | a_expr "+" m_expr | a_expr "-" m_expr

運算符 * (乘) 將輸出其參數的乘積。 兩個(gè)參數或者必須都為數字,或者一個(gè)參數必須為整數而另一個(gè)參數必須為序列。 在前一種情況下,兩個(gè)數字將被轉換為相同類(lèi)型然后相乘。 在后一種情況下,將執行序列的重復;重復因子為負數將輸出空序列。

此運算可使用特殊的 __mul__()__rmul__() 方法來(lái)自定義。

運算符 @ (at) 的目標是用于矩陣乘法。 沒(méi)有內置 Python 類(lèi)型實(shí)現此運算符。

3.5 新版功能.

運算符 / (除) 和 // (整除) 將輸出其參數的商。 兩個(gè)數字參數將先被轉換為相同類(lèi)型。 整數相除會(huì )輸出一個(gè) float 值,整數相整除的結果仍是整數;整除的結果就是使用 'floor' 函數進(jìn)行算術(shù)除法的結果。 除以零的運算將引發(fā) ZeroDivisionError 異常。

This operation can be customized using the special __truediv__() and __floordiv__() methods.

運算符 % (模) 將輸出第一個(gè)參數除以第二個(gè)參數的余數。 兩個(gè)數字參數將先被轉換為相同類(lèi)型。 右參數為零將引發(fā) ZeroDivisionError 異常。 參數可以為浮點(diǎn)數,例如 3.14%0.7 等于 0.34 (因為 3.14 等于 4*0.7 + 0.34)。 模運算符的結果的正負總是與第二個(gè)操作數一致(或是為零);結果的絕對值一定小于第二個(gè)操作數的絕對值 1。

整除與模運算符的聯(lián)系可通過(guò)以下等式說(shuō)明: x == (x//y)*y + (x%y)。 此外整除與模也可通過(guò)內置函數 divmod() 來(lái)同時(shí)進(jìn)行: divmod(x, y) == (x//y, x%y)。 2。

除了對數字執行模運算,運算符 % 還被字符串對象重載用于執行舊式的字符串格式化(又稱(chēng)插值)。 字符串格式化句法的描述參見(jiàn) Python 庫參考的 printf 風(fēng)格的字符串格式化 一節。

取余 運算可使用特殊的 __mod__() 方法來(lái)自定義。

整除運算符,模運算符和 divmod() 函數未被定義用于復數。 如果有必要可以使用 abs() 函數將其轉換為浮點(diǎn)數。

運算符 + (addition) 將輸出其參數的和。 兩個(gè)參數或者必須都為數字,或者都為相同類(lèi)型的序列。 在前一種情況下,兩個(gè)數字將被轉換為相同類(lèi)型然后相加。 在后一種情況下,將執行序列拼接操作。

此運算可使用特殊的 __add__()__radd__() 方法來(lái)自定義。

運算符 - (減) 將輸出其參數的差。 兩個(gè)數字參數將先被轉換為相同類(lèi)型。

此運算可使用特殊的 __sub__() 方法來(lái)自定義。

6.8. 移位運算?

移位運算的優(yōu)先級低于算術(shù)運算:

shift_expr ::=  a_expr | shift_expr ("<<" | ">>") a_expr

這些運算符接受整數參數。 它們會(huì )將第一個(gè)參數左移或右移第二個(gè)參數所指定的比特位數。

此運算可使用特殊的 __lshift__()__rshift__() 方法來(lái)自定義。

右移 n 位被定義為被 pow(2,n) 整除。 左移 n 位被定義為乘以 pow(2,n)。

6.9. 二元位運算?

三種位運算具有各不相同的優(yōu)先級:

and_expr ::=  shift_expr | and_expr "&" shift_expr
xor_expr ::=  and_expr | xor_expr "^" and_expr
or_expr  ::=  xor_expr | or_expr "|" xor_expr

& 運算符會(huì )對其參數執行按位 AND,參數必須都為整數或者其中之一必須為重載了 __and__()__rand__() 特殊方法的自定義對象。

^ 運算符會(huì )對其參數執行按位 XOR (異 OR),參數必須都為整數或者其中之一必須為重載了 __xor__()__rxor__() 特殊方法的自定義對象。

| 運算符會(huì )對其參數執行按位 (合并) OR,參數必須都為整數或者其中之一必須為重載了 __or__()__ror__() 特殊方法的自定義對象。

6.10. 比較運算?

與 C 不同,Python 中所有比較運算的優(yōu)先級相同,低于任何算術(shù)、移位或位運算。 另一個(gè)與 C 不同之處在于 a < b < c 這樣的表達式會(huì )按傳統算術(shù)法則來(lái)解讀:

comparison    ::=  or_expr (comp_operator or_expr)*
comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "!="
                   | "is" ["not"] | ["not"] "in"

比較運算會(huì )產(chǎn)生布爾值: TrueFalse。 自定義的 富比較方法 可能返回非布爾值。 在此情況下 Python 將在布爾運算上下文中對該值調用 bool()。

比較運算可以任意串連,例如 x < y <= z 等價(jià)于 x < y and y <= z,除了 y 只被求值一次(但在兩種寫(xiě)法下當 x < y 值為假時(shí) z 都不會(huì )被求值)。

正式的說(shuō)法是這樣:如果 a, b, c, ..., y, z 為表達式而 op1, op2, ..., opN 為比較運算符,則 a op1 b op2 c ... y opN z 就等價(jià)于 a op1 b and b op2 c and ... y opN z,不同點(diǎn)在于每個(gè)表達式最多只被求值一次。

請注意 a op1 b op2 c 不意味著(zhù)在 ac 之間進(jìn)行任何比較,因此,如 x < y > z 這樣的寫(xiě)法是完全合法的(雖然也許不太好看)。

6.10.1. 值比較?

運算符 <, >, ==, >=, <=!= 將比較兩個(gè)對象的值。 兩個(gè)對象不要求為相同類(lèi)型。

對象、值與類(lèi)型 一章已說(shuō)明對象都有相應的值(還有類(lèi)型和標識號)。 對象值在 Python 中是一個(gè)相當抽象的概念:例如,對象值并沒(méi)有一個(gè)規范的訪(fǎng)問(wèn)方法。 而且,對象值并不要求具有特定的構建方式,例如由其全部數據屬性組成等。 比較運算符實(shí)現了一個(gè)特定的對象值概念。 人們可以認為這是通過(guò)實(shí)現對象比較間接地定義了對象值。

由于所有類(lèi)型都是 object 的(直接或間接)子類(lèi)型,它們都從 object 繼承了默認的比較行為。 類(lèi)型可以通過(guò)實(shí)現 豐富比較方法 例如 __lt__() 來(lái)定義自己的比較行為,詳情參見(jiàn) 基本定制。

默認的一致性比較 (==!=) 是基于對象的標識號。 因此,具有相同標識號的實(shí)例一致性比較結果為相等,具有不同標識號的實(shí)例一致性比較結果為不等。 規定這種默認行為的動(dòng)機是希望所有對象都應該是自反射的 (即 x is y 就意味著(zhù) x == y)。

次序比較 (<, >, <=>=) 默認沒(méi)有提供;如果嘗試比較會(huì )引發(fā) TypeError。 規定這種默認行為的原因是缺少與一致性比較類(lèi)似的固定值。

按照默認的一致性比較行為,具有不同標識號的實(shí)例總是不相等,這可能不適合某些對象值需要有合理定義并有基于值的一致性的類(lèi)型。 這樣的類(lèi)型需要定制自己的比較行為,實(shí)際上,許多內置類(lèi)型都是這樣做的。

以下列表描述了最主要內置類(lèi)型的比較行為。

  • 內置數值類(lèi)型 (數字類(lèi)型 --- int, float, complex) 以及標準庫類(lèi)型 fractions.Fractiondecimal.Decimal 可進(jìn)行類(lèi)型內部和跨類(lèi)型的比較,例外限制是復數不支持次序比較。 在類(lèi)型相關(guān)的限制以?xún)?,它們?huì )按數學(xué)(算法)規則正確進(jìn)行比較且不會(huì )有精度損失。

    非數字值 float('NaN')decimal.Decimal('NaN') 屬于特例。 任何數字與非數字值的排序比較均返回假值。 還有一個(gè)反直覺(jué)的結果是非數字值不等于其自身。 舉例來(lái)說(shuō),如果 x = float('NaN')3 < x, x < 3x == x 均為假值,而 x != x 則為真值。 此行為是遵循 IEEE 754 標準的。

  • NoneNotImplemented 都是單例對象。 PEP 8 建議單例對象的比較應當總是通過(guò) isis not 而不是等于運算符來(lái)進(jìn)行。

  • 二進(jìn)制碼序列 (bytesbytearray 的實(shí)例) 可進(jìn)行類(lèi)型內部和跨類(lèi)型的比較。 它們使用其元素的數字值按字典順序進(jìn)行比較。

  • 字符串 (str 的實(shí)例) 使用其字符的 Unicode 碼位數字值 (內置函數 ord() 的結果) 按字典順序進(jìn)行比較。 3

    字符串和二進(jìn)制碼序列不能直接比較。

  • 序列 (tuple, listrange 的實(shí)例) 只可進(jìn)行類(lèi)型內部的比較,range 還有一個(gè)限制是不支持次序比較。 以上對象的跨類(lèi)型一致性比較結果將是不相等,跨類(lèi)型次序比較將引發(fā) TypeError。

    序列比較是按字典序對相應元素進(jìn)行逐個(gè)比較。 內置容器通常設定同一對象與其自身是相等的。 這使得它們能跳過(guò)同一對象的相等性檢測以提升運行效率并保持它們的內部不變性。

    內置多項集間的字典序比較規則如下:

    • 兩個(gè)多項集若要相等,它們必須為相同類(lèi)型、相同長(cháng)度,并且每對相應的元素都必須相等(例如,[1,2] == (1,2) 為假值,因為類(lèi)型不同)。

    • 對于支持次序比較的多項集,排序與其第一個(gè)不相等元素的排序相同(例如 [1,2,x] <= [1,2,y] 的值與``x <= y`` 相同)。 如果對應元素不存在,較短的多項集排序在前(例如 [1,2] < [1,2,3] 為真值)。

  • 兩個(gè)映射 (dict 的實(shí)例) 若要相等,必須當且僅當它們具有相同的 (鍵, 值) 對。 鍵和值的一致性比較強制規定自反射性。

    次序比較 (<, >, <=>=) 將引發(fā) TypeError。

  • 集合 (setfrozenset 的實(shí)例) 可進(jìn)行類(lèi)型內部和跨類(lèi)型的比較。

    它們將比較運算符定義為子集和超集檢測。 這類(lèi)關(guān)系沒(méi)有定義完全排序(例如 {1,2}{2,3} 兩個(gè)集合不相等,即不為彼此的子集,也不為彼此的超集。 相應地,集合不適宜作為依賴(lài)于完全排序的函數的參數(例如如果給出一個(gè)集合列表作為 min(), max()sorted() 的輸入將產(chǎn)生未定義的結果)。

    集合的比較強制規定其元素的自反射性。

  • 大多數其他內置類(lèi)型沒(méi)有實(shí)現比較方法,因此它們會(huì )繼承默認的比較行為。

在可能的情況下,用戶(hù)定義類(lèi)在定制其比較行為時(shí)應當遵循一些一致性規則:

  • 相等比較應該是自反射的。 換句話(huà)說(shuō),相同的對象比較時(shí)應該相等:

    x is y 意味著(zhù) x == y

  • 比較應該是對稱(chēng)的。 換句話(huà)說(shuō),下列表達式應該有相同的結果:

    x == yy == x

    x != yy != x

    x < yy > x

    x <= yy >= x

  • 比較應該是可傳遞的。 下列(簡(jiǎn)要的)例子顯示了這一點(diǎn):

    x > y and y > z 意味著(zhù) x > z

    x < y and y <= z 意味著(zhù) x < z

  • 反向比較應該導致布爾值取反。 換句話(huà)說(shuō),下列表達式應該有相同的結果:

    x == ynot x != y

    x < ynot x >= y (對于完全排序)

    x > ynot x <= y (對于完全排序)

    最后兩個(gè)表達式適用于完全排序的多項集(即序列而非集合或映射)。 另請參閱 total_ordering() 裝飾器。

  • hash() 的結果應該與是否相等一致。 相等的對象應該或者具有相同的哈希值,或者標記為不可哈希。

Python 并不強制要求這些一致性規則。 實(shí)際上,非數字值就是一個(gè)不遵循這些規則的例子。

6.10.2. 成員檢測運算?

運算符 innot in 用于成員檢測。 如果 xs 的成員則 x in s 求值為 True,否則為 False。 x not in s 返回 x in s 取反后的值。 所有內置序列和集合類(lèi)型以及字典都支持此運算,對于字典來(lái)說(shuō) in 檢測其是否有給定的鍵。 對于 list, tuple, set, frozenset, dict 或 collections.deque 這樣的容器類(lèi)型,表達式 x in y 等價(jià)于 any(x is e or x == e for e in y)。

對于字符串和字節串類(lèi)型來(lái)說(shuō),當且僅當 xy 的子串時(shí) x in yTrue。 一個(gè)等價(jià)的檢測是 y.find(x) != -1。 空字符串總是被視為任何其他字符串的子串,因此 "" in "abc" 將返回 True。

對于定義了 __contains__() 方法的用戶(hù)自定義類(lèi)來(lái)說(shuō),如果 y.__contains__(x) 返回真值則 x in y 返回 True,否則返回 False。

對于未定義 __contains__() 但定義了 __iter__() 的用戶(hù)自定義類(lèi)來(lái)說(shuō),如果在對 y 進(jìn)行迭代時(shí)產(chǎn)生了值 z 使得表達式 x is z or x == z 為真,則 x in yTrue。 如果在迭代期間引發(fā)了異常,則等同于 in 引發(fā)了該異常。

最后將會(huì )嘗試舊式的迭代協(xié)議:如果一個(gè)類(lèi)定義了 __getitem__(),則當且僅當存在非負整數索引號 i 使得 x is y[i] or x == y[i] 并且沒(méi)有更小的索引號引發(fā) IndexError 異常時(shí) x in yTrue。 (如果引發(fā)了任何其他異常,則等同于 in 引發(fā)了該異常)。

運算符 not in 被定義為具有與 in 相反的邏輯值。

6.10.3. 標識號比較?

運算符 isis not 用于檢測對象的標識號:當且僅當 xy 是同一對象時(shí) x is y 為真。 一個(gè)對象的標識號可使用 id() 函數來(lái)確定。 x is not y 會(huì )產(chǎn)生相反的邏輯值。 4

6.11. 布爾運算?

or_test  ::=  and_test | or_test "or" and_test
and_test ::=  not_test | and_test "and" not_test
not_test ::=  comparison | "not" not_test

在執行布爾運算的情況下,或是當表達式被用于流程控制語(yǔ)句時(shí),以下值會(huì )被解析為假值: False, None, 所有類(lèi)型的數字零,以及空字符串和空容器(包括字符串、元組、列表、字典、集合與凍結集合)。 所有其他值都會(huì )被解析為真值。 用戶(hù)自定義對象可通過(guò)提供 __bool__() 方法來(lái)定制其邏輯值。

運算符 not 將在其參數為假值時(shí)產(chǎn)生 True,否則產(chǎn)生 False。

表達式 x and y 首先對 x 求值;如果 x 為假則返回該值;否則對 y 求值并返回其結果值。

表達式 x or y 首先對 x 求值;如果 x 為真則返回該值;否則對 y 求值并返回其結果值。

請注意 andor 都不限制其返回的值和類(lèi)型必須為 FalseTrue,而是返回最后被求值的操作數。 此行為是有必要的,例如假設 s 為一個(gè)當其為空時(shí)應被替換為某個(gè)默認值的字符串,表達式 s or 'foo' 將產(chǎn)生希望的值。 由于 not 必須創(chuàng )建一個(gè)新值,不論其參數為何種類(lèi)型它都會(huì )返回一個(gè)布爾值(例如,not 'foo' 結果為 False 而非 ''。)

6.12. 賦值表達式?

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.

一個(gè)常見(jiàn)用例是在處理匹配的正則表達式的時(shí)候:

if matching := pattern.search(data):
    do_something(matching)

或者是在處理分塊的文件流的時(shí)候:

while chunk := file.read(9000):
    process(chunk)

3.8 新版功能: 請參閱 PEP 572 了解有關(guān)賦值表達式的詳情。

6.13. 條件表達式?

conditional_expression ::=  or_test ["if" or_test "else" expression]
expression             ::=  conditional_expression | lambda_expr

條件表達式(有時(shí)稱(chēng)為“三元運算符”)在所有 Python 運算中具有最低的優(yōu)先級。

表達式 x if C else y 首先是對條件 C 而非 x 求值。 如果 C 為真,x 將被求值并返回其值;否則將對 y 求值并返回其值。

請參閱 PEP 308 了解有關(guān)條件表達式的詳情。

6.14. lambda 表達式?

lambda_expr ::=  "lambda" [parameter_list] ":" expression

lambda 表達式(有時(shí)稱(chēng)為 lambda 構型)被用于創(chuàng )建匿名函數。 表達式 lambda parameters: expression 會(huì )產(chǎn)生一個(gè)函數對象 。 該未命名對象的行為類(lèi)似于用以下方式定義的函數:

def <lambda>(parameters):
    return expression

請參閱 函數定義 了解有關(guān)參數列表的句法。 請注意通過(guò) lambda 表達式創(chuàng )建的函數不能包含語(yǔ)句或標注。

6.15. 表達式列表?

expression_list    ::=  expression ("," expression)* [","]
starred_list       ::=  starred_item ("," starred_item)* [","]
starred_expression ::=  expression | (starred_item ",")* [starred_item]
starred_item       ::=  assignment_expression | "*" or_expr

除了作為列表或集合顯示的一部分,包含至少一個(gè)逗號的表達式列表將生成一個(gè)元組。 元組的長(cháng)度就是列表中表達式的數量。 表達式將從左至右被求值。

一個(gè)星號 * 表示 可迭代拆包。 其操作數必須為一個(gè) iterable。 該可迭代對象將被拆解為迭代項的序列,并被包含于在拆包位置上新建的元組、列表或集合之中。

3.5 新版功能: 表達式列表中的可迭代對象拆包,最初由 PEP 448 提出。

末尾的逗號僅在創(chuàng )建單獨元組 (或稱(chēng) 單例) 時(shí)需要;在所有其他情況下都是可選項。 沒(méi)有末尾逗號的單獨表達式不會(huì )創(chuàng )建一個(gè)元組,而是產(chǎn)生該表達式的值。 (要創(chuàng )建一個(gè)空元組,應使用一對內容為空的圓括號: ()。)

6.16. 求值順序?

Python 按從左至右的順序對表達式求值。 但注意在對賦值操作求值時(shí),右側會(huì )先于左側被求值。

在以下幾行中,表達式將按其后綴的算術(shù)優(yōu)先順序被求值。:

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

6.17. 運算符優(yōu)先級?

下表對 Python 中運算符的優(yōu)先順序進(jìn)行了總結,從最高優(yōu)先級(最先綁定)到最低優(yōu)先級(最后綁定)。 相同單元格內的運算符具有相同優(yōu)先級。 除非句法顯式地給出,否則運算符均指二元運算。 相同單元格內的運算符從左至右分組(除了冪運算是從右至左分組)。

請注意比較、成員檢測和標識號檢測均為相同優(yōu)先級,并具有如 比較運算 一節所描述的從左至右串連特性。

運算符

描述

(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) 在數學(xué)中必為真,但對于浮點(diǎn)數而言,由于舍入的存在,其在數值上未必為真。 例如,假設在某個(gè)平臺上的 Python 浮點(diǎn)數為一個(gè) IEEE 754 雙精度數值,為了使 -1e-100 % 1e100 具有與 1e100 相同的正負性,計算結果將是 -1e-100 + 1e100,這在數值上正好等于 1e100。 函數 math.fmod() 返回的結果則會(huì )具有與第一個(gè)參數相同的正負性,因此在這種情況下將返回 -1e-100。 何種方式更適宜取決于具體的應用。

2

如果 x 恰好非常接近于 y 的整數倍,則由于舍入的存在 x//y 可能會(huì )比 (x-x%y)//y 大。 在這種情況下,Python 會(huì )返回后一個(gè)結果,以便保持令 divmod(x,y)[0] * y + x % y 盡量接近 x.

3

Unicode 標準明確區分 碼位 (例如 U+0041) 和 抽象字符 (例如 "大寫(xiě)拉丁字母 A")。 雖然 Unicode 中的大多數抽象字符都只用一個(gè)碼位來(lái)代表,但也存在一些抽象字符可使用由多個(gè)碼位組成的序列來(lái)表示。 例如,抽象字符 "帶有下加符的大寫(xiě)拉丁字母 C" 可以用 U+00C7 碼位上的單個(gè) 預設字符 來(lái)表示,也可以用一個(gè) U+0043 碼位上的 基礎字符 (大寫(xiě)拉丁字母 C) 加上一個(gè) U+0327 碼位上的 組合字符 (組合下加符) 組成的序列來(lái)表示。

對于字符串,比較運算符會(huì )按 Unicode 碼位級別進(jìn)行比較。 這可能會(huì )違反人類(lèi)的直覺(jué)。 例如,"\u00C7" == "\u0043\u0327"False,雖然兩個(gè)字符串都代表同一個(gè)抽象字符 "帶有下加符的大寫(xiě)拉丁字母 C"。

要按抽象字符級別(即對人類(lèi)來(lái)說(shuō)更直觀(guān)的方式)對字符串進(jìn)行比較,應使用 unicodedata.normalize()。

4

由于存在自動(dòng)垃圾收集、空閑列表以及描述器的動(dòng)態(tài)特性,你可能會(huì )注意到在特定情況下使用 is 運算符會(huì )出現看似不正常的行為,例如涉及到實(shí)例方法或常量之間的比較時(shí)就是如此。 更多信息請查看有關(guān)它們的文檔。

5

冪運算符 ** 綁定的緊密程度低于在其右側的算術(shù)或按位一元運算符,也就是說(shuō) 2**-10.5。

6

% 運算符也被用于字符串格式化;在此場(chǎng)合下會(huì )使用同樣的優(yōu)先級。