Merge branch 'jackfrued:master' into master

pull/329/head
Pan ShanShan 2021-10-13 12:42:32 +08:00 committed by GitHub
commit 1ada8a293a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
861 changed files with 57764 additions and 46046 deletions

View File

@ -10,33 +10,31 @@
4. 2000年10月16日Python 2.0发布,增加了完整的[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8)),提供了对[Unicode](https://zh.wikipedia.org/wiki/Unicode)的支持。与此同时Python的整个开发过程更加透明社区对开发进度的影响逐渐扩大生态圈开始慢慢形成。
5. 2008年12月3日Python 3.0发布它并不完全兼容之前的Python代码不过因为目前还有不少公司在项目和运维中使用Python 2.x版本所以Python 3.x的很多新特性后来也被移植到Python 2.6/2.7版本中。
目前我们使用的Python 3.7.x的版本是在2018年发布的Python的版本号分为三段形如A.B.C。其中A表示大版本号一般当整体重写或出现不向后兼容的改变时增加AB表示功能更新出现新功能时增加BC表示小的改动例如修复了某个Bug只要有修改就增加C。如果对Python的历史感兴趣可以阅读名为[《Python简史》](http://www.cnblogs.com/vamei/archive/2013/02/06/2892628.html)的博文
目前我们使用的Python 3.7.x的版本是在2018年发布的Python的版本号分为三段形如A.B.C。其中A表示大版本号一般当整体重写或出现不向后兼容的改变时增加AB表示功能更新出现新功能时增加BC表示小的改动例如修复了某个Bug只要有修改就增加C。如果对Python的历史感兴趣可以阅读名为[《Python简史》](http://www.cnblogs.com/vamei/archive/2013/02/06/2892628.html)的网络文章
#### Python的优缺点
Python的优点很多简单的可以总结为以下几点。
1. 简单和明确,做一件事只有一种方法。
2. 学习曲线低跟其他很多语言相比Python更容易上手。
3. 开放源代码,拥有强大的社区和生态圈。
4. 解释型语言,天生具有平台可移植性。
5. 对两种主流的编程范式(面向对象编程和函数式编程)都提供了支持。
6. 可扩展性和可嵌入性例如在Python中可以调用C/C++代码。
7. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。
1. 简单明了,学习曲线低,比很多编程语言都容易上手。
2. 开放源代码,拥有强大的社区和生态圈,尤其是在数据分析和机器学习领域。
3. 解释型语言,天生具有平台可移植性,代码可以工作于不同的操作系统。
4. 对两种主流的编程范式(面向对象编程和函数式编程)都提供了支持。
5. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。
Python的缺点主要集中在以下几点。
1. 执行效率稍低,因此计算密集型任务可以由C/C++编写。
1. 执行效率稍低,对执行效率要求高的部分可以由其他语言C、C++编写。
2. 代码无法加密,但是现在很多公司都不销售卖软件而是销售服务,这个问题会被弱化。
3. 在开发时可以选择的框架太多如Web框架就有100多个有选择的地方就有错误。
#### Python的应用领域
目前Python在Web应用开发、云基础设施、DevOps、网络数据采集爬虫、数据分析挖掘、机器学习等领域都有着广泛的应用因此也产生了Web后端开发、数据接口开发、自动化运维、自动化测试、科学计算和可视化、数据分析、量化交易、机器人开发、自然语言处理、图像识别等一系列相关的职位
目前Python在Web应用后端开发、云基础设施建设、DevOps、网络数据采集爬虫自动化测试、数据分析、机器学习等领域都有着广泛的应用。
### 安装Python解释器
想要开始Python编程之旅首先得在自己使用的计算机上安装Python解释器环境下面将以安装官方的Python解释器为例讲解如何在不同的操作系统上安装Python环境。官方的Python解释器是用C语言实现的也是使用最为广泛的Python解释器通常称之为CPython。除此之外Python解释器还有Java语言实现的Jython、C#语言实现的IronPython以及PyPy、Brython、Pyston等版本我们暂时不对这些内容进行介绍,有兴趣的读者可以自行了解。
想要开始Python编程之旅首先得在自己使用的计算机上安装Python解释器环境下面将以安装官方的Python解释器为例讲解如何在不同的操作系统上安装Python环境。官方的Python解释器是用C语言实现的也是使用最为广泛的Python解释器通常称之为CPython。除此之外Python解释器还有Java语言实现的Jython、C#语言实现的IronPython以及PyPy、Brython、Pyston等版本有兴趣的读者可以自行了解。
#### Windows环境
@ -57,15 +55,15 @@ yum -y install wget gcc zlib-devel bzip2-devel openssl-devel ncurses-devel sqlit
2. 下载Python源代码并解压缩到指定目录。
```Shell
wget https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tgz
xz -d Python-3.7.3.tar.xz
tar -xvf Python-3.7.3.tar
wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tar.xz
xz -d Python-3.7.6.tar.xz
tar -xvf Python-3.7.6.tar
```
3. 切换至Python源代码目录并执行下面的命令进行配置和安装。
```Shell
cd Python-3.7.3
cd Python-3.7.6
./configure --prefix=/usr/local/python37 --enable-optimizations
make && make install
```
@ -104,13 +102,13 @@ macOS也自带了Python 2.x版本可以通过[Python的官方网站](https://
```Shell
python --version
```
或者是在Linux或macOS系统的终端中键入下面的命令。
在Linux或macOS系统的终端中键入下面的命令。
```Shell
python3 --version
```
当然也可以先输入python或python3进入交互式环境再执行以下的代码检查Python的版本。
当然也可以先输入`python``python3`进入交互式环境再执行以下的代码检查Python的版本。
```Python
import sys
@ -156,12 +154,8 @@ python3 hello.py
Version: 0.1
Author: 骆昊
"""
print('hello, world!')
# print("你好,世界!")
print('你好', '世界')
print('hello', 'world', sep=', ', end='!')
print('goodbye, world', end='!\n')
# print("你好, 世界!")
```
### Python开发工具
@ -174,7 +168,7 @@ IDLE是安装Python环境时自带的集成开发工具如下图所示。但
#### IPython - 更好的交互式编程工具
IPython是一种基于Python的交互式解释器。相较于原生的Python交互式环境IPython提供了更为强大的编辑和交互功能。可以通过Python的包管理工具pip安装IPython和Jupyter,具体的操作如下所示。
IPython是一种基于Python的交互式解释器。相较于原生的Python交互式环境IPython提供了更为强大的编辑和交互功能。可以通过Python的包管理工具pip安装IPython具体的操作如下所示。
```Shell
pip install ipython
@ -220,7 +214,7 @@ pip3 install ipython
- Python PEP8 Autoformat - PEP8规范自动格式化插件。
- ConvertToUTF8 - 将本地编码转换为UTF-8。
> 说明:事实上[Visual Studio Code](<https://code.visualstudio.com/>)可能是更好的选择,它不用花钱并提供了更为完整和强大的功能,有兴趣的读者可以自行研究。
> **说明**:事实上[Visual Studio Code](<https://code.visualstudio.com/>)可能是更好的选择,它不用花钱并提供了更为完整和强大的功能,有兴趣的读者可以自行研究。
#### PyCharm - Python开发神器
@ -236,12 +230,11 @@ PyCharm的安装、配置和使用在[《玩转PyCharm》](../玩转PyCharm.md)
import this
```
> 说明输入上面的代码在Python的交互式环境中可以看到Tim Peter撰写的[“Python之禅”](../Python之禅.md)里面讲述的道理不仅仅适用于Python也适用于其他编程语言。
>
> **说明**输入上面的代码在Python的交互式环境中可以看到Tim Peter撰写的[“Python之禅”](../Python之禅.md)里面讲述的道理不仅仅适用于Python也适用于其他编程语言。
2. 学习使用turtle在屏幕上绘制图形。
> 说明turtle是Python内置的一个非常有趣的模块特别适合对计算机程序设计进行初体验的小伙伴它最早是Logo语言的一部分Logo语言是Wally Feurzig和Seymour Papert在1966发明的编程语言。
> **说明**turtle是Python内置的一个非常有趣的模块特别适合对计算机程序设计进行初体验的小伙伴它最早是Logo语言的一部分Logo语言是Wally Feurzig和Seymour Papert在1966发明的编程语言。
```Python
import turtle
@ -260,4 +253,4 @@ PyCharm的安装、配置和使用在[《玩转PyCharm》](../玩转PyCharm.md)
turtle.mainloop()
```
> 提示:本章提供的代码中还有画国旗和画小猪佩奇的代码,有兴趣的读者请自行研究。
> **提示**:本章提供的代码中还有画国旗和画小猪佩奇的代码,有兴趣的读者请自行研究。

View File

@ -4,17 +4,17 @@
计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说的中央处理器,它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。我们通常所说的程序实际上就是指令的集合,我们程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。今天我们大多数时候使用的计算机,虽然它们的元器件做工越来越精密,处理能力越来越强大,但究其本质来说仍然属于[“冯·诺依曼结构”](https://zh.wikipedia.org/wiki/%E5%86%AF%C2%B7%E8%AF%BA%E4%BC%8A%E6%9B%BC%E7%BB%93%E6%9E%84)的计算机。“冯·诺依曼结构”有两个关键点一是指出要将存储设备与中央处理器分开二是提出了将数据以二进制方式编码。二进制是一种“逢二进一”的计数法跟我们人类使用的“逢十进一”的计数法没有实质性的区别人类因为有十根手指所以使用了十进制因为在数数时十根手指用完之后就只能进位了当然凡事都有例外玛雅人可能是因为长年光着脚的原因把脚趾头也算上了于是他们使用了二十进制的计数法在这种计数法的指导下玛雅人的历法就与我们平常使用的历法不一样而按照玛雅人的历法2012年是上一个所谓的“太阳纪”的最后一年而2013年则是新的“太阳纪”的开始后来这件事情被以讹传讹的方式误传为”2012年是玛雅人预言的世界末日“这种荒诞的说法今天我们可以大胆的猜测玛雅文明之所以发展缓慢估计也与使用了二十进制有关。对于计算机来说二进制在物理器件上来说是最容易实现的高电压表示1低电压表示0于是在“冯·诺依曼结构”的计算机都使用了二进制。虽然我们并不需要每个程序员都能够使用二进制的思维方式来工作但是了解二进制以及它与我们生活中的十进制之间的转换关系以及二进制与八进制和十六进制的转换关系还是有必要的。如果你对这一点不熟悉可以自行使用[维基百科](https://zh.wikipedia.org/wiki/%E4%BA%8C%E8%BF%9B%E5%88%B6)或者[百度百科](https://baike.baidu.com)科普一下。
> 提示:近期关于**量子计算机**的研究已经被推倒了风口浪尖量子计算机基于量子力学进行运算使用量子瞬移的方式来传递信息。2018年6月Intel宣布开发出新款量子芯片并通过了在接近绝对零度环境下的测试2019年1月IBM向全世界发布了首款商业化量子计算机。
> **说明**:近期关于**量子计算机**的研究已经被推倒了风口浪尖量子计算机基于量子力学进行运算使用量子瞬移的方式来传递信息。2018年6月Intel宣布开发出新款量子芯片并通过了在接近绝对零度环境下的测试2019年IBM和Google都推出了自己的量子计算机。
### 变量和类型
在程序设计中变量是一种存储数据的载体。计算机中的变量是实际存在的数据或者说是存储器中存储数据的一块内存空间变量的值可以被读取和修改这是所有计算和控制的基础。计算机能处理的数据有很多种类型除了数值之外还可以处理文本、图形、音频、视频等各种各样的数据那么不同的数据就需要定义不同的存储类型。Python中的数据类型很多而且也允许我们自定义新的数据类型这一点在后面会讲到我们先介绍几种常用的数据类型。
- 整型Python中可以处理任意大小的整数Python 2.x中有int和long两种类型的整数但这种区分对Python来说意义不大因此在Python 3.x中整数只有int这一种了而且支持二进制如`0b100`换算成十进制是4、八进制如`0o100`换算成十进制是64、十进制`100`)和十六进制(`0x100`换算成十进制是256的表示法。
- 整型Python中可以处理任意大小的整数Python 2.x中有`int``long`两种类型的整数但这种区分对Python来说意义不大因此在Python 3.x中整数只有int这一种了而且支持二进制如`0b100`换算成十进制是4、八进制如`0o100`换算成十进制是64、十进制`100`)和十六进制(`0x100`换算成十进制是256的表示法。
- 浮点型:浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,浮点数除了数学写法(如`123.456`)之外还支持科学计数法(如`1.23456e2`)。
- 字符串型:字符串是以单引号或双引号括起来的任意文本,比如`'hello'`和`"hello"`,字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法而且可以书写成多行的形式用三个单引号或三个双引号开头三个单引号或三个双引号结尾
- 布尔型:布尔值只有`True`、`False`两种值,要么是`True`,要么是`False`在Python中可以直接用`True`、`False`表示布尔值(请注意大小写),也可以通过布尔运算计算出来(例如`3 < 5``True``2 == 1``False`
- 复数型:形如`3+5j`,跟数学上的复数表示一样,唯一不同的是虚部的`i`换成了`j`。实际上,这个类型并不能算作常用类型,大家了解下就可以了。
- 复数型:形如`3+5j`,跟数学上的复数表示一样,唯一不同的是虚部的`i`换成了`j`。实际上,这个类型并不常用,大家了解下就可以了。
#### 变量命名
@ -37,21 +37,17 @@
```Python
"""
使用变量保存数据并进行算术运算
使用变量保存数据并进行加减乘除运算
Version: 0.1
Author: 骆昊
"""
a = 321
b = 123
print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a // b)
print(a % b)
print(a ** b)
b = 12
print(a + b) # 333
print(a - b) # 309
print(a * b) # 3852
print(a / b) # 26.75
```
在Python中可以使用`type`函数对变量的类型进行检查。程序设计中函数的概念跟数学上函数的概念是一致的,数学上的函数相信大家并不陌生,它包括了函数名、自变量和因变量。如果暂时不理解这个概念也不要紧,我们会在后续的章节中专门讲解函数的定义和使用。
@ -62,9 +58,7 @@ print(a ** b)
Version: 0.1
Author: 骆昊
Date: 2018-02-27
"""
a = 100
b = 12.345
c = 1 + 5j
@ -96,7 +90,6 @@ print(type(e)) # <class 'bool'>
Version: 0.1
Author: 骆昊
"""
a = int(input('a = '))
b = int(input('b = '))
print('%d + %d = %d' % (a, b, a + b))
@ -133,7 +126,9 @@ Python支持多种运算符下表大致按照优先级从高到低的顺序
>**说明:** 在实际开发中,如果搞不清楚运算符的优先级,可以使用括号来确保运算的执行顺序。
下面的例子演示了赋值运算符和复合赋值运算符的使用。
#### 赋值运算符
赋值运算符应该是最为常见的运算符,它的作用是将右边的值赋给左边的变量。下面的例子演示了赋值运算符和复合赋值运算符的使用。
```Python
"""
@ -142,24 +137,26 @@ Python支持多种运算符下表大致按照优先级从高到低的顺序
Version: 0.1
Author: 骆昊
"""
a = 10
b = 3
a += b # 相当于a = a + b
a *= a + 2 # 相当于a = a * (a + 2)
print(a) # 想想这里会输出什么
print(a) # 算一下这里会输出什么
```
下面的例子演示了比较运算符(关系运算符)、逻辑运算符和身份运算符的使用。
### 比较运算符和逻辑运算符
比较运算符有的地方也称为关系运算符,包括`==`、`!=`、`<`、`>`、`<=`、`>=`,我相信没有什么好解释的,大家一看就能懂,唯一需要提醒的是比较相等用的是`==`,请注意这个地方是两个等号,因为`=`是赋值运算符,我们在上面刚刚讲到过,`==`才是比较相等的比较运算符。比较运算符会产生布尔值,要么是`True`要么是`False`。
逻辑运算符有三个,分别是`and`、`or`和`not`。`and`字面意思是“而且”,所以`and`运算符会连接两个布尔值,如果两个布尔值都是`True`,那么运算的结果就是`True`;左右两边的布尔值有一个是`False`,最终的运算结果就是`False`。相信大家已经想到了,如果`and`左边的布尔值是`False`,不管右边的布尔值是什么,最终的结果都是`False`,所以在做运算的时候右边的值会被跳过(短路处理),这也就意味着在`and`运算符左边为`False`的情况下,右边的表达式根本不会执行。`or`字面意思是“或者”,所以`or`运算符也会连接两个布尔值,如果两个布尔值有任意一个是`True`,那么最终的结果就是`True`。当然,`or`运算符也是有短路功能的,在它左边的布尔值为`True`的情况下,右边的表达式根本不会执行。`not`运算符的后面会跟上一个布尔值,它的作用是得到与该布尔值相反的值,也就是说,后面的布尔值如果是`True`运算结果就是`False`,而后面的布尔值如果是`False`则运算结果就是`True`。
```Python
"""
比较、逻辑和算身份运算符的使用
比较运算符和逻辑运算符的使用
Version: 0.1
Author: 骆昊
"""
flag0 = 1 == 1
flag1 = 3 > 2
flag2 = 2 < 1
@ -172,10 +169,10 @@ print('flag2 =', flag2) # flag2 = False
print('flag3 =', flag3) # flag3 = False
print('flag4 =', flag4) # flag4 = True
print('flag5 =', flag5) # flag5 = False
print(flag1 is True) # True
print(flag2 is not False) # False
```
> **说明**:比较运算符的优先级高于赋值运算符,所以`flag0 = 1 == 1`先做`1 == 1`产生布尔值`True`,再将这个值赋值给变量`flag0`。`print`函数可以输出多个值,多个值之间可以用`,`进行分隔,输出的内容之间默认以空格分开。
### 练习
#### 练习1华氏温度转换为摄氏温度。
@ -191,12 +188,17 @@ print(flag2 is not False) # False
Version: 0.1
Author: 骆昊
"""
f = float(input('请输入华氏温度: '))
c = (f - 32) / 1.8
print('%.1f华氏度 = %.1f摄氏度' % (f, c))
```
> **说明**:在使用`print`函数输出时,也可以对字符串内容进行格式化处理,上面`print`函数中的字符串`%1.f`是一个占位符,稍后会由一个`float`类型的变量值替换掉它。同理,如果字符串中有`%d`,后面可以用一个`int`类型的变量值替换掉它,而`%s`会被字符串的值替换掉。除了这种格式化字符串的方式外,还可以用下面的方式来格式化字符串,其中`{f:.1f}`和`{c:.1f}`可以先看成是`{f}`和`{c}`,表示输出时会用变量`f`和变量`c`的值替换掉这两个占位符,后面的`:.1f`表示这是一个浮点数小数点后保留1位有效数字。
>
> ```Python
> print(f'{f:.1f}华氏度 = {c:.1f}摄氏度')
> ```
#### 练习2输入圆的半径计算计算周长和面积。
参考答案:
@ -208,12 +210,9 @@ print('%.1f华氏度 = %.1f摄氏度' % (f, c))
Version: 0.1
Author: 骆昊
"""
import math
radius = float(input('请输入圆的半径: '))
perimeter = 2 * math.pi * radius
area = math.pi * radius * radius
perimeter = 2 * 3.1416 * radius
area = 3.1416 * radius * radius
print('周长: %.2f' % perimeter)
print('面积: %.2f' % area)
```
@ -229,11 +228,11 @@ print('面积: %.2f' % area)
Version: 0.1
Author: 骆昊
"""
year = int(input('请输入年份: '))
# 如果代码太长写成一行不便于阅读 可以使用\对代码进行折行
is_leap = (year % 4 == 0 and year % 100 != 0) or \
is_leap = year % 4 == 0 and year % 100 != 0 or \
year % 400 == 0
print(is_leap)
```
> **说明**:比较运算符会产生布尔值,而逻辑运算符`and`和`or`会对这些布尔值进行组合,最终也是得到一个布尔值,闰年输出`True`,平年输出`False`。

View File

@ -15,7 +15,6 @@
Version: 0.1
Author: 骆昊
"""
username = input('请输入用户名: ')
password = input('请输入口令: ')
# 用户名是admin且密码是123456则身份验证成功否则身份验证失败
@ -25,9 +24,9 @@ else:
print('身份验证失败!')
```
唯一需要说明的是和C/C++、Java等语言不同Python中没有用花括号来构造代码块而是使用了缩进的方式来设置代码的层次结构,如果`if`条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了,换句话说连续的代码如果又保持了相同的缩进那么它们属于同一个代码块,相当于是一个执行的整体。
需要说明的是和C/C++、Java等语言不同Python中没有用花括号来构造代码块而是**使用了缩进的方式来表示代码的层次结构**,如果`if`条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了。换句话说**连续的代码如果又保持了相同的缩进那么它们属于同一个代码块**,相当于是一个执行的整体。**缩进**可以使用任意数量的空格,但**通常使用4个空格**,建议大家**不要使用制表键**或者**设置你的代码编辑工具自动将制表键变成4个空格**。
当然如果要构造出更多的分支,可以使用`if…elif…else…`结构,例如下面的分段函数求值。
当然如果要构造出更多的分支,可以使用`if...elif...else...`结构或者嵌套的`if...else...`结构,下面的代码演示了如何利用多分支结构实现分段函数求值。
![$$f(x)=\begin{cases} 3x-5&\text{(x>1)}\\x+2&\text{(-1}\leq\text{x}\leq\text{1)}\\5x+3&\text {(x<-1)}\end{cases}$$](./res/formula_1.png)
@ -92,7 +91,6 @@ print('f(%.2f) = %.2f' % (x, y))
Version: 0.1
Author: 骆昊
"""
value = float(input('请输入长度: '))
unit = input('请输入单位: ')
if unit == 'in' or unit == '英寸':
@ -116,7 +114,6 @@ else:
Version: 0.1
Author: 骆昊
"""
score = float(input('请输入成绩: '))
if score >= 90:
grade = 'A'
@ -141,7 +138,6 @@ print('对应的等级是:', grade)
Version: 0.1
Author: 骆昊
"""
a = float(input('a = '))
b = float(input('b = '))
c = float(input('c = '))

View File

@ -2,9 +2,9 @@
### 应用场景
如果在程序中我们需要重复的执行某条或某些指令,例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了刚才的描述中不仅仅有需要重复的动作还需要用到上一章讲的分支结构。再举一个简单的例子我们要实现一个每隔1秒中在屏幕上打印一次&quot;hello, world&quot;并持续打印一个小时的程序,我们肯定不能够直接把`print('hello, world')`这句代码写3600遍如果真的要这样做,那么编程的工作就太无聊乏味了。因此,我们还需要了解一下循环结构,有了循环结构我们就可以轻松的控制某件事或者某些事重复、重复、再重复的去执行
我们在写程序的时候,一定会遇到需要重复执行某条或某些指令的场景。例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向移动的指令。在这个场景中让机器人向球门方向移动就是一个需要重复的动作当然这里还会用到上一课讲的分支结构来判断机器人是否持球以及是否进入射门范围。再举一个简单的例子如果要实现每隔1秒中在屏幕上打印一次“hello, world”并持续打印一个小时,我们肯定不能够直接把`print('hello, world')`这句代码写3600遍里同样需要循环结构。
在Python中构造循环结构有两种做法一种是`for-in`循环,一种是`while`循环。
循环结构就是程序中控制某条或某些指令重复执行的结构。在Python中构造循环结构有两种做法一种是`for-in`循环,一种是`while`循环。
### for-in循环
@ -24,11 +24,12 @@ for x in range(101):
print(sum)
```
需要说明的是上面代码中的`range(101)`可以用来构造一个从0到100的取值范围这样就可以构造出一个整数的序列并用于循环中例如
需要说明的是上面代码中的`range(1, 101)`可以用来构造一个从1到100的范围当我们把这样一个范围放到`for-in`循环中,就可以通过前面的循环变量`x`依次取出从1到100的整数。当然`range`的用法非常灵活,下面给出了一个例子
- `range(101)`可以产生一个0到100的整数序列。
- `range(1, 100)`可以产生一个1到99的整数序列。
- `range(1, 100, 2)`可以产生一个1到99的奇数序列其中2是步长即数值序列的增量。
- `range(101)`可以用来产生0到100范围的整数需要注意的是取不到101。
- `range(1, 101)`可以用来产生1到100范围的整数相当于前面是闭区间后面是开区间。
- `range(1, 101, 2)`可以用来产生1到100的奇数其中2是步长即每次数值递增的值。
- `range(100, 0, -2)`可以用来产生100到1的偶数其中-2是步长即每次数字递减的值。
知道了这一点我们可以用下面的代码来实现1~100之间的偶数求和。
@ -46,7 +47,7 @@ for x in range(2, 101, 2):
print(sum)
```
也可以通过在循环中使用分支结构的方式来实现相同的功能,代码如下所示。
当然,也可以通过在循环中使用分支结构的方式来实现相同的功能,代码如下所示。
```Python
"""
@ -63,20 +64,21 @@ for x in range(1, 101):
print(sum)
```
> **说明**:相较于上面直接跳过奇数的做法,下面这种做法很明显并不是很好的选择。
### while循环
如果要构造不知道具体循环次数的循环结构,我们推荐使用`while`循环。`while`循环通过一个能够产生或转换出`bool`值的表达式来控制循环,表达式的值为`True`循环继续,表达式的值为`False`循环结束。下面我们通过一个“猜数字”的小游戏计算机出一个1~100之间的随机数人输入自己猜的数字计算机给出对应的提示信息直到人猜出计算机出的数字来看看如何使用`while`循环。
如果要构造不知道具体循环次数的循环结构,我们推荐使用`while`循环。`while`循环通过一个能够产生或转换出`bool`值的表达式来控制循环,表达式的值为`True`则继续循环;表达式的值为`False`则结束循环。
下面我们通过一个“猜数字”的小游戏来看看如何使用`while`循环。猜数字游戏的规则是计算机出一个1到100之间的随机数玩家输入自己猜的数字计算机给出对应的提示信息大一点、小一点或猜对了如果玩家猜中了数字计算机提示用户一共猜了多少次游戏结束否则游戏继续。
```Python
"""
猜数字游戏
计算机出一个1~100之间的随机数由人来猜
计算机根据人猜的数字分别给出提示大一点/小一点/猜对了
Version: 0.1
Author: 骆昊
"""
import random
answer = random.randint(1, 100)
@ -147,6 +149,8 @@ else:
#### 练习2输入两个正整数计算它们的最大公约数和最小公倍数。
> **提示**:两个数的最大公约数是两个数的公共因子中最大的那个数;两个数的最小公倍数则是能够同时被两个数整除的最小的那个数。
参考答案:
```Python

View File

@ -17,7 +17,6 @@
Version: 0.1
Author: 骆昊
"""
m = int(input('m = '))
n = int(input('n = '))
fm = 1
@ -26,10 +25,10 @@ for num in range(1, m + 1):
fn = 1
for num in range(1, n + 1):
fn *= num
fmn = 1
fm_n = 1
for num in range(1, m - n + 1):
fmn *= num
print(fm // fn // fmn)
fm_n *= num
print(fm // fn // fm_n)
```
### 函数的作用
@ -43,7 +42,13 @@ print(fm // fn // fmn)
在了解了如何定义函数后,我们可以对上面的代码进行重构,所谓重构就是在不影响代码执行结果的前提下对代码的结构进行调整,重构之后的代码如下所示。
```Python
def factorial(num):
"""
输入M和N计算C(M,N)
Version: 0.1
Author: 骆昊
"""
def fac(num):
"""求阶乘"""
result = 1
for n in range(1, num + 1):
@ -54,10 +59,10 @@ def factorial(num):
m = int(input('m = '))
n = int(input('n = '))
# 当需要计算阶乘的时候不用再写循环求阶乘而是直接调用已经定义好的函数
print(factorial(m) // factorial(n) // factorial(m - n))
print(fac(m) // fac(n) // fac(m - n))
```
> **说明:** Python的`math`模块中其实已经有一个`factoria`l函数了事实上要计算阶乘可以直接使用这个现成的函数而不用自己定义。下面例子中的一些函数在Python中也都是现成的我们这里是为了讲解函数的定义和使用才把它们又实现了一遍实际开发中不建议做这种低级的重复性的工作
> **说明:** Python的`math`模块中其实已经有一个名为`factorial`函数实现了阶乘运算事实上求阶乘并不用自己定义函数。下面的例子中我们讲的函数在Python标准库已经实现过了我们这里是为了讲解函数的定义和使用才把它们又实现了一遍**实际开发中并不建议做这种低级的重复劳动**
### 函数的参数
@ -267,7 +272,7 @@ def is_palindrome(num):
```Python
def is_prime(num):
"""判断一个数是不是素数"""
for factor in range(2, num):
for factor in range(2, int(num ** 0.5) + 1):
if num % factor == 0:
return False
return True if num != 1 else False
@ -286,6 +291,8 @@ if __name__ == '__main__':
> **注意**:通过上面的程序可以看出,当我们**将代码中重复出现的和相对独立的功能抽取成函数**后,我们可以**组合使用这些函数**来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。
### 变量的作用域
最后我们来讨论一下Python中有关变量作用域的问题。
```Python
@ -344,7 +351,7 @@ if __name__ == '__main__':
在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对[迪米特法则](https://zh.wikipedia.org/zh-hans/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在定义它的函数调用结束后依然可以使用它的值,这时候就需要使用[闭包](https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),这个我们在后续的内容中进行讲解。
> **说明:** 很多人经常会将“闭包”和[“匿名函数”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们是不同的概念,如果想提前了解这个概念,推荐看看[维基百科](https://zh.wikipedia.org/wiki/)或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。
> **说明:** 很多人经常会将“闭包”和[“匿名函数”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们并不是一回事,如果想了解这个概念,可以看看[维基百科](https://zh.wikipedia.org/wiki/)的解释或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。
说了那么多其实结论很简单从现在开始我们可以将Python代码按照下面的格式进行书写这一点点的改进其实就是在我们理解了函数和作用域的基础上跨出的巨大的一步。

View File

@ -276,7 +276,7 @@ if __name__ == '__main__':
### 使用元组
Python 中的元组与列表类似也是一种容器数据类型,可以用一个变量(对象)来存储多个数据,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
Python中的元组与列表类似也是一种容器数据类型可以用一个变量对象来存储多个数据不同之处在于元组的元素不能修改在前面的代码中我们已经不止一次使用过元组了。顾名思义我们把多个元素组合到一起就形成了一个元组所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
```Python
# 定义元组
@ -319,30 +319,39 @@ Python中的集合跟数学上的集合是一致的不允许有重复元素
![](./res/python-set.png)
可以按照下面代码所示的方式来创建和使用集合。
```Python
# 创建集合的字面量语法
set1 = {1, 2, 3, 3, 3, 2}
print(set1)
print('Length =', len(set1))
# 创建集合的构造器语法(面向对象部分会进行详细讲解)
set2 = set(range(1, 10))
print(set2)
set3 = set((1, 2, 3, 3, 2, 1))
print(set2, set3)
# 创建集合的推导式语法(推导式也可以用于推导集合)
set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0}
print(set4)
```
向集合添加元素和从集合删除元素。
```Python
set1.add(4)
set1.add(5)
set2.update([11, 12])
print(set1)
print(set2)
set2.discard(5)
# remove的元素如果不存在会引发KeyError
if 4 in set2:
set2.remove(4)
print(set2)
# 遍历集合容器
for elem in set2:
print(elem ** 2, end=' ')
print()
# 将元组转换成集合
set3 = set((1, 2, 3, 3, 2, 1))
print(set1, set2)
print(set3.pop())
print(set3)
```
集合的成员、交集、并集、差集等运算。
```Python
# 集合的交集、并集、差集、对称差运算
print(set1 & set2)
# print(set1.intersection(set2))
@ -367,16 +376,25 @@ print(set1 >= set3)
### 使用字典
字典是另一种可变容器模型,类似于我们生活中使用的字典,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。
字典是另一种可变容器模型,Python中的字典跟我们生活中使用的字典是一样一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。
```Python
# 创建字典的字面量语法
scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82}
print(scores)
# 创建字典的构造器语法
items1 = dict(one=1, two=2, three=3, four=4)
# 通过zip函数将两个序列压成字典
items2 = dict(zip(['a', 'b', 'c'], '123'))
# 创建字典的推导式语法
items3 = {num: num ** 2 for num in range(1, 10)}
print(items1, items2, items3)
# 通过键可以获取字典中对应的值
print(scores['骆昊'])
print(scores['狄仁杰'])
# 对字典进行遍历(遍历的其实是键再通过键取对应的值)
for elem in scores:
print('%s\t--->\t%d' % (elem, scores[elem]))
# 对字典中所有键值对进行遍历
for key in scores:
print(f'{key}: {scores[key]}')
# 更新字典中的元素
scores['白元芳'] = 65
scores['诸葛王朗'] = 71

View File

@ -32,7 +32,7 @@
| \| | 分支 | foo\|bar | 可以匹配foo或者bar |
| (?#) | 注释 | | |
| (exp) | 匹配exp并捕获到自动命名的组中 | | |
| (?&nbsp;&lt;name&gt;exp) | 匹配exp并捕获到名为name的组中 | | |
| (?&lt;name&gt;exp) | 匹配exp并捕获到名为name的组中 | | |
| (?:exp) | 匹配exp但是不捕获匹配的文本 | | |
| (?=exp) | 匹配exp前面的位置 | \\b\\w+(?=ing) | 可以匹配I'm dancing中的danc |
| (?<=exp) | 匹配exp后面的位置 | (?<=\\bdanc)\\w+\\b | 可以匹配I love dancing and reading中的第一个ing |

View File

@ -316,7 +316,7 @@ if __name__ == '__main__':
但是切换作业是有代价的比如从语文切到数学要先收拾桌子上的语文书本、钢笔这叫保存现场然后打开数学课本、找出圆规直尺这叫准备新环境才能开始做数学作业。操作系统在切换进程或者线程时也是一样的它需要先保存当前执行的现场环境CPU寄存器状态、内存页等然后把新任务的执行环境准备好恢复上次的寄存器状态切换内存页等才能开始执行。这个切换过程虽然很快但是也需要耗费时间。如果有几千个任务同时进行操作系统可能就主要忙着切换任务根本没有多少时间去执行任务了这种情况最常见的就是硬盘狂响点窗口无反应系统处于假死状态。所以多任务一旦多到一个限度反而会使得系统性能急剧下降最终导致所有任务都做不好。
是否采用多任务的第二个考虑是任务的类型可以把任务分为计算密集型和I/O密集型。计算密集型任务的特点是要进行大量的计算消耗CPU资源比如对视频进行编码解码或者格式转换等等这种任务全靠CPU的运算能力虽然也可以用多任务完成但是任务越多花在任务切换的时间就越多CPU执行任务的效率就越低。计算密集型任务由于主要消耗CPU资源这类任务用Python这样的脚本语言去执行效率通常很低最能胜任这类任务的是C语言我们之前提到Python中有嵌入C/C++代码的机制。
是否采用多任务的第二个考虑是任务的类型可以把任务分为计算密集型和I/O密集型。计算密集型任务的特点是要进行大量的计算消耗CPU资源比如对视频进行编码解码或者格式转换等等这种任务全靠CPU的运算能力虽然也可以用多任务完成但是任务越多花在任务切换的时间就越多CPU执行任务的效率就越低。计算密集型任务由于主要消耗CPU资源这类任务用Python这样的脚本语言去执行效率通常很低最能胜任这类任务的是C语言我们之前提到Python中有嵌入C/C++代码的机制。
除了计算密集型任务其他的涉及到网络、存储介质I/O的任务都可以视为I/O密集型任务这类任务的特点是CPU消耗很少任务的大部分时间都在等待I/O操作完成因为I/O的速度远远低于CPU和内存的速度。对于I/O密集型任务如果启动多任务就可以减少I/O等待时间从而让CPU高效率的运转。有一大类的任务都属于I/O密集型任务这其中包括了我们很快会涉及到的网络应用和Web应用。
@ -324,9 +324,9 @@ if __name__ == '__main__':
### 单线程+异步I/O
现代操作系统对I/O操作的改进中最为重要的就是支持异步I/O。如果充分利用操作系统提供的异步I/O支持就可以用单进程单线程模型来执行多任务这种全新的模型称为事件驱动模型。Nginx就是支持异步I/O的Web服务器它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上可以运行多个进程数量与CPU核心数相同充分利用多核CPU。用Node.js开发的服务器端程序也使用了这种工作模式这也是当下实现多任务编程的一种趋势
现代操作系统对I/O操作的改进中最为重要的就是支持异步I/O。如果充分利用操作系统提供的异步I/O支持就可以用单进程单线程模型来执行多任务这种全新的模型称为事件驱动模型。Nginx就是支持异步I/O的Web服务器它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上可以运行多个进程数量与CPU核心数相同充分利用多核CPU。用Node.js开发的服务器端程序也使用了这种工作模式这也是当下并发编程的一种流行方案
在Python语言中单线程+异步I/O的编程模型称为协程有了协程的支持就可以基于事件驱动编写高效的多任务程序。协程最大的优势就是极高的执行效率因为子程序切换不是线程切换而是由程序自身控制因此没有线程切换的开销。协程的第二个优势就是不需要多线程的锁机制因为只有一个线程也不存在同时写变量冲突在协程中控制共享资源不用加锁只需要判断状态就好了所以执行效率比多线程高很多。如果想要充分利用CPU的多核特性最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。关于这方面的内容,我稍后会做一个专题来进行讲解。
在Python语言中单线程+异步I/O的编程模型称为协程有了协程的支持就可以基于事件驱动编写高效的多任务程序。协程最大的优势就是极高的执行效率因为子程序切换不是线程切换而是由程序自身控制因此没有线程切换的开销。协程的第二个优势就是不需要多线程的锁机制因为只有一个线程也不存在同时写变量冲突在协程中控制共享资源不用加锁只需要判断状态就好了所以执行效率比多线程高很多。如果想要充分利用CPU的多核特性最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。关于这方面的内容,在后续的课程中会进行讲解。
### 应用案例

View File

@ -110,13 +110,70 @@ Pillow中最为重要的是Image类读取和处理图像都要通过这个类
### 处理Excel电子表格
Python的openpyxl模块让我们可以在Python程序中读取和修改Excel电子表格当然实际工作中我们可能会用LibreOffice Calc和OpenOffice Calc来处理Excel的电子表格文件这就意味着openpyxl模块也能处理来自这些软件生成的电子表格。关于openpyxl的使用手册和使用文档可以查看它的[官方文档](https://openpyxl.readthedocs.io/en/stable/#)。
Python的openpyxl模块让我们可以在Python程序中读取和修改Excel电子表格由于微软从Office 2007开始使用了新的文件格式这使得Office Excel和LibreOffice Calc、OpenOffice Calc是完全兼容的这就意味着openpyxl模块也能处理来自这些软件生成的电子表格。
```Python
import datetime
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
ws['A1'] = 42
ws.append([1, 2, 3])
ws['A2'] = datetime.datetime.now()
wb.save("sample.xlsx")
```
### 处理Word文档
利用python-docx模块Pytho 可以创建和修改Word文档当然这里的Word文档不仅仅是指通过微软的Office软件创建的扩展名为docx的文档LibreOffice Writer和OpenOffice Writer都是免费的字处理软件。
利用python-docx模块Python可以创建和修改Word文档当然这里的Word文档不仅仅是指通过微软的Office软件创建的扩展名为docx的文档LibreOffice Writer和OpenOffice Writer都是免费的字处理软件。
```Python
from docx import Document
from docx.shared import Inches
### 处理PDF文档
document = Document()
PDF是Portable Document Format的缩写使用.pdf作为文件扩展名。接下来我们就研究一下如何通过Python实现从PDF读取文本内容和从已有的文档生成新的PDF文件。
document.add_heading('Document Title', 0)
p = document.add_paragraph('A plain paragraph having some ')
p.add_run('bold').bold = True
p.add_run(' and some ')
p.add_run('italic.').italic = True
document.add_heading('Heading, level 1', level=1)
document.add_paragraph('Intense quote', style='Intense Quote')
document.add_paragraph(
'first item in unordered list', style='List Bullet'
)
document.add_paragraph(
'first item in ordered list', style='List Number'
)
document.add_picture('monty-truth.png', width=Inches(1.25))
records = (
(3, '101', 'Spam'),
(7, '422', 'Eggs'),
(4, '631', 'Spam, spam, eggs, and spam')
)
table = document.add_table(rows=1, cols=3)
hdr_cells = table.rows[0].cells
hdr_cells[0].text = 'Qty'
hdr_cells[1].text = 'Id'
hdr_cells[2].text = 'Desc'
for qty, id, desc in records:
row_cells = table.add_row().cells
row_cells[0].text = str(qty)
row_cells[1].text = id
row_cells[2].text = desc
document.add_page_break()
document.save('demo.docx')
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

@ -10,24 +10,23 @@ Date: 2018-03-20
# 每个进程都有自己独立的内存空间 所以进程之间共享数据只能通过IPC的方式
from multiprocessing import Process, Queue
from multiprocessing import Process, Queue, current_process
from time import sleep
def sub_task(string, q):
number = q.get()
while number:
print('%d: %s' % (number, string))
sleep(0.001)
number = q.get()
def sub_task(content, counts):
print(f'PID: {current_process().pid}')
counter = 0
while counter < counts:
counter += 1
print(f'{counter}: {content}')
sleep(0.01)
def main():
q = Queue(10)
for number in range(1, 11):
q.put(number)
Process(target=sub_task, args=('Ping', q)).start()
Process(target=sub_task, args=('Pong', q)).start()
number = random.randrange(5, 10)
Process(target=sub_task, args=('Ping', number)).start()
Process(target=sub_task, args=('Pong', number)).start()
if __name__ == '__main__':

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 515 KiB

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 B

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 816 B

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 B

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 609 KiB

After

Width:  |  Height:  |  Size: 468 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 KiB

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 KiB

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 KiB

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 KiB

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 KiB

After

Width:  |  Height:  |  Size: 824 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,31 +1,137 @@
## 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)
```
- 渐近时间复杂度的大O标记
> 说明:生成式(推导式)可以用来生成列表、集合和字典。
- 嵌套的列表的坑
```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))
```
### 数据结构和算法
- 算法:解决问题的方法和步骤
- 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。
- 渐近时间复杂度的大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)
![](./res/algorithm_complexity_2.png)
- 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)
- 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)
```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,12 +142,27 @@
```
```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):
for j in range(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(len(items) - 1 - i):
if comp(items[j], items[j + 1]):
items[j], items[j + 1] = items[j + 1], items[j]
swapped = True
@ -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,95 +232,7 @@
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,43 +435,43 @@
```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)$。
- 将函数视为“一等公民”
### 函数的使用方式
- 将函数视为“一等公民”
- 函数可以赋值给变量
- 函数可以作为函数的参数
- 函数可以作为函数的返回值
- 高阶函数的用法(`filter`、`map`以及它们的替代品)
- 高阶函数的用法(`filter`、`map`以及它们的替代品)
```Python
items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, range(1, 10))))
items2 = [x ** 2 for x in range(1, 10) if x % 2]
```
- 位置参数、可变参数、关键字参数、命名关键字参数
- 位置参数、可变参数、关键字参数、命名关键字参数
- 参数的元信息(代码可读性问题)
- 参数的元信息(代码可读性问题)
- 匿名函数和内联函数的用法(`lambda`函数)
- 匿名函数和内联函数的用法(`lambda`函数)
- 闭包和作用域问题
- 闭包和作用域问题
- Python搜索变量的LEGB顺序Local --> Embedded --> Global --> Built-in
- Python搜索变量的LEGB顺序Local >>> Embedded >>> Global >>> Built-in
- `global`和`nonlocal`关键字的作用
@ -460,7 +479,7 @@
`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__`方式获得被装饰之前的函数或类来取消装饰器的作用。
例子:用装饰器来实现单例模式。
@ -547,22 +566,24 @@
@singleton
class President():
class President:
"""总统(单例类)"""
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,9 +596,11 @@
return wrapper
```
3. 面向对象相关知识
> **提示**:上面的代码用到了`with`上下文语法来进行锁操作,因为锁对象本身就是上下文管理器对象(支持`__enter__`和`__exit__`魔术方法)。在`wrapper`函数中,我们先做了一次不带锁的检查,然后再做带锁的检查,这样做比直接加锁检查性能要更好,如果对象已经创建就没有必须再去加锁而是直接返回该对象就可以了。
- 三大支柱:封装、继承、多态
### 面向对象相关知识
- 三大支柱:封装、继承、多态
例子:工资结算系统。
@ -629,21 +652,15 @@
return 1800.0 + self.sales * 0.05
class EmployeeFactory():
class EmployeeFactory:
"""创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)"""
@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,14 +672,14 @@
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__':
main()
```
- 类与类之间的关系
- 类与类之间的关系
- is-a关系继承
- has-a关系关联 / 聚合 / 合成
@ -699,12 +716,9 @@
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()
return f'{suites[self.suite.value]}{faces[self.face]}'
def __repr__(self):
return self.show()
@ -769,20 +783,20 @@
main()
```
> 说明上面的代码中使用了Emoji字符来表示扑克牌的四种花色在某些不支持Emoji字符的系统上可能无法显示。
> **说明**上面的代码中使用了Emoji字符来表示扑克牌的四种花色在某些不支持Emoji字符的系统上可能无法显示。
- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)
- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)
- 垃圾回收、循环引用和弱引用
- 垃圾回收、循环引用和弱引用
Python使用了自动化内存管理这种管理机制以**引用计数**为基础,同时也引入了**标记-清除**和**分代收集**两种机制为辅的策略。
```C
typedef struct_object {
typedef struct _object {
/* 引用计数 */
int ob_refcnt;
/* 对象指针 */
struct_typeobject *ob_type;
struct _typeobject *ob_type;
} PyObject;
```
@ -826,28 +840,28 @@
以下情况会导致垃圾回收:
- 调用`gc.collect()`
- gc模块的计数器达到阀值
- `gc`模块的计数器达到阀值
- 程序退出
如果循环引用中两个对象都定义了`__del__`方法gc模块不会销毁这些不可达对象因为gc模块不知道应该先调用哪个对象的`__del__`方法这个问题在Python 3.6中得到了解决。
如果循环引用中两个对象都定义了`__del__`方法,`gc`模块不会销毁这些不可达对象因为gc模块不知道应该先调用哪个对象的`__del__`方法这个问题在Python 3.6中得到了解决。
也可以通过`weakref`模块构造弱引用的方式来解决循环引用的问题。
- 魔法属性和方法请参考《Python魔法方法指南》
- 魔法属性和方法请参考《Python魔法方法指南》
有几个小问题请大家思考:
- 自定义的对象能不能使用运算符做运算?
- 自定义的对象能不能放到set中能去重吗
- 自定义的对象能不能作为dict的键
- 自定义的对象能不能放到`set`中?能去重吗?
- 自定义的对象能不能作为`dict`的键?
- 自定义的对象能不能使用上下文语法?
- 混入Mixin
- 混入Mixin
例子自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。
```Python
class SetOnceMappingMixin():
class SetOnceMappingMixin:
"""自定义混入类"""
__slots__ = ()
@ -871,7 +885,9 @@
print(my_dict)
```
- 元编程和元类
- 元编程和元类
对象是通过类创建的,类是通过元类创建的,元类提供了创建类的元信息。所有的类都直接或间接的继承自`object`,所有的元类都直接或间接的继承自`type`。
例子:用元类实现单例模式。
@ -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):
@ -897,10 +913,11 @@
class President(metaclass=SingletonMeta):
"""总统(单例类)"""
pass
```
- 面向对象设计原则
- 面向对象设计原则
- 单一职责原则 **S**RP- 一个类只做该做的事情(类的设计要高内聚)
- 开闭原则 **O**CP- 软件实体应该对扩展开发对修改关闭
@ -910,19 +927,19 @@
- 合成聚合复用原则CARP - 优先使用强关联关系而不是继承关系复用代码
- 最少知识原则迪米特法则Lo**D**- 不要给没有必然联系的对象发消息
> 说明:上面加粗的字母放在一起称为面向对象的**SOLID**原则。
> **说明**:上面加粗的字母放在一起称为面向对象的**SOLID**原则。
- GoF设计模式
- GoF设计模式
- 创建型模式:单例、工厂、建造者、原型
- 结构型模式:适配器、门面(外观)、代理
- 行为型模式:迭代器、观察者、状态、策略
例子:可插拔的哈希算法。
例子:可插拔的哈希算法(策略模式)
```Python
class StreamHasher():
"""哈希摘要生成器(策略模式)"""
"""哈希摘要生成器"""
def __init__(self, alg='md5', size=4096):
self.size = size
@ -941,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))
@ -952,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):
"""迭代器"""
@ -986,11 +997,44 @@
raise StopIteration()
```
5. 并发编程
- 生成器是语法简化版的迭代器。
Python中实现并发编程的三种方案多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验坏处在于并发的程序不容易开发和调试同时对其他程序来说它并不友好。
```Python
def fib(num):
"""生成器"""
a, b = 0, 1
for _ in range(num):
a, b = b, a + b
yield a
```
- 多线程Python中提供了Thread类并辅以Lock、Condition、Event、Semaphore和Barrier。Python中有GIL来防止多个线程同时执行本地字节码这个锁对于CPython是必须的因为CPython的内存管理并不是线程安全的因为GIL的存在多线程并不能发挥CPU的多核特性。
- 生成器进化为协程。
生成器对象可以使用`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
"""
@ -1037,7 +1081,7 @@
main()
```
多个线程竞争资源的情况
多个线程竞争资源的情况
```Python
"""
@ -1066,19 +1110,6 @@
self.balance = new_balance
class AddMoneyThread(threading.Thread):
"""自定义线程类"""
def __init__(self, account, money):
self.account = account
self.money = money
# 自定义线程的初始化方法中必须调用父类的初始化方法
super().__init__()
def run(self):
# 线程启动之后要执行的操作
self.account.deposit(self.money)
def main():
"""主函数"""
account = Account()
@ -1086,14 +1117,6 @@
pool = ThreadPoolExecutor(max_workers=10)
futures = []
for _ in range(100):
# 创建线程的第1种方式
# threading.Thread(
# target=account.deposit, args=(1, )
# ).start()
# 创建线程的第2种方式
# AddMoneyThread(account, 1).start()
# 创建线程的第3种方式
# 调用线程池中的线程来执行特定的任务
future = pool.submit(account.deposit, 1)
futures.append(future)
# 关闭线程池
@ -1107,7 +1130,7 @@
main()
```
修改上面的程序启动5个线程向账户中存钱5个线程从账户中取钱取钱时如果余额不足就暂停线程进行等待。为了达到上述目标需要对存钱和取钱的线程进行调度在余额不足时取钱的线程暂停并释放锁而存钱的线程将钱存入后要通知取钱的线程使其从暂停状态被唤醒。可以使用`threading`模块的Condition来实现线程调度该对象也是基于锁来创建的代码如下所示
修改上面的程序启动5个线程向账户中存钱5个线程从账户中取钱取钱时如果余额不足就暂停线程进行等待。为了达到上述目标需要对存钱和取钱的线程进行调度在余额不足时取钱的线程暂停并释放锁而存钱的线程将钱存入后要通知取钱的线程使其从暂停状态被唤醒。可以使用`threading`模块的`Condition`来实现线程调度,该对象也是基于锁来创建的,代码如下所示:
```Python
"""
@ -1122,12 +1145,12 @@
import threading
class Account():
class Account:
"""银行账户"""
def __init__(self, balance=0):
self.balance = balance
lock = threading.Lock()
lock = threading.RLock()
self.condition = threading.Condition(lock)
def withdraw(self, money):
@ -1168,9 +1191,10 @@
def main():
account = Account()
with ThreadPoolExecutor(max_workers=10) as pool:
with ThreadPoolExecutor(max_workers=15) as pool:
for _ in range(5):
pool.submit(add_money, account)
for _ in range(10):
pool.submit(sub_money, account)
@ -1178,7 +1202,7 @@
main()
```
- 多进程多进程可以有效的解决GIL的问题实现多进程主要的类是Process其他辅助的类跟threading模块中的类似进程间共享数据可以使用管道、套接字等在multiprocessing模块中有一个Queue类它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。
- 多进程多进程可以有效的解决GIL的问题实现多进程主要的类是`Process`,其他辅助的类跟`threading`模块中的类似,进程间共享数据可以使用管道、套接字等,在`multiprocessing`模块中有一个`Queue`类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。
```Python
"""
@ -1235,7 +1259,7 @@
main()
```
> 说明**多线程和多进程的比较**。
> **重点****多线程和多进程的比较**。
>
> 以下情况需要使用多线程:
>
@ -1248,7 +1272,7 @@
> 2. 程序的输入可以并行的分成块,并且可以将运算结果合并。
> 3. 程序在内存使用方面没有任何限制且不强依赖于I/O操作读写文件、套接字等
- 异步处理从调度程序的任务队列中挑选任务该调度程序以交叉的形式执行这些任务我们并不能保证任务将以某种顺序去执行因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现由于执行时间和顺序的不确定因此需要通过回调式编程或者`future`对象来获取任务执行的结果。Python 3通过`asyncio`模块和`await`和`async`关键字在Python 3.7中正式被列为关键字)来支持异步处理。
- 异步处理从调度程序的任务队列中挑选任务该调度程序以交叉的形式执行这些任务我们并不能保证任务将以某种顺序去执行因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现由于执行时间和顺序的不确定因此需要通过回调式编程或者`future`对象来获取任务执行的结果。Python 3通过`asyncio`模块和`await`和`async`关键字在Python 3.7中正式被列为关键字)来支持异步处理。
```Python
"""
@ -1303,9 +1327,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
@ -1334,8 +1358,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()
@ -1343,10 +1367,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来作为后端的消息代理。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -1,6 +1,6 @@
## Web前端概述
> 说明:本文使用的部分插图来自*Jon Duckett*先生的*[HTML and CSS: Design and Build Websites](https://www.amazon.cn/dp/1118008189/ref=sr_1_5?__mk_zh_CN=%E4%BA%9A%E9%A9%AC%E9%80%8A%E7%BD%91%E7%AB%99&keywords=html+%26+css&qid=1554609325&s=gateway&sr=8-5)*一书,这是一本非常棒的前端入门书,有兴趣的读者可以在亚马逊或者其他网站上找到该书的购买链接。
> **说明**:本文使用的部分插图来自*Jon Duckett*先生的*[HTML and CSS: Design and Build Websites](https://www.amazon.cn/dp/1118008189/ref=sr_1_5?__mk_zh_CN=%E4%BA%9A%E9%A9%AC%E9%80%8A%E7%BD%91%E7%AB%99&keywords=html+%26+css&qid=1554609325&s=gateway&sr=8-5)*一书,这是一本非常棒的前端入门书,有兴趣的读者可以在亚马逊或者其他网站上找到该书的购买链接。
### HTML简史
@ -288,7 +288,7 @@
- `delete`关键字
- 标准对象
- `Number` / `String` / `Boolean` / `Symbol` / `Array` / `Function`
- `Date` / `Error` / `Math` / `RegEx` / `Object` / `Map` / `Set`
- `Date` / `Error` / `Math` / `RegExp` / `Object` / `Map` / `Set`
- `JSON` / `Promise` / `Generator` / `Reflect` / `Proxy`
#### BOM

View File

@ -68,7 +68,7 @@
</li>
</ul>
<div>
<input @keydown.enter="addItem()" type="text" id="fname" v-model="fname">
<input @keydown.enter="addItem()" type="text" id="fname" v-model.trim="fname">
<button id="ok" @click="addItem()">确定</button>
</div>
</div>
@ -82,8 +82,8 @@
},
methods: {
addItem() {
if (this.fname.trim().length > 0) {
this.fruits.push(this.fname.trim())
if (this.fname.length > 0) {
this.fruits.push(this.fname)
}
this.fname = ''
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 505 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 B

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 B

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Some files were not shown because too many files have changed in this diff Show More