2038 lines
70 KiB
Markdown
2038 lines
70 KiB
Markdown
- [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)
|
||
- [包内部相互引用](#包内部相互引用)
|
||
- [class](#class)
|
||
- [python scope and namespaces](#python-scope-and-namespaces)
|
||
- [namespace](#namespace)
|
||
- [lifetime](#lifetime)
|
||
- [LGEB](#lgeb)
|
||
- [scope](#scope)
|
||
- [global](#global)
|
||
- [nonlocal](#nonlocal)
|
||
- [global namespace](#global-namespace)
|
||
- [Class Objects](#class-objects)
|
||
- [__init__](#init)
|
||
- [data attributes](#data-attributes)
|
||
- [method object](#method-object)
|
||
- [class and instance variables](#class-and-instance-variables)
|
||
- [function object](#function-object)
|
||
- [Inheritance](#inheritance)
|
||
- [attribute resolving](#attribute-resolving)
|
||
- [method resolving](#method-resolving)
|
||
- [built-in functions](#built-in-functions)
|
||
- [private variables](#private-variables)
|
||
- [name mangling](#name-mangling)
|
||
- [odds and ends](#odds-and-ends)
|
||
- [`__self__, __func__`](#__self__--__func__)
|
||
- [iterators](#iterators)
|
||
- [generators](#generators)
|
||
- [generator expression](#generator-expression)
|
||
|
||
|
||
# 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 <arguments>
|
||
```
|
||
当有一段代码,只有当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)
|
||
|
||
```
|
||
|
||
输出内容为:
|
||
```
|
||
<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")
|
||
```
|
||
当使用`except*`语法时,即使指定了`except* OSError as e`,`e`的类型仍然是`ExceptionGroup`而不是`OSError`,因为try中抛出的`ConditionGroup`中可能含有多个`OSError`类型的异常,故而,实际的`OSError`异常对象需要通过`e.exceptions`来进行访问。
|
||
|
||
### 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
|
||
类似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 <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
|
||
```
|
||
|
||
## class
|
||
python在对对象进行传递时,只支持引用传递(传递指向对象的指针),而不支持类似c++的值传递(对象拷贝)。故而,在python中,如果对方法的入参对象进行了修改,那么该修改对调用方可见。
|
||
|
||
### python scope and namespaces
|
||
#### namespace
|
||
namespace代表name到object的映射。
|
||
|
||
常见的namespace有:
|
||
- 包含built-in names的namespace(built-in names包含类似`abs`之类的built-in函数,以及built-in exception names等)
|
||
- 包含module global names的module namespace
|
||
- 在函数调用时包含`local names`的local namespace
|
||
|
||
从某种意义上说,对象中的attributes也构成了一个namespace。
|
||
|
||
对于`module.name`的访问,其实际也是针对`module object`中`attribute`的访问:module中的global name和module attribute都是位于同一namespace中。
|
||
|
||
`attribute`分为`read-only`和`writable`的。对于`writable`的attribute,可以对其进行赋值,示例如下:
|
||
```py
|
||
modname.the_answer = 42
|
||
```
|
||
同样的,`writable`的attribute也可以通过`del`来进行删除,删除后该attribute将会从对象中被移除
|
||
```py
|
||
del modname.the_answer
|
||
```
|
||
|
||
##### lifetime
|
||
在不同时机创建的namespace拥有不同的生命周期:
|
||
- `built-in namespace`: 在python interpreter启动时被创建,并且该namespace永远不会被删除
|
||
- `module namespace`: 当module的定义被读入时,会创建module namespace,并且module namespace会持续到interpreter退出时
|
||
- 对于被interpreter执行的top-level statements(通过交互式输入或从script中读取),其被视作`__main__`module的一部分,其拥有属于自己的namespace
|
||
- `local namespace of function`:在函数被调用时创建,在函数执行完成/抛出未处理异常时namespace被删除(对于递归调用,每次调用都拥有其自己的namespace)
|
||
|
||
##### LGEB
|
||
在python中,LEGB规则决定了命名空间搜索的顺序:
|
||
- `local`: 定义在函数/类内部
|
||
- `enclosed`:定义在闭包函数内
|
||
- `global`:定义在全局/最上层
|
||
- `built-in`:python中内置的保留名称
|
||
|
||
<img src="https://picx.zhimg.com/v2-9e322a731ae838a75dd2de84dae659e7_1440w.jpg" data-size="small" data-rawwidth="300" data-rawheight="260" data-original-token="v2-00f7cd30194a00ffd46c4fd5612b82c2" class="content_image" width="300">
|
||
|
||
|
||
|
||
##### scope
|
||
scope代表python程序中的一个文本范围,在该范围内可以对某个namespace中的name进行直接访问,而无需前缀`x.y.`之类的限定名。
|
||
|
||
在程序执行的某个时间点,都会存在3/4个nested scopes可以直接访问:
|
||
- 最内层的scope,包含local names,最先查找
|
||
- scope of enclosing function,查找时会从nearest neclosing scope开始,包含`non-local and non-global names`
|
||
- `next-to-last scope`,包含global names
|
||
- 最外层scope,最后查找,包含built-in names
|
||
|
||
##### global
|
||
如果使用`global var_name`的方式来声明变量,那么针对该变量的引用和复制都会直接指向global namespace中的变量。
|
||
|
||
##### nonlocal
|
||
如果想要在local namespace中访问enclosing namespace中的name,可以通过`nonlocal var_name`形式来声明变量,示例如下:
|
||
```py
|
||
def main():
|
||
s = "fuck"
|
||
def s_change(p):
|
||
nonlocal s
|
||
s=p
|
||
s_change("shit")
|
||
print(s)
|
||
```
|
||
|
||
如果没有通过`nonlocal`进行声明,那么对于s的赋值将会看作是`在innermost scope中创建了一个同名变量并进行赋值`,外部的变量值并不会改变。
|
||
|
||
通常来说,在函数定义内,scope为innermost scope,而在函数外则是global namespace,`类定义会新开一个namespace`。
|
||
|
||
##### global namespace
|
||
对于module中定义的function,其global namespace即是module namespace,无论函数在哪里被调用。
|
||
|
||
|
||
在python中,如果没有使用`global`或`nonlocal`,那么变量是只读的,对name的赋值永远会在innermost scope中。`del`语句则是从local scope对应的namespace中移除变量的绑定。
|
||
|
||
> 当在innermost scope中,如果只对外层变量进行读操作,那么无需使用`global`或是`nonlocal`;但是,如果想要修改外层变量,则需要使用`global`或者`nonlocal`,否则外层变量是只读的。
|
||
|
||
```py
|
||
def scope_test():
|
||
def do_local():
|
||
spam = "local spam"
|
||
|
||
def do_nonlocal():
|
||
nonlocal spam
|
||
spam = "nonlocal spam"
|
||
|
||
def do_global():
|
||
global spam
|
||
spam = "global spam"
|
||
|
||
spam = "test spam"
|
||
do_local()
|
||
print("After local assignment:", spam)
|
||
do_nonlocal()
|
||
print("After nonlocal assignment:", spam)
|
||
do_global()
|
||
print("After global assignment:", spam)
|
||
|
||
scope_test()
|
||
print("In global scope:", spam)
|
||
```
|
||
在上述示例中,返回结果如下:
|
||
```
|
||
After local assignment: test spam
|
||
After nonlocal assignment: nonlocal spam
|
||
After global assignment: nonlocal spam
|
||
In global scope: global spam
|
||
```
|
||
|
||
### Class Objects
|
||
class对象支持两种类型的操作,`attribute reference`和`instantiation`。
|
||
|
||
attribute reference使用`obj.name`的语法。class定义如下:
|
||
```py
|
||
class MyClass:
|
||
"""A simple example class"""
|
||
i = 12345
|
||
|
||
def f(self):
|
||
return 'hello world'
|
||
```
|
||
`MyClass.i`和`MyClass.f`是有效的attribute reference,会返回一个integer和一个function。
|
||
|
||
`__doc__`也是一个有效的attribute,返回对该class的简单文档描述。
|
||
|
||
类实例化类似于函数的调用,其语法如下:
|
||
```py
|
||
x = MyClass()
|
||
```
|
||
上述示例会创建一个MyClass类型的对象,并且将其赋值给本地变量x。
|
||
|
||
#### __init__
|
||
python中支持在初始化时为对象指定状态,故而可以自定义构造器,示例如下:
|
||
```py
|
||
class Complex:
|
||
def __init__(self, realpart, imagpart):
|
||
self.r = realpart
|
||
self.i = imagpart
|
||
|
||
c = Complex(3.0, -4.5)
|
||
```
|
||
#### data attributes
|
||
python中的data attributes类似于c++中的data members,但是,`data attributes`并不需要被声明,其类似于local variables,其在第一次赋值时就存在了。
|
||
|
||
例如,`变量x是MyClass类的实例`,那么如下代码将会输出`16`.
|
||
|
||
#### method object
|
||
除了通过data attribute外,另一种attribute reference的方式是method。
|
||
|
||
在python中,class中的function定义了object中的method。但是,`x.f`和`MyClass.f`并不等价,前者是method object,后者是function object。
|
||
|
||
通常,method在其所绑定的对象后调用:
|
||
```py
|
||
x.f()
|
||
```
|
||
|
||
python中,`x.f`为一个method object,支持被存储在变量中,并可以在后续通过变量调用,示例如下:
|
||
```py
|
||
xf = x.f
|
||
|
||
while True:
|
||
print(xf())
|
||
```
|
||
如上所示,method object `x.f`被存储在local variable `xf`中后,后续可以通过`xf()`来调用method object。
|
||
|
||
> 按照`MyClass`的类定义,`x.f`方法将会接收一个`self`参数,但是在调用`xf()`时,并没有传递任何参数。
|
||
|
||
实际上,对于method而言,其method所绑定的对象实例将会作为参数被传向function的第一个参数`self`。
|
||
|
||
故而,对于method object和function object而言,`x.f()`实际上等价于`MyClass.f(x)`。
|
||
|
||
即,对于method object而言,`obj.method(arg1, arg2 ...)`的调用等价于`Class.method(obj, arg1, arg2 ...)`的function调用。
|
||
|
||
### class and instance variables
|
||
简单来说,每个对象的instance variables是相互独立的,但是class variables则是在同一类的所有对象间都是共享的。
|
||
|
||
```py
|
||
class Dog:
|
||
|
||
kind = 'canine' # class variable shared by all instances
|
||
|
||
def __init__(self, name):
|
||
self.name = name # instance variable unique to each instance
|
||
|
||
>>> d = Dog('Fido')
|
||
>>> e = Dog('Buddy')
|
||
>>> d.kind # shared by all dogs
|
||
'canine'
|
||
>>> e.kind # shared by all dogs
|
||
'canine'
|
||
>>> d.name # unique to d
|
||
'Fido'
|
||
>>> e.name # unique to e
|
||
'Buddy'
|
||
```
|
||
|
||
对于class variables而言,其既可以通过`MyClass.name`来进行访问,也可以通过`obj.name`来进行访问。
|
||
|
||
但是,在class variable和instance variable同名时,instance variable会优先被访问,示例如下:
|
||
```py
|
||
class Warehouse:
|
||
purpose = 'storage'
|
||
region = 'west'
|
||
|
||
w1 = Warehouse()
|
||
print(w1.purpose, w1.region)
|
||
storage west
|
||
w2 = Warehouse()
|
||
w2.region = 'east'
|
||
print(w2.purpose, w2.region)
|
||
storage east
|
||
```
|
||
|
||
在python中,用户既可以通过method来访问data attributes,也可以通过对象直接访问。并且,python中也没有类似其他语言中`private`这类强制隐藏的机制,`python中都是基于约定`。
|
||
|
||
在使用data attributes时,应当小心如下场景:
|
||
- data attributes可能被method维护和管理,若用户不经过method直接修改attribute的值,可能会造成attribute的状态被破坏(没有经过method中的相关校验直接将attribute改为错误值)
|
||
- 用户可以在`避免命名冲突`的前提下向data attributes添加自己的值
|
||
|
||
在python中,对象中method的第一个参数名为`self`,这只是一个约定,`self`在python中并没有特别的意义。
|
||
|
||
#### function object
|
||
python中,function object为一个class attribute,用于定义method。function object并不一定要定义在class内部,示例如下:
|
||
```py
|
||
# Function defined outside the class
|
||
def f1(self, x, y):
|
||
return min(x, x+y)
|
||
|
||
class C:
|
||
f = f1
|
||
|
||
def g(self):
|
||
return 'hello world'
|
||
|
||
h = g
|
||
```
|
||
在上述示例中,类`C`包含`f, g, h`三个attributes,C实例中也包含`f, g, h`三个method。其中,`h`和`g`完全等价。
|
||
|
||
在python的class定义中,method可以通过`self.xxx`调用其他method,示例如下:
|
||
```py
|
||
class Bag:
|
||
def __init__(self):
|
||
self.data = []
|
||
|
||
def add(self, x):
|
||
self.data.append(x)
|
||
|
||
def addtwice(self, x):
|
||
self.add(x)
|
||
self.add(x)
|
||
```
|
||
method中同样可以访问global names,method关联的global scope是method定义所位于的module。
|
||
|
||
> 在python中,每个值都是一个对象,其class信息存储在`object.__class__`中。
|
||
|
||
### Inheritance
|
||
在python中,支持类的继承,示例如下:
|
||
```py
|
||
class DerivedClassName(BaseClassName):
|
||
<statement-1>
|
||
.
|
||
.
|
||
.
|
||
<statement-N>
|
||
```
|
||
|
||
#### attribute resolving
|
||
若派生类B继承了基类A,那么在对B的对象进行attribute解析时,其首先会查找派生类,如果在派生类中没有找到,则会继续在基类中查找。
|
||
|
||
#### method resolving
|
||
method解析也会从派生类开始,沿着基类逐渐查询,直到找到对应的function object。
|
||
|
||
在python中,派生类的方法会覆盖基类的方法,用c++中的概念来理解,即python中所有的method都是virtual的。
|
||
|
||
在python中,如果派生类方法中想要调用基类的方法,可以通过`BaseClassName.methodname(self, arguments)`,其类似于其他语言中的`super`。
|
||
|
||
#### built-in functions
|
||
python中包含如下built-in function来判断继承关系:
|
||
- `isinstance`: 该方法通常用于检查是否实例属于某一个类
|
||
- `isinstance(obj, int)`
|
||
- `issubclass`: 该方法通常用于检查是否一个类派生自另一个类
|
||
- `issubclass(bool, int)`: 该调用返回为True,bool是int的子类
|
||
|
||
### private variables
|
||
python中并不存在只能从类内部访问的`private instance variable`。但是,python编码中存在一个规范约束:
|
||
- 以`_`开头的名称应当被作为api中的非公开部分(下划线开头的可以是function, method, data member)
|
||
|
||
#### name mangling
|
||
在类定义中,任何以`__spam`定义的name(至少两个前缀下划线,最多一个后缀下划线),都会被替换为`_classname__spam`的形式。
|
||
|
||
name magling在不破坏列内部调用的场景下十分有用,示例如下:
|
||
```py
|
||
class Mapping:
|
||
def __init__(self, iterable):
|
||
self.items_list = []
|
||
self.__update(iterable)
|
||
|
||
def update(self, iterable):
|
||
for item in iterable:
|
||
self.items_list.append(item)
|
||
|
||
__update = update # private copy of original update() method
|
||
|
||
class MappingSubclass(Mapping):
|
||
|
||
def update(self, keys, values):
|
||
# provides new signature for update()
|
||
# but does not break __init__()
|
||
for item in zip(keys, values):
|
||
self.items_list.append(item)
|
||
```
|
||
在上述代码中,即使在`MappingSubclass`中引入`__update`,其名称也为`_MappingSubclass__update`,和`_Mapping__update`不同,仍然不会对父类造成影响。
|
||
|
||
在类中以`__spam`定义的变量,在类外部可以以`_classname__spam`进行访问。
|
||
|
||
### odds and ends
|
||
有时想使用类似C语言中的struct,惯用方法是使用`dataclasses`,示例如下所示:
|
||
```py
|
||
from dataclasses import dataclass
|
||
|
||
@dataclass
|
||
class Employee:
|
||
name: str
|
||
dept: str
|
||
salary: int
|
||
```
|
||
```py
|
||
>>> john = Employee('john', 'computer lab', 1000)
|
||
>>> john.dept
|
||
'computer lab'
|
||
>>> john.salary
|
||
1000
|
||
```
|
||
|
||
### `__self__, __func__`
|
||
instance method object拥有如下属性:
|
||
- `__self__`: `m.__self__`代表method m关联的实例对象
|
||
- `__func__`: `m.__func__`代表method关联的function object
|
||
|
||
### iterators
|
||
在python中,大多数容器对象都可以通过for-statements进行迭代:
|
||
```py
|
||
for element in [1, 2, 3]:
|
||
print(element)
|
||
for element in (1, 2, 3):
|
||
print(element)
|
||
for key in {'one':1, 'two':2}:
|
||
print(key)
|
||
for char in "123":
|
||
print(char)
|
||
for line in open("myfile.txt"):
|
||
print(line, end='')
|
||
```
|
||
在底层,for-statement针对容器对象调用了`iter()`方法,该方法会返回一个`iterator`对象,iterator对象中定义了`__next__()`方法,该方法在终止时会抛出`StopIteration`异常。
|
||
|
||
可以通过内置的`next()`方法来调用`__next__()`,示例如下所示:
|
||
```py
|
||
>>> s = 'abc'
|
||
>>> it = iter(s)
|
||
>>> it
|
||
<str_iterator object at 0x10c90e650>
|
||
>>> next(it)
|
||
'a'
|
||
>>> next(it)
|
||
'b'
|
||
>>> next(it)
|
||
'c'
|
||
>>> next(it)
|
||
Traceback (most recent call last):
|
||
File "<stdin>", line 1, in <module>
|
||
next(it)
|
||
StopIteration
|
||
```
|
||
如果想要为自定义类添加iterator,可以在类中定义一个`__iter__()`方法,该方法返回一个带`__next__()`方法的对象。`如果一个class中包含了__next__()方法的定义,那么__iter__()方法仅需返回self即可`。
|
||
|
||
```py
|
||
class Reverse:
|
||
"""Iterator for looping over a sequence backwards."""
|
||
def __init__(self, data):
|
||
self.data = data
|
||
self.index = len(data)
|
||
|
||
def __iter__(self):
|
||
return self
|
||
|
||
def __next__(self):
|
||
if self.index == 0:
|
||
raise StopIteration
|
||
self.index = self.index - 1
|
||
return self.data[self.index]
|
||
```
|
||
|
||
### generators
|
||
`Generators`可以简单的创建iterator,其编写和函数类似,但是当想要返回值时,使用`yield statement`。
|
||
|
||
每次调用`next()`时,generator都会从上次中止的地方进行恢复。示例如下所示:
|
||
```py
|
||
def reverse(data):
|
||
for index in range(len(data)-1, -1, -1):
|
||
yield data[index]
|
||
```
|
||
```py
|
||
>>> for char in reverse('golf'):
|
||
print(char)
|
||
|
||
f
|
||
l
|
||
o
|
||
g
|
||
```
|
||
通过generator完成的任何事情都可以通过iterator来完成,但是generator的代码更加紧凑,其`__iter__`和`__next__`方法都是自动生成的。
|
||
|
||
在generator的多次next调用之间,local variables的状态和执行状态也是自动保存的。
|
||
|
||
generator令iterator的编写更加简单。
|
||
|
||
### generator expression
|
||
一些简单的迭代器可以简化为表达式:
|
||
```py
|
||
>>> sum(i*i for i in range(10)) # sum of squares
|
||
285
|
||
|
||
>>> xvec = [10, 20, 30]
|
||
>>> yvec = [7, 5, 3]
|
||
>>> sum(x*y for x,y in zip(xvec, yvec)) # dot product
|
||
260
|
||
|
||
>>> unique_words = set(word for line in page >>> for word in line.split())
|
||
|
||
>>> valedictorian = max((student.gpa, student.name) for student in graduates)
|
||
|
||
>>> data = 'golf'
|
||
>>> list(data[i] for i in range(len(data)-1, -1, -1))
|
||
['f', 'l', 'o', 'g']
|
||
```
|