From 0e2445ccbf46d8f3ba736f8998dd41d1e2bc2c70 Mon Sep 17 00:00:00 2001 From: asahi Date: Sun, 17 Aug 2025 05:28:33 +0800 Subject: [PATCH] =?UTF-8?q?doc:=20=E9=98=85=E8=AF=BBpython=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/py.md | 437 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 328 insertions(+), 109 deletions(-) diff --git a/python/py.md b/python/py.md index 8a8ef0f..a99d183 100644 --- a/python/py.md +++ b/python/py.md @@ -57,14 +57,19 @@ - [dir](#dir) - [package](#package) - [from ... import \*](#from--import-) - - [异常处理](#异常处理) - - [抛异常](#抛异常) - - [捕获异常后重新抛出异常](#捕获异常后重新抛出异常) - - [主动抛出异常](#主动抛出异常) - - [数据存储](#数据存储) - - [json.dump](#jsondump) - - [json.load](#jsonload) - - [http api](#http-api) + - [Errors and Exceptions](#errors-and-exceptions) + - [Syntax Errors](#syntax-errors) + - [Exceptions](#exceptions) + - [handling exceptions](#handling-exceptions) + - [python异常结构](#python异常结构) + - [rasing exception](#rasing-exception) + - [Exception Chaining](#exception-chaining) + - [用户自定义异常类型](#用户自定义异常类型) + - [define cleanup action](#define-cleanup-action) + - [predefined clean-up action](#predefined-clean-up-action) + - [rasing multi exceptions](#rasing-multi-exceptions) + - [except\*](#except) + - [enriching exceptions with notes](#enriching-exceptions-with-notes) - [多线程](#多线程) - [linux命令交互](#linux命令交互) - [正则](#正则) @@ -786,123 +791,337 @@ echo.echofilter(input, output, delay=0.7, atten=4) __all__ = ["echo", "surround", "reverse"] ``` -## 异常处理 -在python中,可以通过如下语法来进行异常处理 +## Errors and Exceptions +在python中存在两种不同的异常,`syntax erros`和`exceptions` + +### Syntax Errors +syntax errors又被称为parsing errors,当parser检测到语法错误时,会抛出该error + +### Exceptions +在python程序执行时,即使语法都正常,也有可能在执行时发生异常。在程序执行时被检测到的异常被称为`exceptions`,但是其时可以被处理的,并不会无条件的造成fatal。 + +### handling exceptions +可以在程序中对特定类型的异常进行处理,示例如下所示: +```py +while True: + try: + x = int(input("Please enter a number: ")) + break + except ValueError: + print("Oops! That was no valid number. Try again...") +``` +上述示例中针对`ValueError`类型的异常进行了捕获,并提示用户输入有效的数字。但是,在上述程序实例中,用户仍然能够通过`Ctrl + C`中断该程序。 + +> `Ctrl + C`将会以抛出`KeyboardInterrupt`的形式表现。 + +try statement中可以包含多个except,为不同类型的异常指定handler,最多只会有一个handler被执行。并且,一个except中也可以包含多个exceptions,示例如下所示: +```py +... except (RuntimeError, TypeError, NameError): + ... pass +``` +包含多个except的示例如下所示: +```py +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") +``` +上述示例中,会输出`B, C, D`。但是,如果修改`except B`的位置为第一位,那么输出将会变为`B, B, B`。 + +在except语法中,除了指定异常类型外,还支持指定一个变量名来捕获异常,该变量中包含`args`属性,能够访问`构造该异常对象时传递的参数`,并且,异常中还定义了`__str__()`方法用于打印所有的异常参数。 + +为捕获的异常绑定变量的示例如下所示: ```py try: - # code block - pass -except ErrorType1: - # error type 1 handling code block - pass -except ErrorType2: - # error type 2 handling code block - pass -except Exception as result: - # handle other exception types - pass -else: - # code block that no error occurs - pass -finally: - # code block executed whether exception occurs - pass + raise Exception('spam', 'eggs') + except Exception as inst: + print(type(inst)) # the exception type + 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) + ``` -### 抛异常 -#### 捕获异常后重新抛出异常 -如果python在捕获异常后需要重新对异常进行抛出,可以使用`raise`关键字 +输出内容为: +``` + + ('spam', 'eggs') + ('spam', 'eggs') + x = spam + y = eggs +``` +#### python异常结构 +`BaseException`是所有异常类的common base class;而`Exception`类则是`BaseException`的子类,同时也是所有非fatal异常类的base class。 + +对于是`BaseException`子类但是不是`Exception`子类的类,其通常代表不应当被处理的异常,抛出该类异常通常代表程序应当被终止,其包含如下类: +- `SystemExit`:由`system.exit()`抛出 +- `KeyboardInterrupt`:由想要中止程序的用户触发`Ctrl + C` + +`Exception`可以被用做匹配所有异常。 +```py +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`,`else`必须跟在所有`except`之后,其代表`try block`中没有抛出任何异常的场景。 + +```py +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() +``` + +### rasing exception +`raise`关键字支持抛出异常,示例如下 +```py +raise NameError('HiThere') +``` +传递给raise的必须是`Exception`的实例或是`继承自Exception的类`,但传递的是类时,其会使用类的默认构造器来构造Exception实例。 +```py +raise ValueError # shorthand for 'raise ValueError()' +``` +`raise`关键字也能用于对异常的重新抛出,示例如下: ```py try: - m = 10/0 + raise NameError('HiThere') + except NameError: + print('An exception flew by!') + raise +``` +### Exception Chaining +如果在`except`处理异常时,处理逻辑又抛出了异常,那么其会将被处理的异常关联到新异常中,并且将被处理异常包含在error msg中: +```py +try: + open("database.sqlite") + except OSError: + raise RuntimeError("unable to handle error") +``` +其输出如下: +``` +Traceback (most recent call last): + File "", line 2, in + open("database.sqlite") + ~~~~^^^^^^^^^^^^^^^^^^^ +FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite' + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "", line 4, in + raise RuntimeError("unable to handle error") +RuntimeError: unable to handle error +``` + +如果想要设置异常的cause,可以使用`from`语法,示例如下所示: +```py +def func(): + raise ConnectionError + + try: + func() + except ConnectionError as exc: + raise RuntimeError('Failed to open database') from exc +``` +上述示例中,`raise ... from exc`直接表明RuntimeError由exc引发。 + +输出示例如下所示: +```py +Traceback (most recent call last): + File "", line 2, in + func() + ~~~~^^ + File "", line 2, in func +ConnectionError + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "", line 4, in + raise RuntimeError('Failed to open database') from exc +RuntimeError: Failed to open database +``` +通过`from None`也可以关闭异常chain,示例如下: +```py +try: + open('database.sqlite') + except OSError: + raise RuntimeError from None +``` +示例如下所示: +```py +Traceback (most recent call last): + File "", line 4, in + raise RuntimeError from None +RuntimeError +``` +### 用户自定义异常类型 +用户如果要自定义异常,可以创建`Exception`类型的子类。 + +大多数异常类型都以`Error`结尾,和standard exceptions的命名规范类似。 + +### define cleanup action +python支持通过`finally`来定义clean-up action,其在所有场景下都会被处理。 +```py +try: + raise KeyboardInterrupt +finally: + print('Goodbye, world!') +``` +其中,finally执行时机如下: +- 当抛出异常时,如果异常没有被except处理,那么异常在finally执行完后再次抛出 +- 当except或else中抛出异常时,那么异常在finally执行完后被抛出 +- 如果在finally中执行了`break, continue, return`等语句,那么异常不会被抛出 +- 如果try中执行了`break, continue, return`,那么finally会在`break, continue, return`执行之前执行 +- 如果finally中执行了return操作,那么其return的值将会覆盖try中return的值 + +finally通常被用于释放资源等操作。 + +### predefined clean-up action +部分对象定义了对象不在被需要时的clean-up action,无论针对对象的操作是否成功,clean-up action都会被执行。 + +故而,可以通过`with`对该类对象进行使用,并无需手动释放其资源,示例如下: +```py +with open("myfile.txt") as f: + for line in f: + print(line, end="") +``` +在上述代码执行完后,文件会自动关闭,即使是在执行过程中遇到异常。 + +### rasing multi exceptions +在部分时候,可能需要抛出多个异常,此时可以通过`ExceptionGroup`来对exception list进行wrap,示例如下: +```py +def f(): + excs = [OSError('error 1'), SystemError('error 2')] + raise ExceptionGroup('there were problems', excs) +``` + +#### except* +当使用`except*`时,可以选择性处理group中特定类型的异常,并将其他异常传递到下游。 + +```py +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") +``` +### enriching exceptions with notes +当创建异常时,可以通过`add_note(note)`方法来为异常补充信息,标准的traceback会按照其被添加的顺序渲染所有的note信息,note信息在异常信息之后 +```py +try: + raise TypeError('bad type') except Exception as e: - print(e) + e.add_note('Add some information') + e.add_note('Add some more information') raise -finally: - print("handle correctly") ``` -#### 主动抛出异常 -主动抛出异常时,也可使用`raise`关键字 +其输出如下: ```py -try: - raise Exception("fucking Nvidia raised their price!") -except Exception as e: - print(e) -finally: - print("my wallet is empty!") +Traceback (most recent call last): + File "", line 2, in + raise TypeError('bad type') +TypeError: bad type +Add some information +Add some more information ``` - -## 数据存储 -### json.dump -可以通过`json.dump`方法将内容以json的格式存储到文件中: ```py -import json +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) -def save_as_json(data, fd): - json.dump(data, fd) - - -waifu = { - "name": "Touma Kazusa", - "job": "Student", - "gender": "female", - "age": 17 -} - -is_save_success = True -try: - with open("shiro.json", "w") as shiro_fd: - save_as_json(waifu, shiro_fd) -except Exception as e: - print(e) - is_save_success = False - exit(1) -finally: - if is_save_success: - print("save success!") +raise ExceptionGroup('We have some problems', excs) ``` -### json.load -可以通过`json.load`方法读取文件中的json格式内容: -```py -import json - - -def read_as_json(fd): - return json.load(fd) - - -is_read_success = True -try: - with open("shiro.json", "r") as shiro_fd: - r_obj = read_as_json(shiro_fd) - print(r_obj) -except Exception as e: - print(e) - is_read_success = False - exit(1) -finally: - if is_read_success: - print("read success!") +其异常信息如下: ``` -## http api -python安装requests包之后,可以访问http接口 -```py -import math -import requests -import time - -try: - body = requests.get("https://api.m.taobao.com/rest/api3.do", { - "api": "mtop.common.getTimestamp" - }).json() - ts = body['data']['t'] - t = time.localtime(math.trunc(int(ts) / 1000)) - time_str = time.strftime("%Y-%m-%d %H:%M:%S %z", t) - print(time_str) -except Exception as e: - print(e) - raise + + Exception Group Traceback (most recent call last): + | File "", line 1, in + | raise ExceptionGroup('We have some problems', excs) + | ExceptionGroup: We have some problems (3 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | File "", line 3, in + | f() + | ~^^ + | File "", line 2, in f + | raise OSError('operation failed') + | OSError: operation failed + | Happened in Iteration 1 + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "", line 3, in + | f() + | ~^^ + | File "", line 2, in f + | raise OSError('operation failed') + | OSError: operation failed + | Happened in Iteration 2 + +---------------- 3 ---------------- + | Traceback (most recent call last): + | File "", line 3, in + | f() + | ~^^ + | File "", line 2, in f + | raise OSError('operation failed') + | OSError: operation failed + | Happened in Iteration 3 + +------------------------------------ ``` ## 多线程