User: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__ 方法