- [Python](#python) - [变量](#变量) - [字符串](#字符串) - [字符串首字母大写](#字符串首字母大写) - [字符串全部字符大写](#字符串全部字符大写) - [字符串全部字符小写](#字符串全部字符小写) - [字符串删除空白符](#字符串删除空白符) - [访问字符串中字符](#访问字符串中字符) - [字符串切片](#字符串切片) - [字符串迭代](#字符串迭代) - [数字](#数字) - [/](#) - [//](#-1) - [数字类型向字符串类型转换](#数字类型向字符串类型转换) - [列表](#列表) - [访问列表中元素](#访问列表中元素) - [列表元素操作](#列表元素操作) - [修改](#修改) - [插入到末尾](#插入到末尾) - [在某位置之前插入](#在某位置之前插入) - [删除列表中的元素](#删除列表中的元素) - [列表与栈api](#列表与栈api) - [remove](#remove) - [列表排序](#列表排序) - [sort](#sort) - [sorted](#sorted) - [列表中顺序反转](#列表中顺序反转) - [获取列表长度](#获取列表长度) - [遍历列表](#遍历列表) - [range](#range) - [max, min, sum](#max-min-sum) - [根据一个列表生成另一个列表](#根据一个列表生成另一个列表) - [切片](#切片) - [列表复制](#列表复制) - [元组](#元组) - [if](#if) - [and/or](#andor) - [列表中是否包含某值](#列表中是否包含某值) - [列表中是否不包含某值](#列表中是否不包含某值) - [多分支if/elif/else](#多分支ifelifelse) - [字典](#字典) - [向字典中添加键值对](#向字典中添加键值对) - [删除字典中的键值对](#删除字典中的键值对) - [字典遍历](#字典遍历) - [按顺序遍历字典的key](#按顺序遍历字典的key) - [while](#while) - [函数](#函数) - [参数默认值](#参数默认值) - [接收多个参数](#接收多个参数) - [文件操作](#文件操作) - [文件读取](#文件读取) - [写入文件](#写入文件) - [文件末尾追加](#文件末尾追加) - [module](#module) - [运行module](#运行module) - [module search path](#module-search-path) - [dir](#dir) - [package](#package) - [from ... import \*](#from--import-) - [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命令交互) - [正则](#正则) - [match](#match) - [search](#search) - [pattern](#pattern) - [findall](#findall) - [finditer](#finditer) - [面向对象](#面向对象) - [命名空间](#命名空间) - [module](#module-1) - [module name](#module-name) - [module statements](#module-statements) - [when statements will be executed](#when-statements-will-be-executed) - [from ... import ...](#from--import--1) - [as](#as) - [execute module as script](#execute-module-as-script) - [module search path](#module-search-path-1) - [`sys.path`初始化](#syspath初始化) - [standard module](#standard-module) - [dir function](#dir-function) - [packages](#packages) - [import \* from package](#import--from-package) - [包内部相互引用](#包内部相互引用) # Python ## 变量 在python中,可以通过如下方式创建变量: ```python message = "Hello Python Crash Course world!" ``` ## 字符串 ### 字符串首字母大写 ```python name = "ada lovelace" print(name.title()) ``` ### 字符串全部字符大写 ```python name = "Ada Lovelace" print(name.upper()) ``` ### 字符串全部字符小写 ```python name = "Ada Lovelace" print(name.lower()) ``` ### 字符串删除空白符 ```py name = " Asahi Ogura ` # 删除左空白 name.lstrip() # 删除右空白 name.rstrip() # 删除左右空白 name.strip() ``` ### 访问字符串中字符 ptyhon可以通过数组下标来访问字符串中的字符,如果字符串长度为n,那么索引范围为`[0, n-1]`,同时,在python中还可以使用负数的索引,索引范围为`[-n, -1]`。 在python字符串中,第一个字符可以通过索引下标`0`和`-n`来访问,最后一个字符可以通过`n-1`和`-1`来访问。 ### 字符串切片 字符串切片可以通过`str[from:to:step]`来标识,其标识下标为`[from, to)`范围内的子字符串,步长为`step`。 ```py temp = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ``` 其中,输出`[0,10)`范围内的字符串可以采用如下方式: ```py print(temp[0:10]) # ABCDEFGHIJ print(temp[0:10:1]) # ABCDEFGHIJ ``` 当步长被设置为2时,输出内容如下: ```py print(temp[0:10:2]) # ACEGI ``` 正数索引和负数索引混用: ```py # 输出不包含末尾2个字符的子字符串 print(temp[0:-2]) # ABCDEFGHIJKLMNOPQRSTUVWX print(temp[0:24]) # ABCDEFGHIJKLMNOPQRSTUVWX print(temp[:-2]) # ABCDEFGHIJKLMNOPQRSTUVWX # 输出末尾两个字符的子字符串 print(temp[-2:]) ``` 逆序输出字字符串 ```py print(temp[::-1]) # ZYXWVUTSRQPONMLKJIHGFEDCBA print(temp[-1:0:-1]) # ZYXWVUTSRQPONMLKJIHGFEDCBA print(temp[25:0:-1]) # ZYXWVUTSRQPONMLKJIHGFEDCBA ``` ### 字符串迭代 python中字符串可迭代,示例如下: ```py temp = "ABCDE" for ind, ch in enumerate(temp): print("ind = "+str(ind)+", ch = "+ch, end="\n") # ind = 0, ch = A # ind = 1, ch = B # ind = 2, ch = C # ind = 3, ch = D # ind = 4, ch = E ``` ## 数字 ### / python中,整数执行`/`操作默认并不会进行整除,而是可能生成浮点数 ```py print(3/2) # 1.5 ``` ### // python中,如果要执行整除,应该使用`//`运算符,该运算符为向下取整 ```py print(3//2) # 1 print(-3//2) # -2 ``` > 和java不同,java在执行整数除时,会舍弃掉小数部分,例如`-3/2`,java中结果为-1,而python则是向下取整为-2 > > 如果python中`/`操作想要和java保持一直,可以使用math中的`trunc`函数,`math.trunc(-3/2)`返回结果和java中一样,为`-1` ### 数字类型向字符串类型转换 可以通过str函数将数字类型转化为字符串类型 ```py temp = 1.2 print("temp = "+str(temp)) ``` ## 列表 python中通过`[e1, e2, ...]`的形式来标识列表 ### 访问列表中元素 可以通过数组下标的方式来访问列表中的元素 ```py temp = [1, "2", "san"] print(temp[1]) # 2 print(temp[-2]) # 2 ``` ### 列表元素操作 #### 修改 ```py temp = [1, "2", "san"] temp[2] = 3.0 print(temp) # [1, '2', 3.0] ``` #### 插入到末尾 如果想要向数组末尾插入元素,可以调用append方法 ```py temp = [1, "2", "san"] temp.append("four") print(temp) # [1, '2', 'san', 'four'] ``` #### 在某位置之前插入 如果想要在特定位置之前插入元素,可以调用insert方法 ```py temp = [1, "2", "san"] temp.insert(1, "1.5") print(temp) # [1, '1.5', '2', 'san'] temp.insert(4, "four") print(temp) # [1, '1.5', '2', 'san', 'four'] ``` #### 删除列表中的元素 如果想要删除列表中的元素,可以通过del关键字进行删除 ```py temp = [1, "2", "san"] del temp[1] print(temp) # [1, 'san'] ``` #### 列表与栈api 列表append操作是将元素追加到列表末尾,同时列表还支持pop操作,将列表末尾的元素作为栈顶元素弹出。 如果此时列表为空,那么调用pop方法将抛出异常 ```py temp = [1, "2", "san"] print(temp.pop()) # san print(temp.pop()) # 2 print(temp.pop()) # 1 print(temp.pop()) # IndexError: pop from empty list ``` 还可以为pop方法指定一个下标,此时可以弹出任何位置的元素 ```py temp = [1, "2", "san"] print(temp.pop(1)) # 2 print(temp.pop(0)) # 1 print(temp.pop(0)) # san ``` #### remove 在列表中,可以调用remove来移除列表中值为xxx的元素。 remove默认只会移除第一个匹配的元素 ```py temp = [2, 1, 2, 1, 2] temp.remove(2) print(temp) # [1, 2, 1, 2] ``` ### 列表排序 #### sort 可以通过sort方法针对列表中的元素进行排序,sort方法会该表列表中的元素顺序 ```py temp = [2, 1, 2, 1, 2] temp.sort() print(temp) # [1, 1, 2, 2, 2] ``` 如果想要逆向排序,可以为sort方法指定`reverse=True` ```py temp = [2, 1, 2, 1, 2] temp.sort(reverse=True) print(temp) # [2, 2, 2, 1, 1] ``` #### sorted 如果不想改变原列表中的元素顺序,而是生成一个排好序的新列表,可以调用sorted函数 ```py temp = [2, 1, 2, 1, 2] print(sorted(temp)) # [1, 1, 2, 2, 2] print(sorted(temp, reverse=True)) # [2, 2, 2, 1, 1] print(temp) # [2, 1, 2, 1, 2] ``` ### 列表中顺序反转 如果不想对列表中元素进行排序,只是想逆序列表中的数据,可以调用reverse方法 ```py temp = [2, 1, 2, 1, 3] temp.reverse() print(temp) # [3, 1, 2, 1, 2] ``` ### 获取列表长度 可以通过len函数获取列表的长度 ```py temp = [1,2,3] temp.pop() print(len(temp)) # 2 temp.pop() print(len(temp)) # 1 temp.pop() print(len(temp)) # 0 ``` ### 遍历列表 在python中,可以通过`for ... in ...`的形式来遍历列表中的元素 ```py temp = [1, 2, 3] for e in temp: print(e) # 1 # 2 # 3 ``` ### range 在python中,可以通过range函数生成一系列数字,`range(m,n)`会生成`[m,n)`范围内的数字,可用于`fori`形式的遍历 ```py arr = ["1", "3", "2"] for ind in range(0, len(arr)): print(arr[ind]) # 1 # 3 # 2 ``` 可以通过range函数来创建数字列表 ```py print(list(range(0, 5))) # [0, 1, 2, 3, 4] ``` range也可以指定步长 ```py print(list(range(0, 7, 2))) # [0, 2, 4, 6] ``` ### max, min, sum 针对数字型的列表,支持max、min、sum等函数来求最大值、最小值、总和 ```py arr = list(range(1, 101)) print(max(arr)) # 100 print(min(arr)) # 1 print(sum(arr)) # 5050 ``` ### 根据一个列表生成另一个列表 python中,根据`[expr(e) for e in list_a]`的语法,可以快速根据一个列表得到应一个列表,目标列表中的每个元素都来源于来源列表的映射 ```py arr = ['amd', 'nvidia'] graphic_msgs = ['fuck '+brand for brand in arr] print(graphic_msgs) # ['fuck amd', 'fuck nvidia'] ``` ### 切片 列表的切片和字符串切片类似 ```py arr = [1, 2, 3, 4, 5] print(arr[::2]) # [1, 3, 5] print([e**2 for e in arr[::2]]) # [1, 9, 25] ``` ### 列表复制 在python中,如果要复制列表,可以创建整个列表的切片 ```py arr = [1, 2, 3] # 复制列表 arr_copied = arr[:] print(arr_copied) # [1, 2, 3] # 对复制后的列表进行写操作,并不会影响复制前的列表 arr_copied.pop() print(arr_copied) # [1, 2] print(arr) # [1, 2, 3] ``` ## 元组 运行时列表中的元素可以被修改,如果想要让元素不可变,可以使用元组。 元组通过`(e1, e2, e3, ...)`形式来表示,和列表一样,元组也可以通过索引来访问。 但是元组中的元素无法复制,在执行赋值语句时会抛出异常。 ```py pos = (50, 100) pos[0] = 125 # TypeError:'tuple' object does not support item assignment ``` ## if 类似其他编程语言,python也支持if/else语法 ```py cars = ['audi', 'bmw', 'subaru', 'toyota'] for car in cars: if car == 'bmw': print(car.upper()) else: print(car.title()) ``` ### and/or python可以通过`and`和`or`操作符来拼接多个条件。 ```py age = 179 is_alive = True if age < 0 or age > 150 and is_alive: print("age wired") ``` ### 列表中是否包含某值 如果想要检查列表中是否包含特定值,可以采用`xxx in list_a`的语法。 ```py arr = [1, 3, 2] print(1 in arr) # True ``` ### 列表中是否不包含某值 如果想要检查列表中是否不包含特定值,可以采用`xxx not in list_a`的语法。 ```py arr = [1, 3, 2] print(1 not in arr) # False ``` ### 多分支if/elif/else python可以通过if/elif/else的语法实现多分支判断 ```py age = 40 if age < 20: print("teen") elif age < 40: print("man") else: print("elder man") ``` ## 字典 在python中,通过字典实现了其他语言中的Map数据结构。 ```py dic = {"name": "Ogura Asahi", "age": 17} print(dic['name']) # Ogura Asahi ``` ### 向字典中添加键值对 ```py dic = {"name": "Ogura Asahi", "age": 17} dic['major'] = "student" print(dic) # {'name': 'Ogura Asahi', 'age': 17, 'major': 'student'} ``` ### 删除字典中的键值对 ```py dic = {"name": "Ogura Asahi", "age": 17} del dic['age'] print(dic) # {'name': 'Ogura Asahi'} ``` ### 字典遍历 字典遍历可以通过`for k, v in dic.items()`的形式完成 ```py dic = {"name": "Ogura Asahi", "age": 17} for key, val in dic.items(): print("key = "+key+" , value = "+str(val)) ``` 如果只想遍历字典的key集合,可以通过`for k in dic.keys()`的方式 ```py dic = {"name": "Ogura Asahi", "age": 17} for key in dic.keys(): print("key = "+key) ``` 如果只想遍历字典的value集合,可以通过`for v in dic.values()`的形式 ```py dic = {"name": "Ogura Asahi", "age": 17} for value in dic.values(): print("value = "+str(value)) ``` ### 按顺序遍历字典的key 通常,字典的key遍历顺序是不一定的。如果想要按排序后的顺序遍历key,可以采用如下方式: ```py dic = {"name": "Ogura Asahi", "age": 17} for key in sorted(dic.keys()): print("key = "+str(key)) ``` ## while ```py i = 0 while i < 10: print(i) i += 1 ``` ## 函数 函数可通过如下方式来定义: ```py def fuck(brand, boss): print("fuck you " + boss + " and your " + brand) fuck("Nvidia", "Jensen Huang") ``` 在调用函数时,可以打乱传入实参顺序,只需要传参时指定参数名称即可: ```py def fuck(brand, boss): print("fuck you " + boss + " and your " + brand) fuck(boss="Jensen Huang", brand="Nvidia") ``` ### 参数默认值 声明函数时,可以为参数指定默认值,故而在调用函数时,可以对有默认值的参数进行省略 ```py def fuck(boss, brand="Nvidia"): print("fuck you " + boss + " and your " + brand) fuck("Jensen Huang") fuck(brand="Apple", boss="Cook") ``` ### 接收多个参数 通过`*paramTup`的形式,python会将函数接收到的参数都存储在一个元组中。 ```py def fuck(*name_list): msg = "fuck " is_head = True for name in name_list: if not is_head: msg += ", " is_head = False msg += name print(msg) fuck("Nvidia", "Amd") ``` ## 文件操作 ### 文件读取 ```py with open('pi_digits.txt') as file_object: contents = file_object.read() print(contents) ``` ### 写入文件 ```py filename = 'programming.txt' with open(filename, 'w') as file_object: file_object.write("I love programming.") file_object.write("I love creating new games.") ``` ### 文件末尾追加 ```py filename = 'programming.txt' with open(filename, 'a') as file_object: file_object.write("I also love finding meaning in large datasets.\n") file_object.write("I love creating apps that can run in a browser.\n") ``` ## module 在python中,支持将一些公用的实现放在一个文件中,在使用时可以在其他script中导入该文件并对公共实现进行调用。 此时,放置公共实现的文件称为module,module中的定义可以被其他module导入。 module是一个包含python定义和statement的文件,`file name`是`module name`加上`.py`。在module中,module name可以通过global变量`__main__`来进行访问。 module中可以包含executable statement和函数定义,statements则是为了初始化module。executable statement只会在module被初次import时执行。 每个module都会有其独有的private namespace,该namespace会用作module中所有函数的global namespace。故而,module的作者可以放心的在mdoule中使用global变量,module global变量有其自己的namespace,并不用担心module global变量和调用方的global变量发生冲突。 故而,可以通过`modname.itemname`来访问module中的global变量。 如果在`module A`中对`module B`执行了导入操作,那么被导入的`module B`中的names将会被全部添加到`module A`的global namespace中。 导入语法如下: ```python # 直接导入fibo中的names from fibo import fib, fib2 fib(500) ``` ```python # 从module中导入所有names,_开头的name不会被导入 from fibo import * fib(500) ``` ```python # 如果import语句后跟随了as,那么as后跟随的名称将会和被导入module绑定 import fibo as fib fib.fib(500) ``` ```python # 和上一个的效果相同,module通过`fibo`来引用 import fibo ``` ```python from fibo import fib as fibonacci fibonacci(500) ``` ### 运行module 当想要运行module时,可以采用如下方法: ```bash python fibo.py ``` 当有一段代码,只有当module以main module的情况下被调用时才会被执行,可以采用如下方式 ```python if __name__ == '__main__': # run some code ``` ### module search path 当module被导入时,python interpreter首先会在`sys.builtin_module_names`中搜寻module name,built-in-module-names中包含如下module: ```ptyhon3 >>> import sys >>> sys.builtin_module_names ('_abc', '_ast', '_bisect', '_blake2', '_codecs', '_collections', '_csv', '_datetime', '_elementtree', '_functools', '_heapq', '_imp', '_io', '_locale', '_md5', '_operator', '_pickle', '_posixsubprocess', '_random', '_sha1', '_sha256', '_sha3', '_sha512', '_signal', '_socket', '_sre', '_stat', '_statistics', '_string', '_struct', '_symtable', '_thread', '_tracemalloc', '_warnings', '_weakref', 'array', 'atexit', 'binascii', 'builtins', 'cmath', 'errno', 'faulthandler', 'fcntl', 'gc', 'grp', 'itertools', 'marshal', 'math', 'posix', 'pwd', 'pyexpat', 'select', 'spwd', 'sys', 'syslog', 'time', 'unicodedata', 'xxsubtype', 'zlib') >>> ``` 如果builtin_module_names中不包含被导入的module name,那么其会在`sys.path`中寻找`{module}.py`文件,`sys.path`通过如下路径来初始化: - input script所处的目录路径 - `PYTHONPATH`全局变量,语法和`PATH`相同 - 安装包默认位置 ### dir 通过内置的dir函数,可以输出module中的names ```bash >>>import fibo, sys >>>dir(fibo) ['__name__', 'fib', 'fib2'] >>>dir(sys) ['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework', '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook', 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info', 'warnoptions'] ``` 当dir函数传参为空时,输出当前namespace的names ```bash >>>a = [1, 2, 3, 4, 5] >>>import fibo >>>fib = fibo.fib >>>dir() ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys'] ``` dir并不会输出内置的names,内置的names定义在`builtins`module中: ```bash >>>import builtins >>>dir(builtins) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'] ``` ### package 假设python项目结构如下,package和子package中包含多个moduels: ``` sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for file format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ... ``` 在导入包时,会在sys.path中搜索package的子目录。 如果需要python将目录视为package,需要在目录下加入`__init__.py`文件,该文件可以为空。 使用者可以导入package中独立的模块: ```python import sound.effects.echo sound.effects.echo.echofilter(input, output, delay=0.7, atten=4) ``` ```python from sound.effects import echo echo.echofilter(input, output, delay=0.7, atten=4) ``` #### from ... import * 对于`from {package} import *`,其会查看是否包下定义的`__init__.py`文件中定义了`__all__`变量,如果定义了,那么会导入`__all__`中的所有module。 ```python __all__ = ["echo", "surround", "reverse"] ``` ## 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: 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) ``` 输出内容为: ``` ('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: 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: e.add_note('Add some information') e.add_note('Add some more information') raise ``` 其输出如下: ```py Traceback (most recent call last): File "", line 2, in raise TypeError('bad type') TypeError: bad type Add some information Add some more information ``` ```py 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 "", 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 +------------------------------------ ``` ## 多线程 python可以通过引入threading和concurrent.futures两个包来引入多线程: ```py import math import time from concurrent.futures import ThreadPoolExecutor, as_completed import threading import requests ts_arr = [] lock = threading.Lock() def query_ts(): try: body = requests.get("https://api.m.taobao.com/rest/api3.do", { "api": "mtop.common.getTimestamp" }).json() except Exception as e: print(e) raise def query_ts_once(): ts_beg = time.time_ns() query_ts() ts_end = time.time_ns() ts_spend = math.trunc((ts_end - ts_beg) / 1000000) with lock: ts_arr.append(ts_spend) def stress_test(test_cnt): with ThreadPoolExecutor(max_workers=32) as executor: i = 0 f_list = [] while i < test_cnt: f_t = executor.submit(query_ts_once) f_list.append(f_t) i += 1 as_completed(f_list) stress_test(100) print(f"max ts : {max(ts_arr)} ms") print(f"min ts : {min(ts_arr)} ms") print(f"avg ts : {sum(ts_arr) / len(ts_arr)} ms") print(ts_arr) ``` ## linux命令交互 如果想要调用系统(linux)的命令,可以使用subprocess模块。 ```py subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None) ``` 调用示例如下: ```py ret = subprocess.run(['ls', '-l'], capture_output=True, text=True) print(ret.stdout) ``` 输出如下: ```bash total 4 -rwxrwxrwx 1 asahi asahi 110 May 4 03:18 main.py -rwxrwxrwx 1 asahi asahi 73 May 4 01:10 shiro.json drwxrwxrwx 1 asahi asahi 4096 Feb 7 21:39 venv ``` ## 正则 python通过re模块提供了正则表达式的支持 ### match match方法定义如下所示 ```py re.match(pattern, string, flags=0) ``` match方法使用示例如下所示: ```py import re def main(): match_res = re.match("(?P.*) father", "i am your father") if match_res is None: print("failed match") return print(match_res.group("prefix")) # i am your print(match_res.group(0)) # i am your father main() ``` ### search search方法定义如下所示 ```py re.search(pattern, string, flags=0) ``` search方法使用示例如下所示 ### pattern 类似java,re提供了compile方法,返回正则编译后的pattern,并通过pattern执行操作 #### findall `pattern.findall`方法会返回所有匹配的字符串 ```py import re def main(): msg = '''although i use amd graphic card, i view it as a trash for its bad performance in ai. nvidia is also trash because its crazy price, fuck amd and nvidia!''' pattern = re.compile("(amd|nvidia)\\s+(graphic card)?") search_res_list = pattern.findall(msg) print(search_res_list) main() # [('amd', 'graphic card'), ('nvidia', ''), ('amd', '')] ``` #### finditer `pattern.finditer`会返回所有匹配的match对象 ```py import re def main(): msg = '''although i use amd graphic card, i view it as a trash for its bad performance in ai. nvidia is also trash because its crazy price, fuck amd and nvidia!''' pattern = re.compile("(amd|nvidia)\\s+(graphic card)?") search_res_list = pattern.finditer(msg) print([e.group(0) for e in search_res_list]) main() # ['amd graphic card', 'nvidia ', 'amd '] ``` ## 面向对象 ### 命名空间 命名空间是name到对象的映射集合,在不同的命名空间中,相同name映射到的对象可能会不同。 namespace的示例有: - 内置name集合:(包含内置异常名称和函数名,例如`abs()`函数) - module中的global names - 函数调用中的local names > 在某种程度上来说,对象中的attributes也构成了一个命名空间 ## module python支持将文件内容拆分到多个文件中,将function definition放置到文件中后,在`其他文件`或`interactive instance`中可以针对fucntion definition进行导入。 放置function definition的文件被称之为`module`,通常用来定义公共方法。 ### module name module为包含python definition和statements的文件。file name由module name和`.py`后缀组成。在module中,module的名称可以通过全局变量名`__name__`进行访问。 如果在文件`fibo.py`中定义如下内容: ```py # Fibonacci numbers module def fib(n): """Write Fibonacci series up to n.""" a, b = 0, 1 while a < n: print(a, end=' ') a, b = b, a+b print() def fib2(n): """Return Fibonacci series up to n.""" result = [] a, b = 0, 1 while a < n: result.append(a) a, b = b, a+b return result ``` 那么`fibo`module中的内容可以通过如下方式进行导入 ```py import fibo ``` 上述导入并不会将定义在`fibo`module中的function name直接添加到当前的命名空间,其只是将module name`fibo`添加到了命名空间,可以通过module name访问module中定义的内容: ```py fibo.fib(1000) fibo.fib2(100) fibo.__name__ ``` ### module statements 在module中除了可以包含function definition外,还可以包含可执行statements。该可执行statements用于对module进行初始化。 #### when statements will be executed module中的statements只会在`第一次该module被导入`时执行。(如果module文件其本身被执行,而非作为依赖被其他文件导入,那么module中的statements也同样会被执行) 每个module都拥有其自己的private namespace,并且对于定义在module中的所有方法而言,`private module`既是`global namespace`。故而,作为module的开发者而言,其可以自由的使用global variables,而不用担心其global variables名称和module使用者定义的global variables发生冲突。 如果想要访问在module中定义的global variables,可以使用`modname.itemname`的方式。 通常而言,对于module的import被放置在文件的最外层最开始的部分,`但这并非是必须的`。在文件最外层被导入的module,其module name会被添加到当前文件的module glboal namespace中。 #### from ... import ... import还存在一个变体,可以从module中直接导入name,而不是导入module后通过module name访问module中内容: ```py from fibo import fib, fib2 fib(500) ``` 在上述示例中,`fibo`并没有被引入到当前文件的global namespace中。 除此之外,还可以导入module中所有的name ```py from fibo import * fib(500) ``` 同样的,`fibo`也不会被导入到当前global namespace中。 上述`from fibo import *`语句会导入`fibo`module中定义的所有name,但是以下划线`_`开头的名称除外。 通常情况下,不要使用`from fibo import *`来进行导入,因为其引入的name是不确定的,可能会覆盖你所定义的部分内容。 #### as 在对其他module执行import操作时,可以通过as来进行重命名 ```py import fibo as fib fib.fib(500) ``` 上述示例中,`fib`的名称将会绑定到module `fibo`。 ```py from fibo import fib as fibonacci fibonacci(500) ``` ### execute module as script 当想要将module作为script执行时,可以使用如下命令: ```bash python fibo.py ``` 上述`将module作为script执行的命令`,和`导入module时对module进行执行`的行为实际上是一致的,唯一区别如下: - `module作为script执行`: `__name__`值为`__main__` - `module被导入`: `__name__`值为`module name` 故而,在module中包含如下代码 ```py if __name__ == "__main__": import sys fib(int(sys.argv[1])) ``` 可以使module既可作为module被导入,也可以作为可执行的脚本,其中该代码块只会在module作为main file时才会被执行。 并且,在module被导入时,`sys`并不会被导入。 ### module search path 当module名为`spam`的module被导入时,interpreter会按照如下顺序来进行查找: 1. 首先,interpreter会从built-in module中查找`spam` 2. 如果步骤1没有查询到,其会在`sys.path`变量定义的路径中查找名为`spam.py`的文件 #### `sys.path`初始化 在python启动时,其会初始化module search path,初始化后的module search path可以通过`sys.path`来进行访问。 在`sys.path`中,组成如下: - `sys.path`中第一条entry是包含input script的目录(如果存在input script) - 若未指定input script,如`运行交互式shell`、`运行-c command`、`运行-m module`时,`sys.path`的第一条entry则是当前目录 - `PYTHONPATH`环境变量中定义的路径也会被添加到search path中 - 包含`standard python modules及其依赖的拓展modules`的路径也会别添加到module search path中 - 拓展modules在windows平台中后缀名为`.pyd`,在其他平台中后缀名为`.so` - 其中,`standard python modules`是平台无关的,被称为`prefix` - `extension modules`则是平台相关的,被称为`exec_prefix` > python支持通过`-c`来直接运行python命令,示例如下 > ```shell > python -c 'print(1+22*33)' > ``` > > python还支持通过`-m`来直接运行module,示例如下 > ```bash > echo '{"name":"John","age":30}' | python -m json.tool > ``` > 部分系统支持symlinks,故而包含input script的目录路径会在input script的symlink被follow之后才进行计算。故而,`包含symlink`的目录并不会被添加到sys.path中 在`sys.path`被初始化之后,python程序可以对`sys.path`进行修改。 在`sys.path`中,包含被运行文件的目录路径被放在了`sys.path`中最开始的位置,代表如果运行文件路径中如果存在和`standard library module`相同名称的文件,那么运行文件路径下的同名文件将会覆盖`standard library module`。 ### standard module python附带了一个包含standard modules的library。`standard modules`中部分modules是内置在interpreter中的,built modules中的内容可能并不是语言核心操作的一部分,但是能够提供对操作系统的访问,例如系统调用等。 built-in modules中的module在不同平台是可选的,例如`winreg` module只存在于windows平台。但是,`sys`module在所有平台的python interpreter中都包含。 ### dir function 内置的`dir()`方法会查看在module中定义了哪些名称,其返回了一个sorted string list: ```bash >>>import fibo, sys >>>dir(fibo) ['__name__', 'fib', 'fib2'] >>>dir(sys) ['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework', '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook', 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info', 'warnoptions'] ``` 当没有为`dir()`方法传递参数时,`dir()`方法会列出当前已经定义的名称: ```python >>> a = [1, 2, 3, 4, 5] >>> import fibo >>> fib = fibo.fib >>> dir() ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys'] ``` 其列出的名称中包含所有类型:变量、module、function等 `dir`方法并不会列出内置的方法和变量,如果想要查看那些,可以使用`dir(builtins)` ```bash import builtins dir(builtins) ``` ### packages packge是一种结构化python modules命名空间的方法,其使用了`dotted module names`。例如,module name为`A.B`其代表的是位于package A下的submodule B。 就像module可以使module的开发者们不用担心使用了和其他module中一样的变量/函数名一样,dotted module name可以令module的开发者不用担心使用了和其他module相同的module name。 如果,你要定义一系列的modules统一处理不同格式的sound files和sound data,可以采用如下的package结构: ``` sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for file format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ... ``` 当对package进行导入时,python会从`sys.path`中查询package的子目录。 其中,`__init__.py`文件用于`让python将该目录看作是package`。`__init__.py`文件可以是空文件,但是`也可以包含对package进行初始化的代码`或`设置__all__变量`。 在使用package中,可以单独导入package中的module: ```py import sound.effects.echo ``` 上述示例中导入了`sound.effects.echo`module,在对module进行使用时,必须采用如下方式指定mdoule的fullname: ```py sound.effects.echo.echofilter(input, output, delay=0.7, atten=4) ``` 另一种可选的导入方式如下: ```py from sound.effects import echo ``` 在使用module时,只需要指定echo即可 ```py echo.echofilter(input, output, delay=0.7, atten=4) ``` 同时,也可以直接导入module中的方法: ```py from sound.effects.echo import echofilter echofilter(input, output, delay=0.7, atten=4) ``` 在使用`import item.subitem.subsubitem`这类语法时,除了最后的item外,其余item必须都要是package,最后的item可以是package或module,但不能是module中的内容。 #### import * from package 在对package执行`import *`的语法时,其导入行为准许如下规范: - 如果package的`__init__.py`文件中定义了`__all__`变量,其将认为`__all__`中定义了所有应该被`import *`所导入的module name 故而,package的开发者应当负责在package版本迭代时将`__all__`进行更新;当然,开发者页可以决定不对`import *`进行支持,不定义`__all__`。 示例如下,`sound/effects/__init__.py`中可能包含如下内容: ```py __all__ = ["echo", "surround", "reverse"] ``` 其代表`from sound.effect import *`将会导入`echo, surround, reverse`三个module。 注意,本地定义的名称可能会遮挡submodules。例如,如果在`sound/effects/__init__.py`中定义了一个reverse函数,那么`from sound.effects import *`将只会导入`echo, surround`两个module。 ```py __all__ = [ "echo", # refers to the 'echo.py' file "surround", # refers to the 'surround.py' file "reverse", # !!! refers to the 'reverse' function now !!! ] def reverse(msg: str): # <-- this name shadows the 'reverse.py' submodule return msg[::-1] # in the case of a 'from sound.effects import *' ``` 如果在`__init__.py`中没有定义`__all__`,那么`from sound.effects import *`只会导入`__init__.py`中的其他定义内容,示例如下: ```py import sound.effects.echo import sound.effects.surround from sound.effects import * ``` ### 包内部相互引用 在同一个包中,不同subpackage中的module可以对彼此进行引用。引用支持绝对路径和相对路径,例如,`sound.filters.vocoder`在对`sound.effects`中定义的包进行import时,可以采用如下方式: - 绝对路径:`from sound.effects import echo` - 相对路径:`from ..effects import echo` 在使用相对路径的导入时,示例如下: 当从`surround`执行相对路径导入时,可以执行如下形式的导入 ```py from . import echo # 位于相同的subpackage中 from .. import formats # 访问../formats from ..filters import equalizer # 访问../filters中的equalizer ```