更新了Python语言进阶部分的内容
parent
b773c05370
commit
3351f019ed
|
@ -1,6 +1,112 @@
|
|||
## Python语言进阶
|
||||
|
||||
1. 数据结构和算法
|
||||
### 重要知识点
|
||||
|
||||
- 生成式(推导式)的用法
|
||||
|
||||
```Python
|
||||
prices = {
|
||||
'AAPL': 191.88,
|
||||
'GOOG': 1186.96,
|
||||
'IBM': 149.24,
|
||||
'ORCL': 48.44,
|
||||
'ACN': 166.89,
|
||||
'FB': 208.09,
|
||||
'SYMC': 21.29
|
||||
}
|
||||
# 用股票价格大于100元的股票构造一个新的字典
|
||||
prices2 = {key: value for key, value in prices.items() if value > 100}
|
||||
print(prices2)
|
||||
```
|
||||
|
||||
> 说明:生成式(推导式)可以用来生成列表、集合和字典。
|
||||
|
||||
- 嵌套的列表的坑
|
||||
|
||||
```Python
|
||||
names = ['关羽', '张飞', '赵云', '马超', '黄忠']
|
||||
courses = ['语文', '数学', '英语']
|
||||
# 录入五个学生三门课程的成绩
|
||||
# 错误 - 参考http://pythontutor.com/visualize.html#mode=edit
|
||||
# scores = [[None] * len(courses)] * len(names)
|
||||
scores = [[None] * len(courses) for _ in range(len(names))]
|
||||
for row, name in enumerate(names):
|
||||
for col, course in enumerate(courses):
|
||||
scores[row][col] = float(input(f'请输入{name}的{course}成绩: '))
|
||||
print(scores)
|
||||
```
|
||||
|
||||
[Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP
|
||||
|
||||
- `heapq`模块(堆排序)
|
||||
|
||||
```Python
|
||||
"""
|
||||
从列表中找出最大的或最小的N个元素
|
||||
堆结构(大根堆/小根堆)
|
||||
"""
|
||||
import heapq
|
||||
|
||||
list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
|
||||
list2 = [
|
||||
{'name': 'IBM', 'shares': 100, 'price': 91.1},
|
||||
{'name': 'AAPL', 'shares': 50, 'price': 543.22},
|
||||
{'name': 'FB', 'shares': 200, 'price': 21.09},
|
||||
{'name': 'HPQ', 'shares': 35, 'price': 31.75},
|
||||
{'name': 'YHOO', 'shares': 45, 'price': 16.35},
|
||||
{'name': 'ACME', 'shares': 75, 'price': 115.65}
|
||||
]
|
||||
print(heapq.nlargest(3, list1))
|
||||
print(heapq.nsmallest(3, list1))
|
||||
print(heapq.nlargest(2, list2, key=lambda x: x['price']))
|
||||
print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
|
||||
```
|
||||
|
||||
- `itertools`模块
|
||||
|
||||
```Python
|
||||
"""
|
||||
迭代工具模块
|
||||
"""
|
||||
import itertools
|
||||
|
||||
# 产生ABCD的全排列
|
||||
itertools.permutations('ABCD')
|
||||
# 产生ABCDE的五选三组合
|
||||
itertools.combinations('ABCDE', 3)
|
||||
# 产生ABCD和123的笛卡尔积
|
||||
itertools.product('ABCD', '123')
|
||||
# 产生ABC的无限循环序列
|
||||
itertools.cycle(('A', 'B', 'C'))
|
||||
```
|
||||
|
||||
- `collections`模块
|
||||
|
||||
常用的工具类:
|
||||
|
||||
- `namedtuple`:命令元组,它是一个类工厂,接受类型的名称和属性列表来创建一个类。
|
||||
- `deque`:双端队列,是列表的替代实现。Python中的列表底层是基于数组来实现的,而deque底层是双向链表,因此当你需要在头尾添加和删除元素是,deque会表现出更好的性能,渐近时间复杂度为$O(1)$。
|
||||
- `Counter`:`dict`的子类,键是元素,值是元素的计数,它的`most_common()`方法可以帮助我们获取出现频率最高的元素。`Counter`和`dict`的继承关系我认为是值得商榷的,按照CARP原则,`Counter`跟`dict`的关系应该设计为关联关系更为合理。
|
||||
- `OrderedDict`:`dict`的子类,它记录了键值对插入的顺序,看起来既有字典的行为,也有链表的行为。
|
||||
- `defaultdict`:类似于字典类型,但是可以通过默认的工厂函数来获得键对应的默认值,相比字典中的`setdefault()`方法,这种做法更加高效。
|
||||
|
||||
```Python
|
||||
"""
|
||||
找出序列中出现次数最多的元素
|
||||
"""
|
||||
from collections import Counter
|
||||
|
||||
words = [
|
||||
'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
|
||||
'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
|
||||
'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes',
|
||||
'look', 'into', 'my', 'eyes', "you're", 'under'
|
||||
]
|
||||
counter = Counter(words)
|
||||
print(counter.most_common(3))
|
||||
```
|
||||
|
||||
### 数据结构和算法
|
||||
|
||||
- 算法:解决问题的方法和步骤
|
||||
|
||||
|
@ -9,12 +115,12 @@
|
|||
- 渐近时间复杂度的大O标记:
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(c)" /> - 常量时间复杂度 - 布隆过滤器 / 哈希存储
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(log_2n)" /> - 对数时间复杂度 - 折半查找(二分查找)
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n)" /> - 线性时间复杂度 - 顺序查找 / 桶排序
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n)" /> - 线性时间复杂度 - 顺序查找 / 计数排序
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n*log_2n)" /> - 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序)
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n^2)" /> - 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序)
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n^3)" /> - 立方时间复杂度 - Floyd算法 / 矩阵乘法运算
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(2^n)" /> - 几何级数时间复杂度 - 汉诺塔
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n!)" /> - 阶乘时间复杂度 - 旅行经销商问题 - NP
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n!)" /> - 阶乘时间复杂度 - 旅行经销商问题 - NPC
|
||||
|
||||
![](./res/algorithm_complexity_1.png)
|
||||
|
||||
|
@ -23,9 +129,9 @@
|
|||
- 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)
|
||||
|
||||
```Python
|
||||
def select_sort(origin_items, comp=lambda x, y: x < y):
|
||||
def select_sort(items, comp=lambda x, y: x < y):
|
||||
"""简单选择排序"""
|
||||
items = origin_items[:]
|
||||
items = items[:]
|
||||
for i in range(len(items) - 1):
|
||||
min_index = i
|
||||
for j in range(i + 1, len(items)):
|
||||
|
@ -36,9 +142,24 @@
|
|||
```
|
||||
|
||||
```Python
|
||||
def bubble_sort(origin_items, comp=lambda x, y: x > y):
|
||||
"""高质量冒泡排序(搅拌排序)"""
|
||||
items = origin_items[:]
|
||||
def bubble_sort(items, comp=lambda x, y: x > y):
|
||||
"""冒泡排序"""
|
||||
items = items[:]
|
||||
for i in range(len(items) - 1):
|
||||
swapped = False
|
||||
for j in range(i, len(items) - 1 - i):
|
||||
if comp(items[j], items[j + 1]):
|
||||
items[j], items[j + 1] = items[j + 1], items[j]
|
||||
swapped = True
|
||||
if not swapped:
|
||||
break
|
||||
return items
|
||||
```
|
||||
|
||||
```Python
|
||||
def bubble_sort(items, comp=lambda x, y: x > y):
|
||||
"""搅拌排序(冒泡排序升级版)"""
|
||||
items = items[:]
|
||||
for i in range(len(items) - 1):
|
||||
swapped = False
|
||||
for j in range(i, len(items) - 1 - i):
|
||||
|
@ -57,17 +178,7 @@
|
|||
```
|
||||
|
||||
```Python
|
||||
def merge_sort(items, comp=lambda x, y: x <= y):
|
||||
"""归并排序(分治法)"""
|
||||
if len(items) < 2:
|
||||
return items[:]
|
||||
mid = len(items) // 2
|
||||
left = merge_sort(items[:mid], comp)
|
||||
right = merge_sort(items[mid:], comp)
|
||||
return merge(left, right, comp)
|
||||
|
||||
|
||||
def merge(items1, items2, comp):
|
||||
def merge(items1, items2, comp=lambda x, y: x < y):
|
||||
"""合并(将两个有序的列表合并成一个有序的列表)"""
|
||||
items = []
|
||||
index1, index2 = 0, 0
|
||||
|
@ -81,6 +192,20 @@
|
|||
items += items1[index1:]
|
||||
items += items2[index2:]
|
||||
return items
|
||||
|
||||
|
||||
def merge_sort(items, comp=lambda x, y: x < y):
|
||||
return _merge_sort(list(items), comp)
|
||||
|
||||
|
||||
def _merge_sort(items, comp):
|
||||
"""归并排序"""
|
||||
if len(items) < 2:
|
||||
return items
|
||||
mid = len(items) // 2
|
||||
left = _merge_sort(items[:mid], comp)
|
||||
right = _merge_sort(items[mid:], comp)
|
||||
return merge(left, right, comp)
|
||||
```
|
||||
|
||||
```Python
|
||||
|
@ -107,94 +232,6 @@
|
|||
return -1
|
||||
```
|
||||
|
||||
- 使用生成式(推导式)语法
|
||||
|
||||
```Python
|
||||
prices = {
|
||||
'AAPL': 191.88,
|
||||
'GOOG': 1186.96,
|
||||
'IBM': 149.24,
|
||||
'ORCL': 48.44,
|
||||
'ACN': 166.89,
|
||||
'FB': 208.09,
|
||||
'SYMC': 21.29
|
||||
}
|
||||
# 用股票价格大于100元的股票构造一个新的字典
|
||||
prices2 = {key: value for key, value in prices.items() if value > 100}
|
||||
print(prices2)
|
||||
```
|
||||
|
||||
> 说明:生成式(推导式)可以用来生成列表、集合和字典。
|
||||
|
||||
- 嵌套的列表
|
||||
|
||||
```Python
|
||||
names = ['关羽', '张飞', '赵云', '马超', '黄忠']
|
||||
courses = ['语文', '数学', '英语']
|
||||
# 录入五个学生三门课程的成绩
|
||||
# 错误 - 参考http://pythontutor.com/visualize.html#mode=edit
|
||||
# scores = [[None] * len(courses)] * len(names)
|
||||
scores = [[None] * len(courses) for _ in range(len(names))]
|
||||
for row, name in enumerate(names):
|
||||
for col, course in enumerate(courses):
|
||||
scores[row][col] = float(input(f'请输入{name}的{course}成绩: '))
|
||||
print(scores)
|
||||
```
|
||||
|
||||
[Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP
|
||||
|
||||
- heapq、itertools等的用法
|
||||
```Python
|
||||
"""
|
||||
从列表中找出最大的或最小的N个元素
|
||||
堆结构(大根堆/小根堆)
|
||||
"""
|
||||
import heapq
|
||||
|
||||
list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
|
||||
list2 = [
|
||||
{'name': 'IBM', 'shares': 100, 'price': 91.1},
|
||||
{'name': 'AAPL', 'shares': 50, 'price': 543.22},
|
||||
{'name': 'FB', 'shares': 200, 'price': 21.09},
|
||||
{'name': 'HPQ', 'shares': 35, 'price': 31.75},
|
||||
{'name': 'YHOO', 'shares': 45, 'price': 16.35},
|
||||
{'name': 'ACME', 'shares': 75, 'price': 115.65}
|
||||
]
|
||||
print(heapq.nlargest(3, list1))
|
||||
print(heapq.nsmallest(3, list1))
|
||||
print(heapq.nlargest(2, list2, key=lambda x: x['price']))
|
||||
print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
|
||||
```
|
||||
|
||||
```Python
|
||||
"""
|
||||
迭代工具 - 排列 / 组合 / 笛卡尔积
|
||||
"""
|
||||
import itertools
|
||||
|
||||
itertools.permutations('ABCD')
|
||||
itertools.combinations('ABCDE', 3)
|
||||
itertools.product('ABCD', '123')
|
||||
```
|
||||
|
||||
- collections模块下的工具类
|
||||
|
||||
```Python
|
||||
"""
|
||||
找出序列中出现次数最多的元素
|
||||
"""
|
||||
from collections import Counter
|
||||
|
||||
words = [
|
||||
'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
|
||||
'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
|
||||
'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes',
|
||||
'look', 'into', 'my', 'eyes', "you're", 'under'
|
||||
]
|
||||
counter = Counter(words)
|
||||
print(counter.most_common(3))
|
||||
```
|
||||
|
||||
- 常用算法:
|
||||
|
||||
- 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。
|
||||
|
@ -305,8 +342,8 @@
|
|||
"""
|
||||
快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大
|
||||
"""
|
||||
def quick_sort(origin_items, comp=lambda x, y: x <= y):
|
||||
items = origin_items[:]
|
||||
def quick_sort(items, comp=lambda x, y: x <= y):
|
||||
items = list(items)[:]
|
||||
_quick_sort(items, 0, len(items) - 1, comp)
|
||||
return items
|
||||
|
||||
|
@ -379,25 +416,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
动态规划例子1:[斐波拉切数列]()。(不使用动态规划将会是几何级数复杂度)
|
||||
|
||||
```Python
|
||||
"""
|
||||
动态规划 - 适用于有重叠子问题和最优子结构性质的问题
|
||||
使用动态规划方法所耗时间往往远少于朴素解法(用空间换取时间)
|
||||
"""
|
||||
def fib(num, temp={}):
|
||||
"""用递归计算Fibonacci数"""
|
||||
if num in (1, 2):
|
||||
return 1
|
||||
try:
|
||||
return temp[num]
|
||||
except KeyError:
|
||||
temp[num] = fib(num - 1) + fib(num - 2)
|
||||
return temp[num]
|
||||
```
|
||||
|
||||
动态规划例子2:子列表元素之和的最大值。(使用动态规划可以避免二重循环)
|
||||
动态规划例子:子列表元素之和的最大值。
|
||||
|
||||
> 说明:子列表指的是列表中索引(下标)连续的元素构成的列表;列表中的元素是int类型,可能包含正整数、0、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如:
|
||||
>
|
||||
|
@ -416,20 +435,20 @@
|
|||
```Python
|
||||
def main():
|
||||
items = list(map(int, input().split()))
|
||||
size = len(items)
|
||||
overall, partial = {}, {}
|
||||
overall[size - 1] = partial[size - 1] = items[size - 1]
|
||||
for i in range(size - 2, -1, -1):
|
||||
partial[i] = max(items[i], partial[i + 1] + items[i])
|
||||
overall[i] = max(partial[i], overall[i + 1])
|
||||
print(overall[0])
|
||||
overall = partial = items[0]
|
||||
for i in range(1, len(items)):
|
||||
partial = max(items[i], partial + items[i])
|
||||
overall = max(partial, overall)
|
||||
print(overall)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
2. 函数的使用方式
|
||||
> **说明**:这个题目最容易想到的解法是使用二重循环,但是代码的时间性能将会变得非常的糟糕。使用动态规划的思想,仅仅是多用了两个变量,就将原来$O(N^2)$复杂度的问题变成了$O(N)$。
|
||||
|
||||
### 函数的使用方式
|
||||
|
||||
- 将函数视为“一等公民”
|
||||
|
||||
|
@ -452,7 +471,7 @@
|
|||
|
||||
- 闭包和作用域问题
|
||||
|
||||
- Python搜索变量的LEGB顺序(Local --> Embedded --> Global --> Built-in)
|
||||
- Python搜索变量的LEGB顺序(Local >>> Embedded >>> Global >>> Built-in)
|
||||
|
||||
- `global`和`nonlocal`关键字的作用
|
||||
|
||||
|
@ -478,7 +497,7 @@
|
|||
return wrapper
|
||||
```
|
||||
|
||||
如果装饰器不希望跟`print`函数耦合,可以编写带参数的装饰器。
|
||||
如果装饰器不希望跟`print`函数耦合,可以编写可以参数化的装饰器。
|
||||
|
||||
```Python
|
||||
from functools import wraps
|
||||
|
@ -486,7 +505,7 @@
|
|||
|
||||
|
||||
def record(output):
|
||||
"""自定义带参数的装饰器"""
|
||||
"""可以参数化的装饰器"""
|
||||
|
||||
def decorate(func):
|
||||
|
||||
|
@ -508,7 +527,7 @@
|
|||
|
||||
|
||||
class Record():
|
||||
"""自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)"""
|
||||
"""通过定义类的方式定义装饰器"""
|
||||
|
||||
def __init__(self, output):
|
||||
self.output = output
|
||||
|
@ -525,7 +544,7 @@
|
|||
return wrapper
|
||||
```
|
||||
|
||||
> 说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。
|
||||
> **说明**:由于对带装饰功能的函数添加了@wraps装饰器,可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。
|
||||
|
||||
例子:用装饰器来实现单例模式。
|
||||
|
||||
|
@ -552,17 +571,19 @@
|
|||
pass
|
||||
```
|
||||
|
||||
> 说明:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?
|
||||
> **提示**:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?
|
||||
|
||||
线程安全的单例装饰器。
|
||||
|
||||
```Python
|
||||
from functools import wraps
|
||||
from threading import Lock
|
||||
from threading import RLock
|
||||
|
||||
|
||||
def singleton(cls):
|
||||
"""线程安全的单例装饰器"""
|
||||
instances = {}
|
||||
locker = Lock()
|
||||
locker = RLock()
|
||||
|
||||
@wraps(cls)
|
||||
def wrapper(*args, **kwargs):
|
||||
|
@ -575,7 +596,9 @@
|
|||
return wrapper
|
||||
```
|
||||
|
||||
3. 面向对象相关知识
|
||||
> **提示**:上面的代码用到了`with`上下文语法来进行锁操作,因为锁对象本身就是上下文管理器对象(支持`__enter__`和`__exit__`魔术方法)。在`wrapper`函数中,我们先做了一次不带锁的检查,然后再做带锁的检查,这样做比直接加锁检查性能要更好,如果对象已经创建就没有必须再去加锁而是直接返回该对象就可以了。
|
||||
|
||||
### 面向对象相关知识
|
||||
|
||||
- 三大支柱:封装、继承、多态
|
||||
|
||||
|
@ -635,15 +658,9 @@
|
|||
@staticmethod
|
||||
def create(emp_type, *args, **kwargs):
|
||||
"""创建员工"""
|
||||
emp_type = emp_type.upper()
|
||||
emp = None
|
||||
if emp_type == 'M':
|
||||
emp = Manager(*args, **kwargs)
|
||||
elif emp_type == 'P':
|
||||
emp = Programmer(*args, **kwargs)
|
||||
elif emp_type == 'S':
|
||||
emp = Salesman(*args, **kwargs)
|
||||
return emp
|
||||
all_emp_types = {'M': Manager, 'P': Programmer, 'S': Salesman}
|
||||
cls = all_emp_types[emp_type.upper()]
|
||||
return cls(*args, **kwargs) if cls else None
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -655,7 +672,7 @@
|
|||
EmployeeFactory.create('S', '典韦', 123000),
|
||||
]
|
||||
for emp in emps:
|
||||
print('%s: %.2f元' % (emp.name, emp.get_salary()))
|
||||
print(f'{emp.name}: {emp.get_salary():.2f}元')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -699,13 +716,10 @@
|
|||
|
||||
def show(self):
|
||||
"""显示牌面"""
|
||||
suites = ['♠️', '♥️', '♣️', '♦️']
|
||||
suites = ['♠︎', '♥︎', '♣︎', '♦︎']
|
||||
faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
|
||||
return f'{suites[self.suite.value]}{faces[self.face]}'
|
||||
|
||||
def __str__(self):
|
||||
return self.show()
|
||||
|
||||
def __repr__(self):
|
||||
return self.show()
|
||||
|
||||
|
@ -769,7 +783,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
> 说明:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。
|
||||
> **说明**:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。
|
||||
|
||||
- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)
|
||||
|
||||
|
@ -826,10 +840,10 @@
|
|||
以下情况会导致垃圾回收:
|
||||
|
||||
- 调用`gc.collect()`
|
||||
- gc模块的计数器达到阀值
|
||||
- `gc`模块的计数器达到阀值
|
||||
- 程序退出
|
||||
|
||||
如果循环引用中两个对象都定义了`__del__`方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。
|
||||
如果循环引用中两个对象都定义了`__del__`方法,`gc`模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。
|
||||
|
||||
也可以通过`weakref`模块构造弱引用的方式来解决循环引用的问题。
|
||||
|
||||
|
@ -838,8 +852,8 @@
|
|||
有几个小问题请大家思考:
|
||||
|
||||
- 自定义的对象能不能使用运算符做运算?
|
||||
- 自定义的对象能不能放到set中?能去重吗?
|
||||
- 自定义的对象能不能作为dict的键?
|
||||
- 自定义的对象能不能放到`set`中?能去重吗?
|
||||
- 自定义的对象能不能作为`dict`的键?
|
||||
- 自定义的对象能不能使用上下文语法?
|
||||
|
||||
- 混入(Mixin)
|
||||
|
@ -873,6 +887,8 @@
|
|||
|
||||
- 元编程和元类
|
||||
|
||||
对象是通过类创建的,类是通过元类创建的,元类提供了创建类的元信息。所有的类都直接或间接的继承自`object`,所有的元类都直接或间接的继承自`type`。
|
||||
|
||||
例子:用元类实现单例模式。
|
||||
|
||||
```Python
|
||||
|
@ -884,7 +900,7 @@
|
|||
|
||||
def __init__(cls, *args, **kwargs):
|
||||
cls.__instance = None
|
||||
cls.__lock = threading.Lock()
|
||||
cls.__lock = threading.RLock()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
|
@ -911,7 +927,7 @@
|
|||
- 合成聚合复用原则(CARP) - 优先使用强关联关系而不是继承关系复用代码
|
||||
- 最少知识原则(迪米特法则,Lo**D**)- 不要给没有必然联系的对象发消息
|
||||
|
||||
> 说明:上面加粗的字母放在一起称为面向对象的**SOLID**原则。
|
||||
> **说明**:上面加粗的字母放在一起称为面向对象的**SOLID**原则。
|
||||
|
||||
- GoF设计模式
|
||||
|
||||
|
@ -919,11 +935,11 @@
|
|||
- 结构型模式:适配器、门面(外观)、代理
|
||||
- 行为型模式:迭代器、观察者、状态、策略
|
||||
|
||||
例子:可插拔的哈希算法。
|
||||
例子:可插拔的哈希算法(策略模式)。
|
||||
|
||||
```Python
|
||||
class StreamHasher():
|
||||
"""哈希摘要生成器(策略模式)"""
|
||||
"""哈希摘要生成器"""
|
||||
|
||||
def __init__(self, alg='md5', size=4096):
|
||||
self.size = size
|
||||
|
@ -942,10 +958,10 @@
|
|||
def main():
|
||||
"""主函数"""
|
||||
hasher1 = StreamHasher()
|
||||
with open('Python-3.7.1.tgz', 'rb') as stream:
|
||||
with open('Python-3.7.6.tgz', 'rb') as stream:
|
||||
print(hasher1.to_digest(stream))
|
||||
hasher2 = StreamHasher('sha1')
|
||||
with open('Python-3.7.1.tgz', 'rb') as stream:
|
||||
with open('Python-3.7.6.tgz', 'rb') as stream:
|
||||
print(hasher2(stream))
|
||||
|
||||
|
||||
|
@ -953,21 +969,15 @@
|
|||
main()
|
||||
```
|
||||
|
||||
4. 迭代器和生成器
|
||||
### 迭代器和生成器
|
||||
|
||||
- 和迭代器相关的魔术方法(`__iter__`和`__next__`)
|
||||
- 迭代器是实现了迭代器协议的对象。
|
||||
|
||||
- 两种创建生成器的方式(生成器表达式和`yield`关键字)
|
||||
- Python中没有像`protocol`或`interface`这样的定义协议的关键字。
|
||||
- Python中用魔术方法表示协议。
|
||||
- `__iter__`和`__next__`魔术方法就是迭代器协议。
|
||||
|
||||
```Python
|
||||
def fib(num):
|
||||
"""生成器"""
|
||||
a, b = 0, 1
|
||||
for _ in range(num):
|
||||
a, b = b, a + b
|
||||
yield a
|
||||
|
||||
|
||||
class Fib(object):
|
||||
"""迭代器"""
|
||||
|
||||
|
@ -987,11 +997,44 @@
|
|||
raise StopIteration()
|
||||
```
|
||||
|
||||
5. 并发编程
|
||||
- 生成器是语法简化版的迭代器。
|
||||
|
||||
```Python
|
||||
def fib(num):
|
||||
"""生成器"""
|
||||
a, b = 0, 1
|
||||
for _ in range(num):
|
||||
a, b = b, a + b
|
||||
yield a
|
||||
```
|
||||
|
||||
- 生成器进化为协程。
|
||||
|
||||
生成器对象可以使用`send()`方法发送数据,发送的数据会成为生成器函数中通过`yield`表达式获得的值。这样,生成器就可以作为协程使用,协程简单的说就是可以相互协作的子程序。
|
||||
|
||||
```Python
|
||||
def calc_avg():
|
||||
"""流式计算平均值"""
|
||||
total, counter = 0, 0
|
||||
avg_value = None
|
||||
while True:
|
||||
value = yield avg_value
|
||||
total, counter = total + value, counter + 1
|
||||
avg_value = total / counter
|
||||
|
||||
|
||||
gen = calc_avg()
|
||||
next(gen)
|
||||
print(gen.send(10))
|
||||
print(gen.send(20))
|
||||
print(gen.send(30))
|
||||
```
|
||||
|
||||
### 并发编程
|
||||
|
||||
Python中实现并发编程的三种方案:多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验;坏处在于并发的程序不容易开发和调试,同时对其他程序来说它并不友好。
|
||||
|
||||
- 多线程:Python中提供了Thread类并辅以Lock、Condition、Event、Semaphore和Barrier。Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。
|
||||
- 多线程:Python中提供了`Thread`类并辅以`Lock`、`Condition`、`Event`、`Semaphore`和`Barrier`。Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1038,7 +1081,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
多个线程竞争资源的情况
|
||||
多个线程竞争资源的情况。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1108,7 +1151,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用`threading`模块的Condition来实现线程调度,该对象也是基于锁来创建的,代码如下所示:
|
||||
修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用`threading`模块的`Condition`来实现线程调度,该对象也是基于锁来创建的,代码如下所示:
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1179,7 +1222,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
- 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是Process,其他辅助的类跟threading模块中的类似,进程间共享数据可以使用管道、套接字等,在multiprocessing模块中有一个Queue类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。
|
||||
- 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是`Process`,其他辅助的类跟`threading`模块中的类似,进程间共享数据可以使用管道、套接字等,在`multiprocessing`模块中有一个`Queue`类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1236,7 +1279,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
> 说明:**多线程和多进程的比较**。
|
||||
> **重点**:**多线程和多进程的比较**。
|
||||
>
|
||||
> 以下情况需要使用多线程:
|
||||
>
|
||||
|
@ -1304,9 +1347,9 @@
|
|||
main()
|
||||
```
|
||||
|
||||
> 说明:上面的代码使用`get_event_loop`函数获得系统默认的事件循环,通过`gather`函数可以获得一个`future`对象,`future`对象的`add_done_callback`可以添加执行完成时的回调函数,`loop`对象的`run_until_complete`方法可以等待通过`future`对象获得协程执行结果。
|
||||
> **说明**:上面的代码使用`get_event_loop`函数获得系统默认的事件循环,通过`gather`函数可以获得一个`future`对象,`future`对象的`add_done_callback`可以添加执行完成时的回调函数,`loop`对象的`run_until_complete`方法可以等待通过`future`对象获得协程执行结果。
|
||||
|
||||
Python中有一个名为`aiohttp`的三方库,它提供了异步的HTTP客户端和服务器,这个三方库可以跟`asyncio`模块一起工作,并提供了对`Future`对象的支持。Python 3.6中引入了async和await来定义异步执行的函数以及创建异步上下文,在Python 3.7中它们正式成为了关键字。下面的代码异步的从5个URL中获取页面并通过正则表达式的命名捕获组提取了网站的标题。
|
||||
Python中有一个名为`aiohttp`的三方库,它提供了异步的HTTP客户端和服务器,这个三方库可以跟`asyncio`模块一起工作,并提供了对`Future`对象的支持。Python 3.6中引入了`async`和`await`来定义异步执行的函数以及创建异步上下文,在Python 3.7中它们正式成为了关键字。下面的代码异步的从5个URL中获取页面并通过正则表达式的命名捕获组提取了网站的标题。
|
||||
|
||||
```Python
|
||||
import asyncio
|
||||
|
@ -1335,8 +1378,8 @@
|
|||
'https://www.taobao.com/',
|
||||
'https://www.douban.com/')
|
||||
loop = asyncio.get_event_loop()
|
||||
tasks = [show_title(url) for url in urls]
|
||||
loop.run_until_complete(asyncio.wait(tasks))
|
||||
cos = [show_title(url) for url in urls]
|
||||
loop.run_until_complete(asyncio.wait(cos))
|
||||
loop.close()
|
||||
|
||||
|
||||
|
@ -1344,10 +1387,10 @@
|
|||
main()
|
||||
```
|
||||
|
||||
> 说明:**异步I/O与多进程的比较**。
|
||||
> **重点**:**异步I/O与多进程的比较**。
|
||||
>
|
||||
> 当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,asyncio就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑asyncio,它很适合编写没有实时数据处理需求的Web应用服务器。
|
||||
> 当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,`asyncio`就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑`asyncio`,它很适合编写没有实时数据处理需求的Web应用服务器。
|
||||
|
||||
Python还有很多用于处理并行任务的三方库,例如:joblib、PyMP等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。
|
||||
Python还有很多用于处理并行任务的三方库,例如:`joblib`、`PyMP`等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。
|
||||
|
||||
要实现任务的异步化,可以使用名为Celery的三方库。Celery是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。
|
||||
要实现任务的异步化,可以使用名为`Celery`的三方库。`Celery`是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。
|
Loading…
Reference in New Issue