Files
rikako-note/python/py.md
2025-08-17 05:28:33 +08:00

1574 lines
52 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- [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中导入该文件并对公共实现进行调用。
此时放置公共实现的文件称为modulemodule中的定义可以被其他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 <arguments>
```
当有一段代码只有当module以main module的情况下被调用时才会被执行可以采用如下方式
```python
if __name__ == '__main__':
# run some code
```
### module search path
当module被导入时python interpreter首先会在`sys.builtin_module_names`中搜寻module namebuilt-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)
```
输出内容为:
```
<class 'Exception'>
('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 "<stdin>", line 2, in <module>
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 "<stdin>", line 4, in <module>
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 "<stdin>", line 2, in <module>
func()
~~~~^^
File "<stdin>", line 2, in func
ConnectionError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
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 "<stdin>", line 4, in <module>
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 "<stdin>", line 2, in <module>
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 "<stdin>", line 1, in <module>
| raise ExceptionGroup('We have some problems', excs)
| ExceptionGroup: We have some problems (3 sub-exceptions)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| f()
| ~^^
| File "<stdin>", line 2, in f
| raise OSError('operation failed')
| OSError: operation failed
| Happened in Iteration 1
+---------------- 2 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| f()
| ~^^
| File "<stdin>", line 2, in f
| raise OSError('operation failed')
| OSError: operation failed
| Happened in Iteration 2
+---------------- 3 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| f()
| ~^^
| File "<stdin>", 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<prefix>.*) 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
类似javare提供了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 <arguments>
```
上述`将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
```