用戶:Xyy23330121/Python/查漏補缺

來自維基學院


在上面的學習中,有一些語句和程序結構因為暫時用不到,沒有進行講解。本頁面將講述這些內容。

with 語句[編輯 | 編輯原始碼]

with 語句用於「上下文管理器」。

上下文管理器[編輯 | 編輯原始碼]

上下文管理器指的是擁有 __enter__ 方法和 __exit__ 方法的對象。其中:

  1. __enter__ 方法應不能額外傳入參數
  2. __exit__ 方法需要傳入三個參數

我們看以下示例:

class Test:
    def __enter__(self):
        print("开始 with 语句")
        return "value"
    def __exit__(self, *args):
        print("结束 with 语句")
        return True

with Test() as a:
    print(a)

該示例的輸出為:

开始 with 语句
value
结束 with 语句

with 語句的執行順序[編輯 | 編輯原始碼]

對於以下語句:

with ContextManager as var:

在執行 with 子句前,會調用 ContextManager.__enter__(),並把其結果賦值給 var,而在 with 子句執行後,會調用上下文管理器的 ContextManager.__exit__ 方法。

特別的,可以省略 as var。此時,with 語句不會進行最開始的賦值操作。

錯誤處理[編輯 | 編輯原始碼]

如果在 with 子句中沒有引發錯誤,則在結束時,調用 ContextManager.__exit__(None, None, None)。如果 with 子句中引發了錯誤,則這三個參數會包含錯誤信息。

此時,如果 ContextManager.__exit__ 返回 True,則不報錯。如果返回 False,則報錯並退出。

我們看以下示例:

def prt(*items):
    for item in items:
        print(item)
        print(type(item))
        print("------------------")

class Test:
    def __enter__(self): pass
    def __exit__(self, a, b, c):
        prt(a,b,c)
        return False #如果出错,必定报错

with Test():
    raise ValueError("示例错误信息")

該示例的輸出為:

<class 'ValueError'>
<class 'type'>
------------------
示例错误信息
<class 'ValueError'>
------------------
<traceback object at 0x00000137E51F5000>
<class 'traceback'>
------------------
Traceback (most recent call last):
  ...
    raise ValueError("示例错误信息")
ValueError: 示例错误信息

可以看出,傳入的參數按順序分別為錯誤對象的類型、錯誤對象、Traceback。

需要注意的是,如果 with 子句中沒有產生錯誤,則無論 __exit__ 返回什麼值,都不會報錯。

match 語句[編輯 | 編輯原始碼]

match 語句是 Python 3.10 版本加入的語句。它和 if 語句的作用類似,但功能更強大,語法也更複雜。

由於 match 語句集「賦值」和「比較」兩個方式為一體,且兩個方式的區分在 Python 文檔中沒有嚴格地定義,所以這裏只講解最簡單的「比較」方式,而完全跳過「賦值」的部分。

簡單的 match 語句:匹配[編輯 | 編輯原始碼]

boolean = True

match boolean:
    case True:
        print("True")
    case False:
        print("False")
    case True:
        print("不会匹配到这里,因为已经有匹配值了")

在「匹配」時,case 後面不可以跟着任何變量名稱,而必須是 0"str"之類的字面值。或者,也可以是只由類型構造器構成的表達式。

類型構造器就是int(x)這類,調用時和函數的方法一致——但直接用於構造類的實例的方式。

在這種情況下,match 語句和 if 語句作用是完全相同的。它會嘗試將 match 關鍵字後緊跟着的、表達式的結果和 case 語句後緊跟着的內容作比較。如果和某個 case 匹配,則會執行匹配的 case 子句。執行完成後,不會再進行匹配。

yield 語句與生成器[編輯 | 編輯原始碼]

yield 語句用於創建「生成器函數」。

生成器[編輯 | 編輯原始碼]

關於迭代器

我們之前接觸過生成器,我們在列表和元組的括號內使用過生成器,比如:

print(i**2 for i in range(3))  #输出:<generator object <genexpr> at *>

生成器是一種迭代器。對於這種方法所創建的生成器,我們可以用函數的方式獲取:

def identity(x):
	return x

a = identity(i**2 for i in range(3))
for i in a:
    print(i, end=" ")  #输出:0 1 4

生成器函數[編輯 | 編輯原始碼]

當一個函數中包含 yield 語句時,這個函數就會變成「生成器函數」。我們看以下示例:

def sliced(start=0, end=3):
    now = start
    while now < end:
        yield now
        now += 1
print(sliced)         #输出:<function sliced at *>
print(sliced())       #输出:<generator object sliced at *>
for i in sliced():
    print(i,end=" ")  #输出:0 1 2

生成器函數所返回的對象是一個生成器。該函數所返回的生成器,其中的元素就是每一次調用 yield 表達式時、yield 表達式的值。

yield from[編輯 | 編輯原始碼]

使用 yield from 語句,可以將迭代器中的元素加入到返回的生成器中。

def gf():
    yield from "123"
    yield from [1,2,3]
    yield "end"

for i in gf():
    print(i,end=" ")  #输出:1 2 3 1 2 3 end

next 函數[編輯 | 編輯原始碼]

除了用 for 語句調用之外,還可以用 next 函數來讀取生成器中的值。比如:

def g():
    yield 1
    yield 2

generator = g()
print(next(generator)) #输出:1
print(next(generator)) #输出:2
print(next(generator)) #报错:StopIteration

next 函數對於一般的迭代器也是適用的,以下內容和上面內容的輸出完全一致:

iterator = iter([1,2])
print(next(iterator))
print(next(iterator))
print(next(iterator))

鈎子[編輯 | 編輯原始碼]

鈎子(Hook)指的是監測某個事件發生的程序。當事件發生時,鈎子會按照設置執行對應的程序。以下是帶有鈎子的一個對象的示例:

class StrHook():
    def __init__(self, obj, hook=None):
        self.obj = obj
        self.hook= hook
    def __str__(self):
        #当执行 __str__ 方法时,如果设置了钩子,则触发钩子的内容。
        if self.hook != None:
            self.hook(self.obj)
        return str(self.obj)

def func(obj):
    print(obj,"执行了 __str__ 方法")

#创建对象
a = StrHook(123, hook=func)
#尝试触发对象的钩子
b = str(a)

輸出為:

123 执行了 __str__ 方法