diff --git a/Day01-15/01.初识Python.md b/Day01-15/01.初识Python.md index 05acd85..50f7e3c 100644 --- a/Day01-15/01.初识Python.md +++ b/Day01-15/01.初识Python.md @@ -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表示大版本号,一般当整体重写,或出现不向后兼容的改变时,增加A;B表示功能更新,出现新功能时增加B;C表示小的改动(例如:修复了某个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表示大版本号,一般当整体重写,或出现不向后兼容的改变时,增加A;B表示功能更新,出现新功能时增加B;C表示小的改动(例如:修复了某个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]()可能是更好的选择,它不用花钱并提供了更为完整和强大的功能,有兴趣的读者可以自行研究。 +> **说明**:事实上[Visual Studio Code]()可能是更好的选择,它不用花钱并提供了更为完整和强大的功能,有兴趣的读者可以自行研究。 #### 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() ``` - > 提示:本章提供的代码中还有画国旗和画小猪佩奇的代码,有兴趣的读者请自行研究。 + > **提示**:本章提供的代码中还有画国旗和画小猪佩奇的代码,有兴趣的读者请自行研究。 diff --git a/Day01-15/02.语言元素.md b/Day01-15/02.语言元素.md index 65d3dd9..bdb7f13 100644 --- a/Day01-15/02.语言元素.md +++ b/Day01-15/02.语言元素.md @@ -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,19 +58,17 @@ print(a ** b) Version: 0.1 Author: 骆昊 -Date: 2018-02-27 """ - a = 100 b = 12.345 c = 1 + 5j d = 'hello, world' e = True -print(type(a)) # -print(type(b)) # -print(type(c)) # -print(type(d)) # -print(type(e)) # +print(type(a)) # +print(type(b)) # +print(type(c)) # +print(type(d)) # +print(type(e)) # ``` 可以使用Python中内置的函数对变量类型进行转换。 @@ -96,7 +90,6 @@ print(type(e)) # 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,40 +137,42 @@ Python支持多种运算符,下表大致按照优先级从高到低的顺序 Version: 0.1 Author: 骆昊 """ - a = 10 b = 3 -a += b # 相当于:a = a + b -a *= a + 2 # 相当于:a = a * (a + 2) -print(a) # 想想这里会输出什么 +a += b # 相当于:a = a + b +a *= a + 2 # 相当于:a = a * (a + 2) +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 flag3 = flag1 and flag2 flag4 = flag1 or flag2 flag5 = not (1 != 2) -print('flag0 =', flag0) # flag0 = True -print('flag1 =', flag1) # flag1 = True -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 +print('flag0 =', flag0) # flag0 = True +print('flag1 =', flag1) # flag1 = True +print('flag2 =', flag2) # flag2 = False +print('flag3 =', flag3) # flag3 = False +print('flag4 =', flag4) # flag4 = True +print('flag5 =', flag5) # flag5 = 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 \ - year % 400 == 0 +is_leap = year % 4 == 0 and year % 100 != 0 or \ + year % 400 == 0 print(is_leap) ``` +> **说明**:比较运算符会产生布尔值,而逻辑运算符`and`和`or`会对这些布尔值进行组合,最终也是得到一个布尔值,闰年输出`True`,平年输出`False`。 \ No newline at end of file diff --git a/Day01-15/03.分支结构.md b/Day01-15/03.分支结构.md index e69246b..abd08e7 100644 --- a/Day01-15/03.分支结构.md +++ b/Day01-15/03.分支结构.md @@ -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 = ')) diff --git a/Day01-15/04.循环结构.md b/Day01-15/04.循环结构.md index 4415720..7bc33f9 100644 --- a/Day01-15/04.循环结构.md +++ b/Day01-15/04.循环结构.md @@ -2,9 +2,9 @@ ### 应用场景 -如果在程序中我们需要重复的执行某条或某些指令,例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了,刚才的描述中不仅仅有需要重复的动作,还需要用到上一章讲的分支结构。再举一个简单的例子,我们要实现一个每隔1秒中在屏幕上打印一次"hello, world"并持续打印一个小时的程序,我们肯定不能够直接把`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 diff --git a/Day01-15/06.函数和模块的使用.md b/Day01-15/06.函数和模块的使用.md index 4badd51..11d5099 100644 --- a/Day01-15/06.函数和模块的使用.md +++ b/Day01-15/06.函数和模块的使用.md @@ -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 diff --git a/Day01-15/res/TCP-IP-model.png b/Day01-15/res/TCP-IP-model.png index 777af47..1c641fc 100644 Binary files a/Day01-15/res/TCP-IP-model.png and b/Day01-15/res/TCP-IP-model.png differ diff --git a/Day01-15/res/after-browser.jpg b/Day01-15/res/after-browser.jpg index 7cc9520..fb6b27c 100644 Binary files a/Day01-15/res/after-browser.jpg and b/Day01-15/res/after-browser.jpg differ diff --git a/Day01-15/res/arpanet.png b/Day01-15/res/arpanet.png index 65d2dd3..3a1f632 100644 Binary files a/Day01-15/res/arpanet.png and b/Day01-15/res/arpanet.png differ diff --git a/Day01-15/res/ball-game.png b/Day01-15/res/ball-game.png index 37b9f89..a0b175b 100644 Binary files a/Day01-15/res/ball-game.png and b/Day01-15/res/ball-game.png differ diff --git a/Day01-15/res/ball.png b/Day01-15/res/ball.png index 0a63bdc..496e6b5 100644 Binary files a/Day01-15/res/ball.png and b/Day01-15/res/ball.png differ diff --git a/Day01-15/res/before-browser.jpg b/Day01-15/res/before-browser.jpg index f816144..89c3acf 100644 Binary files a/Day01-15/res/before-browser.jpg and b/Day01-15/res/before-browser.jpg differ diff --git a/Day01-15/res/browers.jpg b/Day01-15/res/browers.jpg index 0be1d4d..2802a23 100644 Binary files a/Day01-15/res/browers.jpg and b/Day01-15/res/browers.jpg differ diff --git a/Day01-15/res/browser-market-place.jpeg b/Day01-15/res/browser-market-place.jpeg index 196b0f4..cb71d38 100644 Binary files a/Day01-15/res/browser-market-place.jpeg and b/Day01-15/res/browser-market-place.jpeg differ diff --git a/Day01-15/res/fibonacci-blocks.png b/Day01-15/res/fibonacci-blocks.png index a638bf1..be4fc13 100644 Binary files a/Day01-15/res/fibonacci-blocks.png and b/Day01-15/res/fibonacci-blocks.png differ diff --git a/Day01-15/res/file-open-mode.png b/Day01-15/res/file-open-mode.png index 2d01ad4..dd6b927 100644 Binary files a/Day01-15/res/file-open-mode.png and b/Day01-15/res/file-open-mode.png differ diff --git a/Day01-15/res/formula_1.png b/Day01-15/res/formula_1.png index 5a0a7e2..ec5e551 100644 Binary files a/Day01-15/res/formula_1.png and b/Day01-15/res/formula_1.png differ diff --git a/Day01-15/res/formula_2.png b/Day01-15/res/formula_2.png index c21c2ae..954d921 100644 Binary files a/Day01-15/res/formula_2.png and b/Day01-15/res/formula_2.png differ diff --git a/Day01-15/res/formula_3.png b/Day01-15/res/formula_3.png index b9a84ed..716835f 100644 Binary files a/Day01-15/res/formula_3.png and b/Day01-15/res/formula_3.png differ diff --git a/Day01-15/res/formula_4.png b/Day01-15/res/formula_4.png index 692fb08..c87452f 100644 Binary files a/Day01-15/res/formula_4.png and b/Day01-15/res/formula_4.png differ diff --git a/Day01-15/res/formula_5.png b/Day01-15/res/formula_5.png index 28fad34..29e0a58 100644 Binary files a/Day01-15/res/formula_5.png and b/Day01-15/res/formula_5.png differ diff --git a/Day01-15/res/formula_6.png b/Day01-15/res/formula_6.png index 86df5da..41e0dfa 100644 Binary files a/Day01-15/res/formula_6.png and b/Day01-15/res/formula_6.png differ diff --git a/Day01-15/res/formula_7.png b/Day01-15/res/formula_7.png index 92cd76d..34862f2 100644 Binary files a/Day01-15/res/formula_7.png and b/Day01-15/res/formula_7.png differ diff --git a/Day01-15/res/formula_8.png b/Day01-15/res/formula_8.png index a37f92b..f3f2e29 100644 Binary files a/Day01-15/res/formula_8.png and b/Day01-15/res/formula_8.png differ diff --git a/Day01-15/res/how-data-is-processed.jpg b/Day01-15/res/how-data-is-processed.jpg index 4359bcd..b1f5486 100644 Binary files a/Day01-15/res/how-data-is-processed.jpg and b/Day01-15/res/how-data-is-processed.jpg differ diff --git a/Day01-15/res/image-crop.png b/Day01-15/res/image-crop.png index bb4a9bf..fa19b5b 100644 Binary files a/Day01-15/res/image-crop.png and b/Day01-15/res/image-crop.png differ diff --git a/Day01-15/res/image-filter.png b/Day01-15/res/image-filter.png index aac571c..cc4e6e5 100644 Binary files a/Day01-15/res/image-filter.png and b/Day01-15/res/image-filter.png differ diff --git a/Day01-15/res/image-paste.png b/Day01-15/res/image-paste.png index 6c5c39d..df25e85 100644 Binary files a/Day01-15/res/image-paste.png and b/Day01-15/res/image-paste.png differ diff --git a/Day01-15/res/image-putpixel.png b/Day01-15/res/image-putpixel.png index 54712b7..4a4a7a8 100644 Binary files a/Day01-15/res/image-putpixel.png and b/Day01-15/res/image-putpixel.png differ diff --git a/Day01-15/res/image-rotate.png b/Day01-15/res/image-rotate.png index 5db7459..84af30b 100644 Binary files a/Day01-15/res/image-rotate.png and b/Day01-15/res/image-rotate.png differ diff --git a/Day01-15/res/image-show.png b/Day01-15/res/image-show.png index 396cb5a..15723fb 100644 Binary files a/Day01-15/res/image-show.png and b/Day01-15/res/image-show.png differ diff --git a/Day01-15/res/image-thumbnail.png b/Day01-15/res/image-thumbnail.png index 91c623f..a6d5239 100644 Binary files a/Day01-15/res/image-thumbnail.png and b/Day01-15/res/image-thumbnail.png differ diff --git a/Day01-15/res/image-transpose.png b/Day01-15/res/image-transpose.png index 685a280..43a8007 100644 Binary files a/Day01-15/res/image-transpose.png and b/Day01-15/res/image-transpose.png differ diff --git a/Day01-15/res/ipython-timeit.png b/Day01-15/res/ipython-timeit.png index 5b52382..4cd5a72 100644 Binary files a/Day01-15/res/ipython-timeit.png and b/Day01-15/res/ipython-timeit.png differ diff --git a/Day01-15/res/macos-monitor.png b/Day01-15/res/macos-monitor.png index 3d1b88b..cac5138 100644 Binary files a/Day01-15/res/macos-monitor.png and b/Day01-15/res/macos-monitor.png differ diff --git a/Day01-15/res/object-feature.png b/Day01-15/res/object-feature.png index 62c6057..538fa4d 100644 Binary files a/Day01-15/res/object-feature.png and b/Day01-15/res/object-feature.png differ diff --git a/Day01-15/res/oop-zhihu.png b/Day01-15/res/oop-zhihu.png index c473984..40e45ce 100644 Binary files a/Day01-15/res/oop-zhihu.png and b/Day01-15/res/oop-zhihu.png differ diff --git a/Day01-15/res/osi_rm.gif b/Day01-15/res/osi_rm.gif index 876960d..9228af5 100644 Binary files a/Day01-15/res/osi_rm.gif and b/Day01-15/res/osi_rm.gif differ diff --git a/Day01-15/res/osimodel.png b/Day01-15/res/osimodel.png index f0361c9..9c13968 100644 Binary files a/Day01-15/res/osimodel.png and b/Day01-15/res/osimodel.png differ diff --git a/Day01-15/res/python-idle.png b/Day01-15/res/python-idle.png index 1e90598..e55248f 100644 Binary files a/Day01-15/res/python-idle.png and b/Day01-15/res/python-idle.png differ diff --git a/Day01-15/res/python-ipython.png b/Day01-15/res/python-ipython.png index c3a9054..35d34a7 100644 Binary files a/Day01-15/res/python-ipython.png and b/Day01-15/res/python-ipython.png differ diff --git a/Day01-15/res/python-jupyter-1.png b/Day01-15/res/python-jupyter-1.png index 58cedc5..cad28e2 100644 Binary files a/Day01-15/res/python-jupyter-1.png and b/Day01-15/res/python-jupyter-1.png differ diff --git a/Day01-15/res/python-jupyter-2.png b/Day01-15/res/python-jupyter-2.png index 2724988..70102b0 100644 Binary files a/Day01-15/res/python-jupyter-2.png and b/Day01-15/res/python-jupyter-2.png differ diff --git a/Day01-15/res/python-pycharm.png b/Day01-15/res/python-pycharm.png index ffae8c3..57f2da5 100644 Binary files a/Day01-15/res/python-pycharm.png and b/Day01-15/res/python-pycharm.png differ diff --git a/Day01-15/res/python-set.png b/Day01-15/res/python-set.png index 2110598..0d59ce1 100644 Binary files a/Day01-15/res/python-set.png and b/Day01-15/res/python-set.png differ diff --git a/Day01-15/res/python-sublime.png b/Day01-15/res/python-sublime.png index c786d13..ce9590d 100644 Binary files a/Day01-15/res/python-sublime.png and b/Day01-15/res/python-sublime.png differ diff --git a/Day01-15/res/tcpipprotocols.png b/Day01-15/res/tcpipprotocols.png index 4acf216..bb62ed2 100644 Binary files a/Day01-15/res/tcpipprotocols.png and b/Day01-15/res/tcpipprotocols.png differ diff --git a/Day01-15/res/tel-start-number.png b/Day01-15/res/tel-start-number.png index b1be7a9..827522c 100644 Binary files a/Day01-15/res/tel-start-number.png and b/Day01-15/res/tel-start-number.png differ diff --git a/Day01-15/res/telnet.png b/Day01-15/res/telnet.png index 20c8653..1aa4d4d 100644 Binary files a/Day01-15/res/telnet.png and b/Day01-15/res/telnet.png differ diff --git a/Day01-15/res/uml-components.png b/Day01-15/res/uml-components.png index 0394cd7..00598b8 100644 Binary files a/Day01-15/res/uml-components.png and b/Day01-15/res/uml-components.png differ diff --git a/Day01-15/res/uml-example.png b/Day01-15/res/uml-example.png index 14e8e9c..1638fe7 100644 Binary files a/Day01-15/res/uml-example.png and b/Day01-15/res/uml-example.png differ diff --git a/Day16-20/16-20.Python语言进阶.md b/Day16-20/16-20.Python语言进阶.md index 35a1486..62dba5d 100644 --- a/Day16-20/16-20.Python语言进阶.md +++ b/Day16-20/16-20.Python语言进阶.md @@ -1,1353 +1,1396 @@ ## Python语言进阶 -1. 数据结构和算法 - - - 算法:解决问题的方法和步骤 - - - 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。 - - - 渐近时间复杂度的大O标记: - - - 常量时间复杂度 - 布隆过滤器 / 哈希存储 - - - 对数时间复杂度 - 折半查找(二分查找) - - - 线性时间复杂度 - 顺序查找 / 桶排序 - - - 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序) - - - 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序) - - - 立方时间复杂度 - Floyd算法 / 矩阵乘法运算 - - - 几何级数时间复杂度 - 汉诺塔 - - - 阶乘时间复杂度 - 旅行经销商问题 - NP - - ![](./res/algorithm_complexity_1.png) - - ![](./res/algorithm_complexity_2.png) - - - 排序算法(选择、冒泡和归并)和查找算法(顺序和折半) - - ```Python - def select_sort(origin_items, comp=lambda x, y: x < y): - """简单选择排序""" - items = origin_items[:] - for i in range(len(items) - 1): - min_index = i - for j in range(i + 1, len(items)): - if comp(items[j], items[min_index]): - min_index = j - items[i], items[min_index] = items[min_index], items[i] - return items - ``` - - ```Python - def bubble_sort(origin_items, comp=lambda x, y: x > y): - """高质量冒泡排序(搅拌排序)""" - items = origin_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 swapped: - swapped = False - for j in range(len(items) - 2 - i, i, -1): - if comp(items[j - 1], items[j]): - items[j], items[j - 1] = items[j - 1], items[j] - swapped = True - if not swapped: - break - return items - ``` - - ```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): - """合并(将两个有序的列表合并成一个有序的列表)""" - items = [] - index1, index2 = 0, 0 - while index1 < len(items1) and index2 < len(items2): - if comp(items1[index1], items2[index2]): - items.append(items1[index1]) - index1 += 1 - else: - items.append(items2[index2]) - index2 += 1 - items += items1[index1:] - items += items2[index2:] - return items - ``` - - ```Python - def seq_search(items, key): - """顺序查找""" - for index, item in enumerate(items): - if item == key: - return index - return -1 - ``` - - ```Python - def bin_search(items, key): - """折半查找""" - start, end = 0, len(items) - 1 - while start <= end: - mid = (start + end) // 2 - if key > items[mid]: - start = mid + 1 - elif key < items[mid]: - end = mid - 1 - else: - return mid - 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)) - ``` - - - 常用算法: - - - 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。 - - 贪婪法 - 在对问题求解时,总是做出在当前看来 - - 最好的选择,不追求最优解,快速找到满意解。 - - 分治法 - 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到可以直接求解的程度,最后将子问题的解进行合并得到原问题的解。 - - 回溯法 - 回溯法又称为试探法,按选优条件向前搜索,当搜索到某一步发现原先选择并不优或达不到目标时,就退回一步重新选择。 - - 动态规划 - 基本思想也是将待求解问题分解成若干个子问题,先求解并保存这些子问题的解,避免产生大量的重复运算。 - - 穷举法例子:百钱百鸡和五人分鱼。 - - ```Python - # 公鸡5元一只 母鸡3元一只 小鸡1元三只 - # 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只 - for x in range(20): - for y in range(33): - z = 100 - x - y - if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0: - print(x, y, z) - - # A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉 - # 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份 - # B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份 - # 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼 - fish = 6 - while True: - total = fish - enough = True - for _ in range(5): - if (total - 1) % 5 == 0: - total = (total - 1) // 5 * 4 - else: - enough = False - break - if enough: - print(fish) - break - fish += 5 - ``` - - 贪婪法例子:假设小偷有一个背包,最多能装20公斤赃物,他闯入一户人家,发现如下表所示的物品。很显然,他不能把所有物品都装进背包,所以必须确定拿走哪些物品,留下哪些物品。 - - | 名称 | 价格(美元) | 重量(kg) | - | :----: | :----------: | :--------: | - | 电脑 | 200 | 20 | - | 收音机 | 20 | 4 | - | 钟 | 175 | 10 | - | 花瓶 | 50 | 2 | - | 书 | 10 | 1 | - | 油画 | 90 | 9 | - - ```Python - """ - 贪婪法:在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。 - 输入: - 20 6 - 电脑 200 20 - 收音机 20 4 - 钟 175 10 - 花瓶 50 2 - 书 10 1 - 油画 90 9 - """ - class Thing(object): - """物品""" - - def __init__(self, name, price, weight): - self.name = name - self.price = price - self.weight = weight - - @property - def value(self): - """价格重量比""" - return self.price / self.weight - - - def input_thing(): - """输入物品信息""" - name_str, price_str, weight_str = input().split() - return name_str, int(price_str), int(weight_str) - - - def main(): - """主函数""" - max_weight, num_of_things = map(int, input().split()) - all_things = [] - for _ in range(num_of_things): - all_things.append(Thing(*input_thing())) - all_things.sort(key=lambda x: x.value, reverse=True) - total_weight = 0 - total_price = 0 - for thing in all_things: - if total_weight + thing.weight <= max_weight: - print(f'小偷拿走了{thing.name}') - total_weight += thing.weight - total_price += thing.price - print(f'总价值: {total_price}美元') - - - if __name__ == '__main__': - main() - ``` - - 分治法例子:[快速排序](https://zh.wikipedia.org/zh/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F)。 - - ```Python - """ - 快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大 - """ - def quick_sort(origin_items, comp=lambda x, y: x <= y): - items = origin_items[:] - _quick_sort(items, 0, len(items) - 1, comp) - return items - - - def _quick_sort(items, start, end, comp): - if start < end: - pos = _partition(items, start, end, comp) - _quick_sort(items, start, pos - 1, comp) - _quick_sort(items, pos + 1, end, comp) - - - def _partition(items, start, end, comp): - pivot = items[end] - i = start - 1 - for j in range(start, end): - if comp(items[j], pivot): - i += 1 - items[i], items[j] = items[j], items[i] - items[i + 1], items[end] = items[end], items[i + 1] - return i + 1 - ``` - - 回溯法例子:[骑士巡逻](https://zh.wikipedia.org/zh/%E9%AA%91%E5%A3%AB%E5%B7%A1%E9%80%BB)。 - - ```Python - """ - 递归回溯法:叫称为试探法,按选优条件向前搜索,当搜索到某一步,发现原先选择并不优或达不到目标时,就退回一步重新选择,比较经典的问题包括骑士巡逻、八皇后和迷宫寻路等。 - """ - import sys - import time - - SIZE = 5 - total = 0 - - - def print_board(board): - for row in board: - for col in row: - print(str(col).center(4), end='') - print() - - - def patrol(board, row, col, step=1): - if row >= 0 and row < SIZE and \ - col >= 0 and col < SIZE and \ - board[row][col] == 0: - board[row][col] = step - if step == SIZE * SIZE: - global total - total += 1 - print(f'第{total}种走法: ') - print_board(board) - patrol(board, row - 2, col - 1, step + 1) - patrol(board, row - 1, col - 2, step + 1) - patrol(board, row + 1, col - 2, step + 1) - patrol(board, row + 2, col - 1, step + 1) - patrol(board, row + 2, col + 1, step + 1) - patrol(board, row + 1, col + 2, step + 1) - patrol(board, row - 1, col + 2, step + 1) - patrol(board, row - 2, col + 1, step + 1) - board[row][col] = 0 - - - def main(): - board = [[0] * SIZE for _ in range(SIZE)] - patrol(board, SIZE - 1, SIZE - 1) - - - if __name__ == '__main__': - 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、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如: - > - > 输入:1 -2 3 5 -3 2 - > - > 输出:8 - > - > 输入:0 -2 3 5 -1 2 - > - > 输出:9 - > - > 输入:-9 -2 -3 -5 -3 - > - > 输出:-2 - - ```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]) - - - if __name__ == '__main__': - main() - ``` - -2. 函数的使用方式 - - - 将函数视为“一等公民” - - - 函数可以赋值给变量 - - 函数可以作为函数的参数 - - 函数可以作为函数的返回值 - - - 高阶函数的用法(`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`函数) - - - 闭包和作用域问题 - - - Python搜索变量的LEGB顺序(Local --> Embedded --> Global --> Built-in) - - - `global`和`nonlocal`关键字的作用 - - `global`:声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域)。 - - `nonlocal`:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。 - - - 装饰器函数(使用装饰器和取消装饰器) - - 例子:输出函数执行时间的装饰器。 - - ```Python - def record_time(func): - """自定义装饰函数的装饰器""" - - @wraps(func) - def wrapper(*args, **kwargs): - start = time() - result = func(*args, **kwargs) - print(f'{func.__name__}: {time() - start}秒') - return result - - return wrapper - ``` - - 如果装饰器不希望跟`print`函数耦合,可以编写带参数的装饰器。 - - ```Python - from functools import wraps - from time import time - - - def record(output): - """自定义带参数的装饰器""" - - def decorate(func): - - @wraps(func) - def wrapper(*args, **kwargs): - start = time() - result = func(*args, **kwargs) - output(func.__name__, time() - start) - return result - - return wrapper - - return decorate - ``` - - ```Python - from functools import wraps - from time import time - - - class Record(): - """自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)""" - - def __init__(self, output): - self.output = output - - def __call__(self, func): - - @wraps(func) - def wrapper(*args, **kwargs): - start = time() - result = func(*args, **kwargs) - self.output(func.__name__, time() - start) - return result - - return wrapper - ``` - - > 说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。 - - 例子:用装饰器来实现单例模式。 - - ```Python - from functools import wraps - - - def singleton(cls): - """装饰类的装饰器""" - instances = {} - - @wraps(cls) - def wrapper(*args, **kwargs): - if cls not in instances: - instances[cls] = cls(*args, **kwargs) - return instances[cls] - - return wrapper - - - @singleton - class President(): - """总统(单例类)""" - pass - ``` - - > 说明:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢? - - ```Python - from functools import wraps - from threading import Lock - - - def singleton(cls): - """线程安全的单例装饰器""" - instances = {} - locker = Lock() - - @wraps(cls) - def wrapper(*args, **kwargs): - if cls not in instances: - with locker: - if cls not in instances: - instances[cls] = cls(*args, **kwargs) - return instances[cls] - - return wrapper - ``` - -3. 面向对象相关知识 - - - 三大支柱:封装、继承、多态 - - 例子:工资结算系统。 - - ```Python - """ - 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成 - """ - from abc import ABCMeta, abstractmethod - - - class Employee(metaclass=ABCMeta): - """员工(抽象类)""" - - def __init__(self, name): - self.name = name - - @abstractmethod - def get_salary(self): - """结算月薪(抽象方法)""" - pass - - - class Manager(Employee): - """部门经理""" - - def get_salary(self): - return 15000.0 - - - class Programmer(Employee): - """程序员""" - - def __init__(self, name, working_hour=0): - self.working_hour = working_hour - super().__init__(name) - - def get_salary(self): - return 200.0 * self.working_hour - - - class Salesman(Employee): - """销售员""" - - def __init__(self, name, sales=0.0): - self.sales = sales - super().__init__(name) - - def get_salary(self): - return 1800.0 + self.sales * 0.05 - - - 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 - - - def main(): - """主函数""" - emps = [ - EmployeeFactory.create('M', '曹操'), - EmployeeFactory.create('P', '荀彧', 120), - EmployeeFactory.create('P', '郭嘉', 85), - EmployeeFactory.create('S', '典韦', 123000), - ] - for emp in emps: - print('%s: %.2f元' % (emp.name, emp.get_salary())) - - - if __name__ == '__main__': - main() - ``` - - - 类与类之间的关系 - - - is-a关系:继承 - - has-a关系:关联 / 聚合 / 合成 - - use-a关系:依赖 - - 例子:扑克游戏。 - - ```Python - """ - 经验:符号常量总是优于字面常量,枚举类型是定义符号常量的最佳选择 - """ - from enum import Enum, unique - - import random - - - @unique - class Suite(Enum): - """花色""" - - SPADE, HEART, CLUB, DIAMOND = range(4) - - def __lt__(self, other): - return self.value < other.value - - - class Card(): - """牌""" - - def __init__(self, suite, face): - """初始化方法""" - self.suite = suite - self.face = face - - def show(self): - """显示牌面""" - 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() - - - class Poker(): - """扑克""" - - def __init__(self): - self.index = 0 - self.cards = [Card(suite, face) - for suite in Suite - for face in range(1, 14)] - - def shuffle(self): - """洗牌(随机乱序)""" - random.shuffle(self.cards) - self.index = 0 - - def deal(self): - """发牌""" - card = self.cards[self.index] - self.index += 1 - return card - - @property - def has_more(self): - return self.index < len(self.cards) - - - class Player(): - """玩家""" - - def __init__(self, name): - self.name = name - self.cards = [] - - def get_one(self, card): - """摸一张牌""" - self.cards.append(card) - - def sort(self, comp=lambda card: (card.suite, card.face)): - """整理手上的牌""" - self.cards.sort(key=comp) - - - def main(): - """主函数""" - poker = Poker() - poker.shuffle() - players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')] - while poker.has_more: - for player in players: - player.get_one(poker.deal()) - for player in players: - player.sort() - print(player.name, end=': ') - print(player.cards) - - - if __name__ == '__main__': - main() - ``` - - > 说明:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。 - - - 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆) - - - 垃圾回收、循环引用和弱引用 - - Python使用了自动化内存管理,这种管理机制以**引用计数**为基础,同时也引入了**标记-清除**和**分代收集**两种机制为辅的策略。 - - ```C - typedef struct_object { - /* 引用计数 */ - int ob_refcnt; - /* 对象指针 */ - struct_typeobject *ob_type; - } PyObject; - ``` - - ```C - /* 增加引用计数的宏定义 */ - #define Py_INCREF(op) ((op)->ob_refcnt++) - /* 减少引用计数的宏定义 */ - #define Py_DECREF(op) \ //减少计数 - if (--(op)->ob_refcnt != 0) \ - ; \ - else \ - __Py_Dealloc((PyObject *)(op)) - ``` - - 导致引用计数+1的情况: - - - 对象被创建,例如`a = 23` - - 对象被引用,例如`b = a` - - 对象被作为参数,传入到一个函数中,例如`f(a)` - - 对象作为一个元素,存储在容器中,例如`list1 = [a, a]` - - 导致引用计数-1的情况: - - - 对象的别名被显式销毁,例如`del a` - - 对象的别名被赋予新的对象,例如`a = 24` - - 一个对象离开它的作用域,例如f函数执行完毕时,f函数中的局部变量(全局变量不会) - - 对象所在的容器被销毁,或从容器中删除对象 - - 引用计数可能会导致循环引用问题,而循环引用会导致内存泄露,如下面的代码所示。为了解决这个问题,Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。 - - ```Python - # 循环引用会导致内存泄露 - Python除了引用技术还引入了标记清理和分代回收 - # 在Python 3.6以前如果重写__del__魔术方法会导致循环引用处理失效 - # 如果不想造成循环引用可以使用弱引用 - list1 = [] - list2 = [] - list1.append(list2) - list2.append(list1) - ``` - - 以下情况会导致垃圾回收: - - - 调用`gc.collect()` - - gc模块的计数器达到阀值 - - 程序退出 - - 如果循环引用中两个对象都定义了`__del__`方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。 - - 也可以通过`weakref`模块构造弱引用的方式来解决循环引用的问题。 - - - 魔法属性和方法(请参考《Python魔法方法指南》) - - 有几个小问题请大家思考: - - - 自定义的对象能不能使用运算符做运算? - - 自定义的对象能不能放到set中?能去重吗? - - 自定义的对象能不能作为dict的键? - - 自定义的对象能不能使用上下文语法? - - - 混入(Mixin) - - 例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。 - - ```Python - class SetOnceMappingMixin: - """自定义混入类""" - __slots__ = () - - def __setitem__(self, key, value): - if key in self: - raise KeyError(str(key) + ' already set') - return super().__setitem__(key, value) - - - class SetOnceDict(SetOnceMappingMixin, dict): - """自定义字典""" - pass - - - my_dict= SetOnceDict() - try: - my_dict['username'] = 'jackfrued' - my_dict['username'] = 'hellokitty' - except KeyError: - pass - print(my_dict) - ``` - - - 元编程和元类 - - 例子:用元类实现单例模式。 - - ```Python - import threading - - - class SingletonMeta(type): - """自定义元类""" - - def __init__(cls, *args, **kwargs): - cls.__instance = None - cls.__lock = threading.Lock() - super().__init__(*args, **kwargs) - - def __call__(cls, *args, **kwargs): - if cls.__instance is None: - with cls.__lock: - if cls.__instance is None: - cls.__instance = super().__call__(*args, **kwargs) - return cls.__instance - - - class President(metaclass=SingletonMeta): - """总统(单例类)""" - - pass - ``` - - - 面向对象设计原则 - - - 单一职责原则 (**S**RP)- 一个类只做该做的事情(类的设计要高内聚) - - 开闭原则 (**O**CP)- 软件实体应该对扩展开发对修改关闭 - - 依赖倒转原则(DIP)- 面向抽象编程(在弱类型语言中已经被弱化) - - 里氏替换原则(**L**SP) - 任何时候可以用子类对象替换掉父类对象 - - 接口隔离原则(**I**SP)- 接口要小而专不要大而全(Python中没有接口的概念) - - 合成聚合复用原则(CARP) - 优先使用强关联关系而不是继承关系复用代码 - - 最少知识原则(迪米特法则,Lo**D**)- 不要给没有必然联系的对象发消息 - - > 说明:上面加粗的字母放在一起称为面向对象的**SOLID**原则。 - - - GoF设计模式 - - - 创建型模式:单例、工厂、建造者、原型 - - 结构型模式:适配器、门面(外观)、代理 - - 行为型模式:迭代器、观察者、状态、策略 - - 例子:可插拔的哈希算法。 - - ```Python - class StreamHasher(): - """哈希摘要生成器(策略模式)""" - - def __init__(self, alg='md5', size=4096): - self.size = size - alg = alg.lower() - self.hasher = getattr(__import__('hashlib'), alg.lower())() - - def __call__(self, stream): - return self.to_digest(stream) - - def to_digest(self, stream): - """生成十六进制形式的摘要""" - for buf in iter(lambda: stream.read(self.size), b''): - self.hasher.update(buf) - return self.hasher.hexdigest() - - def main(): - """主函数""" - hasher1 = StreamHasher() - with open('Python-3.7.1.tgz', 'rb') as stream: - print(hasher1.to_digest(stream)) - hasher2 = StreamHasher('sha1') - with open('Python-3.7.1.tgz', 'rb') as stream: - print(hasher2(stream)) - - - if __name__ == '__main__': - main() - ``` - -4. 迭代器和生成器 - - - 和迭代器相关的魔术方法(`__iter__`和`__next__`) - - - 两种创建生成器的方式(生成器表达式和`yield`关键字) - - ```Python - def fib(num): - """生成器""" - a, b = 0, 1 - for _ in range(num): - a, b = b, a + b - yield a - - - class Fib(object): - """迭代器""" - - def __init__(self, num): - self.num = num - self.a, self.b = 0, 1 - self.idx = 0 - - def __iter__(self): - return self - - def __next__(self): - if self.idx < self.num: - self.a, self.b = self.b, self.a + self.b - self.idx += 1 - return self.a - raise StopIteration() - ``` - -5. 并发编程 - - Python中实现并发编程的三种方案:多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验;坏处在于并发的程序不容易开发和调试,同时对其他程序来说它并不友好。 - - - 多线程:Python中提供了Thread类并辅以Lock、Condition、Event、Semaphore和Barrier。Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。 - - ```Python - """ - 面试题:进程和线程的区别和联系? - 进程 - 操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程 - 线程 - 操作系统分配CPU的基本单位 - 并发编程(concurrent programming) - 1. 提升执行性能 - 让程序中没有因果关系的部分可以并发的执行 - 2. 改善用户体验 - 让耗时间的操作不会造成程序的假死 - """ - import glob - import os - import threading - - from PIL import Image - - PREFIX = 'thumbnails' - - - def generate_thumbnail(infile, size, format='PNG'): - """生成指定图片文件的缩略图""" - file, ext = os.path.splitext(infile) - file = file[file.rfind('/') + 1:] - outfile = f'{PREFIX}/{file}_{size[0]}_{size[1]}.{ext}' - img = Image.open(infile) - img.thumbnail(size, Image.ANTIALIAS) - img.save(outfile, format) - - - def main(): - """主函数""" - if not os.path.exists(PREFIX): - os.mkdir(PREFIX) - for infile in glob.glob('images/*.png'): - for size in (32, 64, 128): - # 创建并启动线程 - threading.Thread( - target=generate_thumbnail, - args=(infile, (size, size)) - ).start() - - - if __name__ == '__main__': - main() - ``` - - 多个线程竞争资源的情况 - - ```Python - """ - 多线程程序如果没有竞争资源处理起来通常也比较简单 - 当多个线程竞争临界资源的时候如果缺乏必要的保护措施就会导致数据错乱 - 说明:临界资源就是被多个线程竞争的资源 - """ - import time - import threading - - from concurrent.futures import ThreadPoolExecutor - - - class Account(object): - """银行账户""" - - def __init__(self): - self.balance = 0.0 - self.lock = threading.Lock() - - def deposit(self, money): - # 通过锁保护临界资源 - with self.lock: - new_balance = self.balance + money - time.sleep(0.001) - 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() - # 创建线程池 - 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) - # 关闭线程池 - pool.shutdown() - for future in futures: - future.result() - print(account.balance) - - - if __name__ == '__main__': - main() - ``` - - 修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用`threading`模块的Condition来实现线程调度,该对象也是基于锁来创建的,代码如下所示: - - ```Python - """ - 多个线程竞争一个资源 - 保护临界资源 - 锁(Lock/RLock) - 多个线程竞争多个资源(线程数>资源数) - 信号量(Semaphore) - 多个线程的调度 - 暂停线程执行/唤醒等待中的线程 - Condition - """ - from concurrent.futures import ThreadPoolExecutor - from random import randint - from time import sleep - - import threading - - - class Account(): - """银行账户""" - - def __init__(self, balance=0): - self.balance = balance - lock = threading.Lock() - self.condition = threading.Condition(lock) - - def withdraw(self, money): - """取钱""" - with self.condition: - while money > self.balance: - self.condition.wait() - new_balance = self.balance - money - sleep(0.001) - self.balance = new_balance - - def deposit(self, money): - """存钱""" - with self.condition: - new_balance = self.balance + money - sleep(0.001) - self.balance = new_balance - self.condition.notify_all() - - - def add_money(account): - while True: - money = randint(5, 10) - account.deposit(money) - print(threading.current_thread().name, - ':', money, '====>', account.balance) - sleep(0.5) - - - def sub_money(account): - while True: - money = randint(10, 30) - account.withdraw(money) - print(threading.current_thread().name, - ':', money, '<====', account.balance) - sleep(1) - - - def main(): - account = Account() - with ThreadPoolExecutor(max_workers=10) as pool: - for _ in range(5): - pool.submit(add_money, account) - pool.submit(sub_money, account) - - - if __name__ == '__main__': - main() - ``` - - - 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是Process,其他辅助的类跟threading模块中的类似,进程间共享数据可以使用管道、套接字等,在multiprocessing模块中有一个Queue类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。 - - ```Python - """ - 多进程和进程池的使用 - 多线程因为GIL的存在不能够发挥CPU的多核特性 - 对于计算密集型任务应该考虑使用多进程 - time python3 example22.py - real 0m11.512s - user 0m39.319s - sys 0m0.169s - 使用多进程后实际执行时间为11.512秒,而用户时间39.319秒约为实际执行时间的4倍 - 这就证明我们的程序通过多进程使用了CPU的多核特性,而且这台计算机配置了4核的CPU - """ - import concurrent.futures - import math - - PRIMES = [ - 1116281, - 1297337, - 104395303, - 472882027, - 533000389, - 817504243, - 982451653, - 112272535095293, - 112582705942171, - 112272535095293, - 115280095190773, - 115797848077099, - 1099726899285419 - ] * 5 - - - def is_prime(n): - """判断素数""" - if n % 2 == 0: - return False - - sqrt_n = int(math.floor(math.sqrt(n))) - for i in range(3, sqrt_n + 1, 2): - if n % i == 0: - return False - return True - - - def main(): - """主函数""" - with concurrent.futures.ProcessPoolExecutor() as executor: - for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)): - print('%d is prime: %s' % (number, prime)) - - - if __name__ == '__main__': - main() - ``` - - > 说明:**多线程和多进程的比较**。 - > - > 以下情况需要使用多线程: - > - > 1. 程序需要维护许多共享的状态(尤其是可变状态),Python中的列表、字典、集合都是线程安全的,所以使用线程而不是进程维护共享状态的代价相对较小。 - > 2. 程序会花费大量时间在I/O操作上,没有太多并行计算的需求且不需占用太多的内存。 - > - > 以下情况需要使用多进程: - > - > 1. 程序执行计算密集型任务(如:字节码操作、数据处理、科学计算)。 - > 2. 程序的输入可以并行的分成块,并且可以将运算结果合并。 - > 3. 程序在内存使用方面没有任何限制且不强依赖于I/O操作(如:读写文件、套接字等)。 - - - 异步处理:从调度程序的任务队列中挑选任务,该调度程序以交叉的形式执行这些任务,我们并不能保证任务将以某种顺序去执行,因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现,由于执行时间和顺序的不确定,因此需要通过回调式编程或者`future`对象来获取任务执行的结果。Python 3通过`asyncio`模块和`await`和`async`关键字(在Python 3.7中正式被列为关键字)来支持异步处理。 - - ```Python - """ - 异步I/O - async / await - """ - import asyncio - - - def num_generator(m, n): - """指定范围的数字生成器""" - yield from range(m, n + 1) - - - async def prime_filter(m, n): - """素数过滤器""" - primes = [] - for i in num_generator(m, n): - flag = True - for j in range(2, int(i ** 0.5 + 1)): - if i % j == 0: - flag = False - break - if flag: - print('Prime =>', i) - primes.append(i) - - await asyncio.sleep(0.001) - return tuple(primes) - - - async def square_mapper(m, n): - """平方映射器""" - squares = [] - for i in num_generator(m, n): - print('Square =>', i * i) - squares.append(i * i) - - await asyncio.sleep(0.001) - return squares - - - def main(): - """主函数""" - loop = asyncio.get_event_loop() - future = asyncio.gather(prime_filter(2, 100), square_mapper(1, 100)) - future.add_done_callback(lambda x: print(x.result())) - loop.run_until_complete(future) - loop.close() - - - if __name__ == '__main__': - main() - ``` - - > 说明:上面的代码使用`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 - import asyncio - import re - - import aiohttp - - PATTERN = re.compile(r'\(?P.*)\<\/title\>') - - - async def fetch_page(session, url): - async with session.get(url, ssl=False) as resp: - return await resp.text() - - - async def show_title(url): - async with aiohttp.ClientSession() as session: - html = await fetch_page(session, url) - print(PATTERN.search(html).group('title')) - - - def main(): - urls = ('https://www.python.org/', - 'https://git-scm.com/', - 'https://www.jd.com/', - '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)) - loop.close() - - - if __name__ == '__main__': - main() - ``` - - > 说明:**异步I/O与多进程的比较**。 - > - > 当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,asyncio就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑asyncio,它很适合编写没有实时数据处理需求的Web应用服务器。 - - Python还有很多用于处理并行任务的三方库,例如:joblib、PyMP等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。 - - 要实现任务的异步化,可以使用名为Celery的三方库。Celery是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。 +### 重要知识点 + +- 生成式(推导式)的用法 + + ```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)) + ``` + +### 数据结构和算法 + +- 算法:解决问题的方法和步骤 + +- 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。 + +- 渐近时间复杂度的大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*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!)" /> - 阶乘时间复杂度 - 旅行经销商问题 - NPC + + ![](./res/algorithm_complexity_1.png) + + ![](./res/algorithm_complexity_2.png) + +- 排序算法(选择、冒泡和归并)和查找算法(顺序和折半) + + ```Python + def select_sort(items, comp=lambda x, y: x < y): + """简单选择排序""" + items = items[:] + for i in range(len(items) - 1): + min_index = i + for j in range(i + 1, len(items)): + if comp(items[j], items[min_index]): + min_index = j + items[i], items[min_index] = items[min_index], items[i] + 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): + 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): + if comp(items[j], items[j + 1]): + items[j], items[j + 1] = items[j + 1], items[j] + swapped = True + if swapped: + swapped = False + for j in range(len(items) - 2 - i, i, -1): + if comp(items[j - 1], items[j]): + items[j], items[j - 1] = items[j - 1], items[j] + swapped = True + if not swapped: + break + return items + ``` + + ```Python + def merge(items1, items2, comp=lambda x, y: x < y): + """合并(将两个有序的列表合并成一个有序的列表)""" + items = [] + index1, index2 = 0, 0 + while index1 < len(items1) and index2 < len(items2): + if comp(items1[index1], items2[index2]): + items.append(items1[index1]) + index1 += 1 + else: + items.append(items2[index2]) + index2 += 1 + 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 + def seq_search(items, key): + """顺序查找""" + for index, item in enumerate(items): + if item == key: + return index + return -1 + ``` + + ```Python + def bin_search(items, key): + """折半查找""" + start, end = 0, len(items) - 1 + while start <= end: + mid = (start + end) // 2 + if key > items[mid]: + start = mid + 1 + elif key < items[mid]: + end = mid - 1 + else: + return mid + return -1 + ``` + +- 常用算法: + + - 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。 + - 贪婪法 - 在对问题求解时,总是做出在当前看来 + - 最好的选择,不追求最优解,快速找到满意解。 + - 分治法 - 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到可以直接求解的程度,最后将子问题的解进行合并得到原问题的解。 + - 回溯法 - 回溯法又称为试探法,按选优条件向前搜索,当搜索到某一步发现原先选择并不优或达不到目标时,就退回一步重新选择。 + - 动态规划 - 基本思想也是将待求解问题分解成若干个子问题,先求解并保存这些子问题的解,避免产生大量的重复运算。 + + 穷举法例子:百钱百鸡和五人分鱼。 + + ```Python + # 公鸡5元一只 母鸡3元一只 小鸡1元三只 + # 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只 + for x in range(20): + for y in range(33): + z = 100 - x - y + if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0: + print(x, y, z) + + # A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉 + # 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份 + # B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份 + # 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼 + fish = 6 + while True: + total = fish + enough = True + for _ in range(5): + if (total - 1) % 5 == 0: + total = (total - 1) // 5 * 4 + else: + enough = False + break + if enough: + print(fish) + break + fish += 5 + ``` + + 贪婪法例子:假设小偷有一个背包,最多能装20公斤赃物,他闯入一户人家,发现如下表所示的物品。很显然,他不能把所有物品都装进背包,所以必须确定拿走哪些物品,留下哪些物品。 + + | 名称 | 价格(美元) | 重量(kg) | + | :----: | :----------: | :--------: | + | 电脑 | 200 | 20 | + | 收音机 | 20 | 4 | + | 钟 | 175 | 10 | + | 花瓶 | 50 | 2 | + | 书 | 10 | 1 | + | 油画 | 90 | 9 | + + ```Python + """ + 贪婪法:在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。 + 输入: + 20 6 + 电脑 200 20 + 收音机 20 4 + 钟 175 10 + 花瓶 50 2 + 书 10 1 + 油画 90 9 + """ + class Thing(object): + """物品""" + + def __init__(self, name, price, weight): + self.name = name + self.price = price + self.weight = weight + + @property + def value(self): + """价格重量比""" + return self.price / self.weight + + + def input_thing(): + """输入物品信息""" + name_str, price_str, weight_str = input().split() + return name_str, int(price_str), int(weight_str) + + + def main(): + """主函数""" + max_weight, num_of_things = map(int, input().split()) + all_things = [] + for _ in range(num_of_things): + all_things.append(Thing(*input_thing())) + all_things.sort(key=lambda x: x.value, reverse=True) + total_weight = 0 + total_price = 0 + for thing in all_things: + if total_weight + thing.weight <= max_weight: + print(f'小偷拿走了{thing.name}') + total_weight += thing.weight + total_price += thing.price + print(f'总价值: {total_price}美元') + + + if __name__ == '__main__': + main() + ``` + + 分治法例子:[快速排序](https://zh.wikipedia.org/zh/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F)。 + + ```Python + """ + 快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大 + """ + def quick_sort(items, comp=lambda x, y: x <= y): + items = list(items)[:] + _quick_sort(items, 0, len(items) - 1, comp) + return items + + + def _quick_sort(items, start, end, comp): + if start < end: + pos = _partition(items, start, end, comp) + _quick_sort(items, start, pos - 1, comp) + _quick_sort(items, pos + 1, end, comp) + + + def _partition(items, start, end, comp): + pivot = items[end] + i = start - 1 + for j in range(start, end): + if comp(items[j], pivot): + i += 1 + items[i], items[j] = items[j], items[i] + items[i + 1], items[end] = items[end], items[i + 1] + return i + 1 + ``` + + 回溯法例子:[骑士巡逻](https://zh.wikipedia.org/zh/%E9%AA%91%E5%A3%AB%E5%B7%A1%E9%80%BB)。 + + ```Python + """ + 递归回溯法:叫称为试探法,按选优条件向前搜索,当搜索到某一步,发现原先选择并不优或达不到目标时,就退回一步重新选择,比较经典的问题包括骑士巡逻、八皇后和迷宫寻路等。 + """ + import sys + import time + + SIZE = 5 + total = 0 + + + def print_board(board): + for row in board: + for col in row: + print(str(col).center(4), end='') + print() + + + def patrol(board, row, col, step=1): + if row >= 0 and row < SIZE and \ + col >= 0 and col < SIZE and \ + board[row][col] == 0: + board[row][col] = step + if step == SIZE * SIZE: + global total + total += 1 + print(f'第{total}种走法: ') + print_board(board) + patrol(board, row - 2, col - 1, step + 1) + patrol(board, row - 1, col - 2, step + 1) + patrol(board, row + 1, col - 2, step + 1) + patrol(board, row + 2, col - 1, step + 1) + patrol(board, row + 2, col + 1, step + 1) + patrol(board, row + 1, col + 2, step + 1) + patrol(board, row - 1, col + 2, step + 1) + patrol(board, row - 2, col + 1, step + 1) + board[row][col] = 0 + + + def main(): + board = [[0] * SIZE for _ in range(SIZE)] + patrol(board, SIZE - 1, SIZE - 1) + + + if __name__ == '__main__': + main() + ``` + + 动态规划例子:子列表元素之和的最大值。 + + > 说明:子列表指的是列表中索引(下标)连续的元素构成的列表;列表中的元素是int类型,可能包含正整数、0、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如: + > + > 输入:1 -2 3 5 -3 2 + > + > 输出:8 + > + > 输入:0 -2 3 5 -1 2 + > + > 输出:9 + > + > 输入:-9 -2 -3 -5 -3 + > + > 输出:-2 + + ```Python + def main(): + items = list(map(int, input().split())) + 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() + ``` + + > **说明**:这个题目最容易想到的解法是使用二重循环,但是代码的时间性能将会变得非常的糟糕。使用动态规划的思想,仅仅是多用了两个变量,就将原来$O(N^2)$复杂度的问题变成了$O(N)$。 + +### 函数的使用方式 + +- 将函数视为“一等公民” + + - 函数可以赋值给变量 + - 函数可以作为函数的参数 + - 函数可以作为函数的返回值 + +- 高阶函数的用法(`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`函数) + +- 闭包和作用域问题 + + - Python搜索变量的LEGB顺序(Local >>> Embedded >>> Global >>> Built-in) + + - `global`和`nonlocal`关键字的作用 + + `global`:声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域)。 + + `nonlocal`:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。 + +- 装饰器函数(使用装饰器和取消装饰器) + + 例子:输出函数执行时间的装饰器。 + + ```Python + def record_time(func): + """自定义装饰函数的装饰器""" + + @wraps(func) + def wrapper(*args, **kwargs): + start = time() + result = func(*args, **kwargs) + print(f'{func.__name__}: {time() - start}秒') + return result + + return wrapper + ``` + + 如果装饰器不希望跟`print`函数耦合,可以编写可以参数化的装饰器。 + + ```Python + from functools import wraps + from time import time + + + def record(output): + """可以参数化的装饰器""" + + def decorate(func): + + @wraps(func) + def wrapper(*args, **kwargs): + start = time() + result = func(*args, **kwargs) + output(func.__name__, time() - start) + return result + + return wrapper + + return decorate + ``` + + ```Python + from functools import wraps + from time import time + + + class Record(): + """通过定义类的方式定义装饰器""" + + def __init__(self, output): + self.output = output + + def __call__(self, func): + + @wraps(func) + def wrapper(*args, **kwargs): + start = time() + result = func(*args, **kwargs) + self.output(func.__name__, time() - start) + return result + + return wrapper + ``` + + > **说明**:由于对带装饰功能的函数添加了@wraps装饰器,可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。 + + 例子:用装饰器来实现单例模式。 + + ```Python + from functools import wraps + + + def singleton(cls): + """装饰类的装饰器""" + instances = {} + + @wraps(cls) + def wrapper(*args, **kwargs): + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return wrapper + + + @singleton + class President: + """总统(单例类)""" + pass + ``` + + > **提示**:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢? + + 线程安全的单例装饰器。 + + ```Python + from functools import wraps + from threading import RLock + + + def singleton(cls): + """线程安全的单例装饰器""" + instances = {} + locker = RLock() + + @wraps(cls) + def wrapper(*args, **kwargs): + if cls not in instances: + with locker: + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return wrapper + ``` + + > **提示**:上面的代码用到了`with`上下文语法来进行锁操作,因为锁对象本身就是上下文管理器对象(支持`__enter__`和`__exit__`魔术方法)。在`wrapper`函数中,我们先做了一次不带锁的检查,然后再做带锁的检查,这样做比直接加锁检查性能要更好,如果对象已经创建就没有必须再去加锁而是直接返回该对象就可以了。 + +### 面向对象相关知识 + +- 三大支柱:封装、继承、多态 + + 例子:工资结算系统。 + + ```Python + """ + 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成 + """ + from abc import ABCMeta, abstractmethod + + + class Employee(metaclass=ABCMeta): + """员工(抽象类)""" + + def __init__(self, name): + self.name = name + + @abstractmethod + def get_salary(self): + """结算月薪(抽象方法)""" + pass + + + class Manager(Employee): + """部门经理""" + + def get_salary(self): + return 15000.0 + + + class Programmer(Employee): + """程序员""" + + def __init__(self, name, working_hour=0): + self.working_hour = working_hour + super().__init__(name) + + def get_salary(self): + return 200.0 * self.working_hour + + + class Salesman(Employee): + """销售员""" + + def __init__(self, name, sales=0.0): + self.sales = sales + super().__init__(name) + + def get_salary(self): + return 1800.0 + self.sales * 0.05 + + + class EmployeeFactory: + """创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)""" + + @staticmethod + def create(emp_type, *args, **kwargs): + """创建员工""" + 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(): + """主函数""" + emps = [ + EmployeeFactory.create('M', '曹操'), + EmployeeFactory.create('P', '荀彧', 120), + EmployeeFactory.create('P', '郭嘉', 85), + EmployeeFactory.create('S', '典韦', 123000), + ] + for emp in emps: + print(f'{emp.name}: {emp.get_salary():.2f}元') + + + if __name__ == '__main__': + main() + ``` + +- 类与类之间的关系 + + - is-a关系:继承 + - has-a关系:关联 / 聚合 / 合成 + - use-a关系:依赖 + + 例子:扑克游戏。 + + ```Python + """ + 经验:符号常量总是优于字面常量,枚举类型是定义符号常量的最佳选择 + """ + from enum import Enum, unique + + import random + + + @unique + class Suite(Enum): + """花色""" + + SPADE, HEART, CLUB, DIAMOND = range(4) + + def __lt__(self, other): + return self.value < other.value + + + class Card(): + """牌""" + + def __init__(self, suite, face): + """初始化方法""" + self.suite = suite + self.face = face + + def show(self): + """显示牌面""" + 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 __repr__(self): + return self.show() + + + class Poker(): + """扑克""" + + def __init__(self): + self.index = 0 + self.cards = [Card(suite, face) + for suite in Suite + for face in range(1, 14)] + + def shuffle(self): + """洗牌(随机乱序)""" + random.shuffle(self.cards) + self.index = 0 + + def deal(self): + """发牌""" + card = self.cards[self.index] + self.index += 1 + return card + + @property + def has_more(self): + return self.index < len(self.cards) + + + class Player(): + """玩家""" + + def __init__(self, name): + self.name = name + self.cards = [] + + def get_one(self, card): + """摸一张牌""" + self.cards.append(card) + + def sort(self, comp=lambda card: (card.suite, card.face)): + """整理手上的牌""" + self.cards.sort(key=comp) + + + def main(): + """主函数""" + poker = Poker() + poker.shuffle() + players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')] + while poker.has_more: + for player in players: + player.get_one(poker.deal()) + for player in players: + player.sort() + print(player.name, end=': ') + print(player.cards) + + + if __name__ == '__main__': + main() + ``` + + > **说明**:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。 + +- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆) + +- 垃圾回收、循环引用和弱引用 + + Python使用了自动化内存管理,这种管理机制以**引用计数**为基础,同时也引入了**标记-清除**和**分代收集**两种机制为辅的策略。 + + ```C + typedef struct_object { + /* 引用计数 */ + int ob_refcnt; + /* 对象指针 */ + struct_typeobject *ob_type; + } PyObject; + ``` + + ```C + /* 增加引用计数的宏定义 */ + #define Py_INCREF(op) ((op)->ob_refcnt++) + /* 减少引用计数的宏定义 */ + #define Py_DECREF(op) \ //减少计数 + if (--(op)->ob_refcnt != 0) \ + ; \ + else \ + __Py_Dealloc((PyObject *)(op)) + ``` + + 导致引用计数+1的情况: + + - 对象被创建,例如`a = 23` + - 对象被引用,例如`b = a` + - 对象被作为参数,传入到一个函数中,例如`f(a)` + - 对象作为一个元素,存储在容器中,例如`list1 = [a, a]` + + 导致引用计数-1的情况: + + - 对象的别名被显式销毁,例如`del a` + - 对象的别名被赋予新的对象,例如`a = 24` + - 一个对象离开它的作用域,例如f函数执行完毕时,f函数中的局部变量(全局变量不会) + - 对象所在的容器被销毁,或从容器中删除对象 + + 引用计数可能会导致循环引用问题,而循环引用会导致内存泄露,如下面的代码所示。为了解决这个问题,Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。 + + ```Python + # 循环引用会导致内存泄露 - Python除了引用技术还引入了标记清理和分代回收 + # 在Python 3.6以前如果重写__del__魔术方法会导致循环引用处理失效 + # 如果不想造成循环引用可以使用弱引用 + list1 = [] + list2 = [] + list1.append(list2) + list2.append(list1) + ``` + + 以下情况会导致垃圾回收: + + - 调用`gc.collect()` + - `gc`模块的计数器达到阀值 + - 程序退出 + + 如果循环引用中两个对象都定义了`__del__`方法,`gc`模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。 + + 也可以通过`weakref`模块构造弱引用的方式来解决循环引用的问题。 + +- 魔法属性和方法(请参考《Python魔法方法指南》) + + 有几个小问题请大家思考: + + - 自定义的对象能不能使用运算符做运算? + - 自定义的对象能不能放到`set`中?能去重吗? + - 自定义的对象能不能作为`dict`的键? + - 自定义的对象能不能使用上下文语法? + +- 混入(Mixin) + + 例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。 + + ```Python + class SetOnceMappingMixin: + """自定义混入类""" + __slots__ = () + + def __setitem__(self, key, value): + if key in self: + raise KeyError(str(key) + ' already set') + return super().__setitem__(key, value) + + + class SetOnceDict(SetOnceMappingMixin, dict): + """自定义字典""" + pass + + + my_dict= SetOnceDict() + try: + my_dict['username'] = 'jackfrued' + my_dict['username'] = 'hellokitty' + except KeyError: + pass + print(my_dict) + ``` + +- 元编程和元类 + + 对象是通过类创建的,类是通过元类创建的,元类提供了创建类的元信息。所有的类都直接或间接的继承自`object`,所有的元类都直接或间接的继承自`type`。 + + 例子:用元类实现单例模式。 + + ```Python + import threading + + + class SingletonMeta(type): + """自定义元类""" + + def __init__(cls, *args, **kwargs): + cls.__instance = None + cls.__lock = threading.RLock() + super().__init__(*args, **kwargs) + + def __call__(cls, *args, **kwargs): + if cls.__instance is None: + with cls.__lock: + if cls.__instance is None: + cls.__instance = super().__call__(*args, **kwargs) + return cls.__instance + + + class President(metaclass=SingletonMeta): + """总统(单例类)""" + + pass + ``` + +- 面向对象设计原则 + + - 单一职责原则 (**S**RP)- 一个类只做该做的事情(类的设计要高内聚) + - 开闭原则 (**O**CP)- 软件实体应该对扩展开发对修改关闭 + - 依赖倒转原则(DIP)- 面向抽象编程(在弱类型语言中已经被弱化) + - 里氏替换原则(**L**SP) - 任何时候可以用子类对象替换掉父类对象 + - 接口隔离原则(**I**SP)- 接口要小而专不要大而全(Python中没有接口的概念) + - 合成聚合复用原则(CARP) - 优先使用强关联关系而不是继承关系复用代码 + - 最少知识原则(迪米特法则,Lo**D**)- 不要给没有必然联系的对象发消息 + + > **说明**:上面加粗的字母放在一起称为面向对象的**SOLID**原则。 + +- GoF设计模式 + + - 创建型模式:单例、工厂、建造者、原型 + - 结构型模式:适配器、门面(外观)、代理 + - 行为型模式:迭代器、观察者、状态、策略 + + 例子:可插拔的哈希算法(策略模式)。 + + ```Python + class StreamHasher(): + """哈希摘要生成器""" + + def __init__(self, alg='md5', size=4096): + self.size = size + alg = alg.lower() + self.hasher = getattr(__import__('hashlib'), alg.lower())() + + def __call__(self, stream): + return self.to_digest(stream) + + def to_digest(self, stream): + """生成十六进制形式的摘要""" + for buf in iter(lambda: stream.read(self.size), b''): + self.hasher.update(buf) + return self.hasher.hexdigest() + + def main(): + """主函数""" + hasher1 = StreamHasher() + with open('Python-3.7.6.tgz', 'rb') as stream: + print(hasher1.to_digest(stream)) + hasher2 = StreamHasher('sha1') + with open('Python-3.7.6.tgz', 'rb') as stream: + print(hasher2(stream)) + + + if __name__ == '__main__': + main() + ``` + +### 迭代器和生成器 + +- 迭代器是实现了迭代器协议的对象。 + + - Python中没有像`protocol`或`interface`这样的定义协议的关键字。 + - Python中用魔术方法表示协议。 + - `__iter__`和`__next__`魔术方法就是迭代器协议。 + + ```Python + class Fib(object): + """迭代器""" + + def __init__(self, num): + self.num = num + self.a, self.b = 0, 1 + self.idx = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.idx < self.num: + self.a, self.b = self.b, self.a + self.b + self.idx += 1 + return self.a + raise StopIteration() + ``` + +- 生成器是语法简化版的迭代器。 + + ```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 + """ + 面试题:进程和线程的区别和联系? + 进程 - 操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程 + 线程 - 操作系统分配CPU的基本单位 + 并发编程(concurrent programming) + 1. 提升执行性能 - 让程序中没有因果关系的部分可以并发的执行 + 2. 改善用户体验 - 让耗时间的操作不会造成程序的假死 + """ + import glob + import os + import threading + + from PIL import Image + + PREFIX = 'thumbnails' + + + def generate_thumbnail(infile, size, format='PNG'): + """生成指定图片文件的缩略图""" + file, ext = os.path.splitext(infile) + file = file[file.rfind('/') + 1:] + outfile = f'{PREFIX}/{file}_{size[0]}_{size[1]}.{ext}' + img = Image.open(infile) + img.thumbnail(size, Image.ANTIALIAS) + img.save(outfile, format) + + + def main(): + """主函数""" + if not os.path.exists(PREFIX): + os.mkdir(PREFIX) + for infile in glob.glob('images/*.png'): + for size in (32, 64, 128): + # 创建并启动线程 + threading.Thread( + target=generate_thumbnail, + args=(infile, (size, size)) + ).start() + + + if __name__ == '__main__': + main() + ``` + + 多个线程竞争资源的情况。 + + ```Python + """ + 多线程程序如果没有竞争资源处理起来通常也比较简单 + 当多个线程竞争临界资源的时候如果缺乏必要的保护措施就会导致数据错乱 + 说明:临界资源就是被多个线程竞争的资源 + """ + import time + import threading + + from concurrent.futures import ThreadPoolExecutor + + + class Account(object): + """银行账户""" + + def __init__(self): + self.balance = 0.0 + self.lock = threading.Lock() + + def deposit(self, money): + # 通过锁保护临界资源 + with self.lock: + new_balance = self.balance + money + time.sleep(0.001) + 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() + # 创建线程池 + 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) + # 关闭线程池 + pool.shutdown() + for future in futures: + future.result() + print(account.balance) + + + if __name__ == '__main__': + main() + ``` + + 修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用`threading`模块的`Condition`来实现线程调度,该对象也是基于锁来创建的,代码如下所示: + + ```Python + """ + 多个线程竞争一个资源 - 保护临界资源 - 锁(Lock/RLock) + 多个线程竞争多个资源(线程数>资源数) - 信号量(Semaphore) + 多个线程的调度 - 暂停线程执行/唤醒等待中的线程 - Condition + """ + from concurrent.futures import ThreadPoolExecutor + from random import randint + from time import sleep + + import threading + + + class Account(): + """银行账户""" + + def __init__(self, balance=0): + self.balance = balance + lock = threading.Lock() + self.condition = threading.Condition(lock) + + def withdraw(self, money): + """取钱""" + with self.condition: + while money > self.balance: + self.condition.wait() + new_balance = self.balance - money + sleep(0.001) + self.balance = new_balance + + def deposit(self, money): + """存钱""" + with self.condition: + new_balance = self.balance + money + sleep(0.001) + self.balance = new_balance + self.condition.notify_all() + + + def add_money(account): + while True: + money = randint(5, 10) + account.deposit(money) + print(threading.current_thread().name, + ':', money, '====>', account.balance) + sleep(0.5) + + + def sub_money(account): + while True: + money = randint(10, 30) + account.withdraw(money) + print(threading.current_thread().name, + ':', money, '<====', account.balance) + sleep(1) + + + def main(): + account = Account() + with ThreadPoolExecutor(max_workers=10) as pool: + for _ in range(5): + pool.submit(add_money, account) + pool.submit(sub_money, account) + + + if __name__ == '__main__': + main() + ``` + +- 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是`Process`,其他辅助的类跟`threading`模块中的类似,进程间共享数据可以使用管道、套接字等,在`multiprocessing`模块中有一个`Queue`类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。 + + ```Python + """ + 多进程和进程池的使用 + 多线程因为GIL的存在不能够发挥CPU的多核特性 + 对于计算密集型任务应该考虑使用多进程 + time python3 example22.py + real 0m11.512s + user 0m39.319s + sys 0m0.169s + 使用多进程后实际执行时间为11.512秒,而用户时间39.319秒约为实际执行时间的4倍 + 这就证明我们的程序通过多进程使用了CPU的多核特性,而且这台计算机配置了4核的CPU + """ + import concurrent.futures + import math + + PRIMES = [ + 1116281, + 1297337, + 104395303, + 472882027, + 533000389, + 817504243, + 982451653, + 112272535095293, + 112582705942171, + 112272535095293, + 115280095190773, + 115797848077099, + 1099726899285419 + ] * 5 + + + def is_prime(n): + """判断素数""" + if n % 2 == 0: + return False + + sqrt_n = int(math.floor(math.sqrt(n))) + for i in range(3, sqrt_n + 1, 2): + if n % i == 0: + return False + return True + + + def main(): + """主函数""" + with concurrent.futures.ProcessPoolExecutor() as executor: + for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)): + print('%d is prime: %s' % (number, prime)) + + + if __name__ == '__main__': + main() + ``` + + > **重点**:**多线程和多进程的比较**。 + > + > 以下情况需要使用多线程: + > + > 1. 程序需要维护许多共享的状态(尤其是可变状态),Python中的列表、字典、集合都是线程安全的,所以使用线程而不是进程维护共享状态的代价相对较小。 + > 2. 程序会花费大量时间在I/O操作上,没有太多并行计算的需求且不需占用太多的内存。 + > + > 以下情况需要使用多进程: + > + > 1. 程序执行计算密集型任务(如:字节码操作、数据处理、科学计算)。 + > 2. 程序的输入可以并行的分成块,并且可以将运算结果合并。 + > 3. 程序在内存使用方面没有任何限制且不强依赖于I/O操作(如:读写文件、套接字等)。 + +- 异步处理:从调度程序的任务队列中挑选任务,该调度程序以交叉的形式执行这些任务,我们并不能保证任务将以某种顺序去执行,因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现,由于执行时间和顺序的不确定,因此需要通过回调式编程或者`future`对象来获取任务执行的结果。Python 3通过`asyncio`模块和`await`和`async`关键字(在Python 3.7中正式被列为关键字)来支持异步处理。 + + ```Python + """ + 异步I/O - async / await + """ + import asyncio + + + def num_generator(m, n): + """指定范围的数字生成器""" + yield from range(m, n + 1) + + + async def prime_filter(m, n): + """素数过滤器""" + primes = [] + for i in num_generator(m, n): + flag = True + for j in range(2, int(i ** 0.5 + 1)): + if i % j == 0: + flag = False + break + if flag: + print('Prime =>', i) + primes.append(i) + + await asyncio.sleep(0.001) + return tuple(primes) + + + async def square_mapper(m, n): + """平方映射器""" + squares = [] + for i in num_generator(m, n): + print('Square =>', i * i) + squares.append(i * i) + + await asyncio.sleep(0.001) + return squares + + + def main(): + """主函数""" + loop = asyncio.get_event_loop() + future = asyncio.gather(prime_filter(2, 100), square_mapper(1, 100)) + future.add_done_callback(lambda x: print(x.result())) + loop.run_until_complete(future) + loop.close() + + + if __name__ == '__main__': + main() + ``` + + > **说明**:上面的代码使用`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 + import asyncio + import re + + import aiohttp + + PATTERN = re.compile(r'\<title\>(?P<title>.*)\<\/title\>') + + + async def fetch_page(session, url): + async with session.get(url, ssl=False) as resp: + return await resp.text() + + + async def show_title(url): + async with aiohttp.ClientSession() as session: + html = await fetch_page(session, url) + print(PATTERN.search(html).group('title')) + + + def main(): + urls = ('https://www.python.org/', + 'https://git-scm.com/', + 'https://www.jd.com/', + 'https://www.taobao.com/', + 'https://www.douban.com/') + loop = asyncio.get_event_loop() + cos = [show_title(url) for url in urls] + loop.run_until_complete(asyncio.wait(cos)) + loop.close() + + + if __name__ == '__main__': + main() + ``` + + > **重点**:**异步I/O与多进程的比较**。 + > + > 当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,`asyncio`就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑`asyncio`,它很适合编写没有实时数据处理需求的Web应用服务器。 + + Python还有很多用于处理并行任务的三方库,例如:`joblib`、`PyMP`等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。 + + 要实现任务的异步化,可以使用名为`Celery`的三方库。`Celery`是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。 \ No newline at end of file diff --git a/Day16-20/res/algorithm_complexity_1.png b/Day16-20/res/algorithm_complexity_1.png index 952889d..2d57aed 100644 Binary files a/Day16-20/res/algorithm_complexity_1.png and b/Day16-20/res/algorithm_complexity_1.png differ diff --git a/Day16-20/res/algorithm_complexity_2.png b/Day16-20/res/algorithm_complexity_2.png index 4c14249..0ff66f3 100644 Binary files a/Day16-20/res/algorithm_complexity_2.png and b/Day16-20/res/algorithm_complexity_2.png differ diff --git a/Day21-30/21-30.Web前端概述.md b/Day21-30/21-30.Web前端概述.md index 928da6a..0ea3daf 100644 --- a/Day21-30/21-30.Web前端概述.md +++ b/Day21-30/21-30.Web前端概述.md @@ -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 @@ -903,4 +903,4 @@ Bulma是一个基于Flexbox的现代化的CSS框架,其初衷就是移动优 3. 可视化 - ![](./res/bootstrap-layoutit.png) \ No newline at end of file + ![](./res/bootstrap-layoutit.png) \ No newline at end of file diff --git a/Day21-30/code/垃圾分类查询/harmful-waste.png b/Day21-30/code/html/harmful-waste.png similarity index 100% rename from Day21-30/code/垃圾分类查询/harmful-waste.png rename to Day21-30/code/html/harmful-waste.png diff --git a/Day21-30/code/垃圾分类查询/垃圾分类.html b/Day21-30/code/html/index.html similarity index 100% rename from Day21-30/code/垃圾分类查询/垃圾分类.html rename to Day21-30/code/html/index.html diff --git a/Day21-30/code/垃圾分类查询/kitchen-waste.png b/Day21-30/code/html/kitchen-waste.png similarity index 100% rename from Day21-30/code/垃圾分类查询/kitchen-waste.png rename to Day21-30/code/html/kitchen-waste.png diff --git a/Day21-30/code/垃圾分类查询/other-waste.png b/Day21-30/code/html/other-waste.png similarity index 100% rename from Day21-30/code/垃圾分类查询/other-waste.png rename to Day21-30/code/html/other-waste.png diff --git a/Day21-30/code/垃圾分类查询/recyclable.png b/Day21-30/code/html/recyclable.png similarity index 100% rename from Day21-30/code/垃圾分类查询/recyclable.png rename to Day21-30/code/html/recyclable.png diff --git a/Day21-30/res/baidu_echarts.png b/Day21-30/res/baidu_echarts.png index e583e89..7144a46 100644 Binary files a/Day21-30/res/baidu_echarts.png and b/Day21-30/res/baidu_echarts.png differ diff --git a/Day21-30/res/bootstrap-layoutit.png b/Day21-30/res/bootstrap-layoutit.png index ae39347..53b4d86 100644 Binary files a/Day21-30/res/bootstrap-layoutit.png and b/Day21-30/res/bootstrap-layoutit.png differ diff --git a/Day21-30/res/browser-joke-1.png b/Day21-30/res/browser-joke-1.png deleted file mode 100644 index 0e3efd8..0000000 Binary files a/Day21-30/res/browser-joke-1.png and /dev/null differ diff --git a/Day21-30/res/browser-joke-2.png b/Day21-30/res/browser-joke-2.png deleted file mode 100644 index b3ae531..0000000 Binary files a/Day21-30/res/browser-joke-2.png and /dev/null differ diff --git a/Day21-30/res/browser-joke-3.png b/Day21-30/res/browser-joke-3.png deleted file mode 100644 index bd6028f..0000000 Binary files a/Day21-30/res/browser-joke-3.png and /dev/null differ diff --git a/Day21-30/res/dom-page.png b/Day21-30/res/dom-page.png deleted file mode 100644 index 2777e9c..0000000 Binary files a/Day21-30/res/dom-page.png and /dev/null differ diff --git a/Day21-30/res/dom-tree.png b/Day21-30/res/dom-tree.png deleted file mode 100644 index ce5e74e..0000000 Binary files a/Day21-30/res/dom-tree.png and /dev/null differ diff --git a/Day21-30/res/字体样式.png b/Day21-30/res/字体样式.png index 9baacb6..8f592bb 100644 Binary files a/Day21-30/res/字体样式.png and b/Day21-30/res/字体样式.png differ diff --git a/Day21-30/res/字符实体.png b/Day21-30/res/字符实体.png index 4a1fd12..ff4a01a 100644 Binary files a/Day21-30/res/字符实体.png and b/Day21-30/res/字符实体.png differ diff --git a/Day21-30/res/客户端对字体文件的支持.png b/Day21-30/res/客户端对字体文件的支持.png index aa93eeb..1a0e5b0 100644 Binary files a/Day21-30/res/客户端对字体文件的支持.png and b/Day21-30/res/客户端对字体文件的支持.png differ diff --git a/Day21-30/res/尺寸单位.png b/Day21-30/res/尺寸单位.png index 3b71674..47968e0 100644 Binary files a/Day21-30/res/尺寸单位.png and b/Day21-30/res/尺寸单位.png differ diff --git a/Day21-30/res/属性选择器.png b/Day21-30/res/属性选择器.png index f6ae4dc..06fef27 100644 Binary files a/Day21-30/res/属性选择器.png and b/Day21-30/res/属性选择器.png differ diff --git a/Day21-30/res/常用选择器.png b/Day21-30/res/常用选择器.png index bf86166..fe55a35 100644 Binary files a/Day21-30/res/常用选择器.png and b/Day21-30/res/常用选择器.png differ diff --git a/Day21-30/res/开始标签.png b/Day21-30/res/开始标签.png index 29bd455..6f79eb4 100644 Binary files a/Day21-30/res/开始标签.png and b/Day21-30/res/开始标签.png differ diff --git a/Day21-30/res/标签属性.png b/Day21-30/res/标签属性.png index 21eb12f..7eb42f0 100644 Binary files a/Day21-30/res/标签属性.png and b/Day21-30/res/标签属性.png differ diff --git a/Day21-30/res/样式属性.png b/Day21-30/res/样式属性.png index 476ef50..9b63b89 100644 Binary files a/Day21-30/res/样式属性.png and b/Day21-30/res/样式属性.png differ diff --git a/Day21-30/res/盒子模型.png b/Day21-30/res/盒子模型.png index a2517f3..af181ff 100644 Binary files a/Day21-30/res/盒子模型.png and b/Day21-30/res/盒子模型.png differ diff --git a/Day21-30/res/相对路径.png b/Day21-30/res/相对路径.png index b0e6ec9..f9b75ff 100644 Binary files a/Day21-30/res/相对路径.png and b/Day21-30/res/相对路径.png differ diff --git a/Day21-30/res/经典布局-1.png b/Day21-30/res/经典布局-1.png index 42f4b94..16dfe2b 100644 Binary files a/Day21-30/res/经典布局-1.png and b/Day21-30/res/经典布局-1.png differ diff --git a/Day21-30/res/经典布局-2.png b/Day21-30/res/经典布局-2.png index 089133b..349c6d0 100644 Binary files a/Day21-30/res/经典布局-2.png and b/Day21-30/res/经典布局-2.png differ diff --git a/Day21-30/res/结束标签.png b/Day21-30/res/结束标签.png index c80a3c2..2479512 100644 Binary files a/Day21-30/res/结束标签.png and b/Day21-30/res/结束标签.png differ diff --git a/Day21-30/res/网站地图.png b/Day21-30/res/网站地图.png index 55a4daf..744e7d9 100644 Binary files a/Day21-30/res/网站地图.png and b/Day21-30/res/网站地图.png differ diff --git a/Day21-30/res/衬线字体+非衬线字体+等宽字体.png b/Day21-30/res/衬线字体+非衬线字体+等宽字体.png index 7693210..1fd0818 100644 Binary files a/Day21-30/res/衬线字体+非衬线字体+等宽字体.png and b/Day21-30/res/衬线字体+非衬线字体+等宽字体.png differ diff --git a/Day21-30/res/选择器语法.png b/Day21-30/res/选择器语法.png index 54c159d..75021ef 100644 Binary files a/Day21-30/res/选择器语法.png and b/Day21-30/res/选择器语法.png differ diff --git a/Day31-35/31-35.玩转Linux操作系统.md b/Day31-35/31-35.玩转Linux操作系统.md index c519043..97a1040 100644 --- a/Day31-35/31-35.玩转Linux操作系统.md +++ b/Day31-35/31-35.玩转Linux操作系统.md @@ -111,7 +111,7 @@ Linux系统的命令通常都是如下所示的格式: Shell也被称为“壳”或“壳程序”,它是用户与操作系统内核交流的翻译官,简单的说就是人与计算机交互的界面和接口。目前很多Linux系统默认的Shell都是bash(<u>B</u>ourne <u>A</u>gain <u>SH</u>ell),因为它可以使用tab键进行命令和路径补全、可以保存历史命令、可以方便的配置环境变量以及执行批处理操作。 ```Shell - [root@izwz97tbgo9lkabnat2lo8z ~]# ps + [root ~]# ps PID TTY TIME CMD 3531 pts/0 00:00:00 bash 3553 pts/0 00:00:00 ps @@ -1283,7 +1283,7 @@ build environment: ### 计划任务 -1. 在指定的时间执行命令 +1. 在指定的时间执行命令。 - **at** - 将任务排队,在指定的时间执行。 - **atq** - 查看待执行的任务队列。 diff --git a/Day31-35/res/andrew.jpg b/Day31-35/res/andrew.jpg index 8f001d3..41f37a5 100644 Binary files a/Day31-35/res/andrew.jpg and b/Day31-35/res/andrew.jpg differ diff --git a/Day31-35/res/dmr.png b/Day31-35/res/dmr.png index 2453340..2ca20e8 100644 Binary files a/Day31-35/res/dmr.png and b/Day31-35/res/dmr.png differ diff --git a/Day31-35/res/file-mode.png b/Day31-35/res/file-mode.png index 294139a..f80d500 100644 Binary files a/Day31-35/res/file-mode.png and b/Day31-35/res/file-mode.png differ diff --git a/Day31-35/res/history-of-os.png b/Day31-35/res/history-of-os.png deleted file mode 100644 index 22262ec..0000000 Binary files a/Day31-35/res/history-of-os.png and /dev/null differ diff --git a/Day31-35/res/history-of-unix.png b/Day31-35/res/history-of-unix.png index 401b706..1da969e 100644 Binary files a/Day31-35/res/history-of-unix.png and b/Day31-35/res/history-of-unix.png differ diff --git a/Day31-35/res/ibm-col80-punched-card.png b/Day31-35/res/ibm-col80-punched-card.png index 467ce12..d075ec8 100644 Binary files a/Day31-35/res/ibm-col80-punched-card.png and b/Day31-35/res/ibm-col80-punched-card.png differ diff --git a/Day31-35/res/ken-and-dennis-pdp-11.png b/Day31-35/res/ken-and-dennis-pdp-11.png index 28ad0f7..2a2b510 100644 Binary files a/Day31-35/res/ken-and-dennis-pdp-11.png and b/Day31-35/res/ken-and-dennis-pdp-11.png differ diff --git a/Day31-35/res/ken_old.png b/Day31-35/res/ken_old.png index 5d322c4..7fd7853 100644 Binary files a/Day31-35/res/ken_old.png and b/Day31-35/res/ken_old.png differ diff --git a/Day31-35/res/ken_young.jpg b/Day31-35/res/ken_young.jpg index 46e3009..4bf8fa6 100644 Binary files a/Day31-35/res/ken_young.jpg and b/Day31-35/res/ken_young.jpg differ diff --git a/Day31-35/res/linus.png b/Day31-35/res/linus.png index acbe3bf..b2e1620 100644 Binary files a/Day31-35/res/linus.png and b/Day31-35/res/linus.png differ diff --git a/Day31-35/res/linux-network-config.png b/Day31-35/res/linux-network-config.png index a3b01e6..1db952c 100644 Binary files a/Day31-35/res/linux-network-config.png and b/Day31-35/res/linux-network-config.png differ diff --git a/Day31-35/res/pdp-11.jpg b/Day31-35/res/pdp-11.jpg index 6b4486b..13cca8e 100644 Binary files a/Day31-35/res/pdp-11.jpg and b/Day31-35/res/pdp-11.jpg differ diff --git a/Day31-35/res/pdp-7.png b/Day31-35/res/pdp-7.png index 8e7ab9e..8246dd0 100644 Binary files a/Day31-35/res/pdp-7.png and b/Day31-35/res/pdp-7.png differ diff --git a/Day31-35/res/vim-diff.png b/Day31-35/res/vim-diff.png index 5fde982..5951e02 100644 Binary files a/Day31-35/res/vim-diff.png and b/Day31-35/res/vim-diff.png differ diff --git a/Day31-35/res/vim-macro.png b/Day31-35/res/vim-macro.png index 33b5eed..e80ef3e 100644 Binary files a/Day31-35/res/vim-macro.png and b/Day31-35/res/vim-macro.png differ diff --git a/Day31-35/res/vim-multi-window.png b/Day31-35/res/vim-multi-window.png index c6e17d7..85ef359 100644 Binary files a/Day31-35/res/vim-multi-window.png and b/Day31-35/res/vim-multi-window.png differ diff --git a/Day36-40/36-38.关系型数据库MySQL.md b/Day36-40/36-38.关系型数据库MySQL.md index ce1abf2..e733595 100644 --- a/Day36-40/36-38.关系型数据库MySQL.md +++ b/Day36-40/36-38.关系型数据库MySQL.md @@ -174,7 +174,7 @@ MySQL在过去由于性能高、成本低、可靠性好,已经成为最流行 alter user 'root'@'localhost' identified by '123456'; ``` - > 说明:MySQL较新的版本默认不允许使用弱口令作为用户口令,所以我们通过上面的前两条命令修改了验证用户口令的策略和口令的长度。事实上我们不应该使用弱口令,因为存在用户口令被暴力破解的风险。近年来,攻击数据库窃取数据和劫持数据库勒索比特币的事件屡见不鲜,要避免这些潜在的风险,最为重要的一点是不要让数据库服务器暴露在公网上(最好的做法是将数据库置于内网,至少要做到不向公网开放数据库服务器的访问端口),另外要保管好`root`账号的口令,应用系统需要访问数据库时,通常不使用`root`账号进行访问,而是创建其他拥有适当权限的账号来访问。 + > **说明**:MySQL较新的版本默认不允许使用弱口令作为用户口令,所以我们通过上面的前两条命令修改了验证用户口令的策略和口令的长度。事实上我们不应该使用弱口令,因为存在用户口令被暴力破解的风险。近年来,攻击数据库窃取数据和劫持数据库勒索比特币的事件屡见不鲜,要避免这些潜在的风险,最为重要的一点是不要让数据库服务器暴露在公网上(最好的做法是将数据库置于内网,至少要做到不向公网开放数据库服务器的访问端口),另外要保管好`root`账号的口令,应用系统需要访问数据库时,通常不使用`root`账号进行访问,而是创建其他拥有适当权限的账号来访问。 再次使用客户端工具连接MySQL服务器时,就可以使用新设置的口令了。在实际开发中,为了方便用户操作,可以选择图形化的客户端工具来连接MySQL服务器,包括: @@ -936,15 +936,16 @@ drop index idx_student_name on tb_student; 创建视图。 ```SQL -create view vw_score +create view vw_avg_score as - select sid, round(avg(score), 1) as avgscore from tb_record group by sid; + select sid, round(avg(score), 1) as avgscore + from tb_record group by sid; create view vw_student_score as - select stuname, avgscore - from tb_student, vw_score - where stuid=sid; + select stuname, avgscore + from tb_student, vw_avg_score + where stuid=sid; ``` > **提示**:因为视图不包含数据,所以每次使用视图时,都必须执行查询以获得数据,如果你使用了连接查询、嵌套查询创建了较为复杂的视图,你可能会发现查询性能下降得很厉害。因此,在使用复杂的视图前,应该进行测试以确保其性能能够满足应用的需求。 @@ -1199,7 +1200,7 @@ insert into tb_emp values # 1. 创建数据库连接对象 con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', - user='root', password='123456') + user='yourname', password='yourpass') try: # 2. 通过连接对象获取游标 with con.cursor() as cursor: @@ -1231,7 +1232,7 @@ insert into tb_emp values no = int(input('编号: ')) con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', - user='root', password='123456', + user='yourname', password='yourpass', autocommit=True) try: with con.cursor() as cursor: @@ -1263,7 +1264,7 @@ insert into tb_emp values loc = input('所在地: ') con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', - user='root', password='123456', + user='yourname', password='yourpass', autocommit=True) try: with con.cursor() as cursor: @@ -1291,7 +1292,7 @@ insert into tb_emp values def main(): con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', - user='root', password='123456') + user='yourname', password='yourpass') try: with con.cursor(cursor=DictCursor) as cursor: cursor.execute('select dno as no, dname as name, dloc as loc from tb_dept') @@ -1334,7 +1335,7 @@ insert into tb_emp values size = int(input('大小: ')) con = pymysql.connect(host='localhost', port=3306, database='hrs', charset='utf8', - user='root', password='123456') + user='yourname', password='yourpass') try: with con.cursor() as cursor: cursor.execute( diff --git a/Day36-40/39-40.NoSQL入门.md b/Day36-40/39-40.NoSQL入门.md index c5fbe4c..35ab2ca 100644 --- a/Day36-40/39-40.NoSQL入门.md +++ b/Day36-40/39-40.NoSQL入门.md @@ -107,7 +107,7 @@ redis-server 方式一:通过参数指定认证口令和AOF持久化方式。 ```Shell -redis-server --requirepass 1qaz2wsx --appendonly yes +redis-server --requirepass yourpass --appendonly yes ``` 方式二:通过指定的配置文件来修改Redis的配置。 @@ -119,7 +119,7 @@ redis-server /root/redis-5.0.4/redis.conf 下面我们使用第一种方式来启动Redis并将其置于后台运行,将Redis产生的输出重定向到名为redis.log的文件中。 ```Shell -redis-server --requirepass 1qaz2wsx > redis.log & +redis-server --requirepass yourpass > redis.log & ``` 可以通过ps或者netstat来检查Redis服务器是否启动成功。 @@ -133,7 +133,7 @@ netstat -nap | grep redis-server ```Shell redis-cli -127.0.0.1:6379> auth 1qaz2wsx +127.0.0.1:6379> auth yourpass OK 127.0.0.1:6379> ping PONG @@ -274,7 +274,7 @@ python3 ```Python >>> import redis ->>> client = redis.Redis(host='1.2.3.4', port=6379, password='1qaz2wsx') +>>> client = redis.Redis(host='1.2.3.4', port=6379, password='yourpass') >>> client.set('username', 'admin') True >>> client.hset('student', 'name', 'hao') diff --git a/Day36-40/code/contact/main.py b/Day36-40/code/contact/main.py index 2e95a33..70ebb36 100644 --- a/Day36-40/code/contact/main.py +++ b/Day36-40/code/contact/main.py @@ -171,8 +171,8 @@ def find_contacters(con): def main(): - con = pymysql.connect(host='120.77.222.217', port=3306, - user='root', passwd='123456', + con = pymysql.connect(host='1.2.3.4', port=3306, + user='yourname', passwd='yourpass', db='address', charset='utf8', autocommit=True, cursorclass=pymysql.cursors.DictCursor) diff --git a/Day36-40/res/conceptual_model.png b/Day36-40/res/conceptual_model.png index 2b93a21..2516ba3 100644 Binary files a/Day36-40/res/conceptual_model.png and b/Day36-40/res/conceptual_model.png differ diff --git a/Day36-40/res/er_diagram.png b/Day36-40/res/er_diagram.png index e851a30..434216e 100644 Binary files a/Day36-40/res/er_diagram.png and b/Day36-40/res/er_diagram.png differ diff --git a/Day36-40/res/redis-aof.png b/Day36-40/res/redis-aof.png index bdd13c6..567c841 100644 Binary files a/Day36-40/res/redis-aof.png and b/Day36-40/res/redis-aof.png differ diff --git a/Day36-40/res/redis-bind-and-port.png b/Day36-40/res/redis-bind-and-port.png index 1abc58f..2c2fb85 100644 Binary files a/Day36-40/res/redis-bind-and-port.png and b/Day36-40/res/redis-bind-and-port.png differ diff --git a/Day36-40/res/redis-data-types.png b/Day36-40/res/redis-data-types.png index bb6d74e..a45625c 100644 Binary files a/Day36-40/res/redis-data-types.png and b/Day36-40/res/redis-data-types.png differ diff --git a/Day36-40/res/redis-databases.png b/Day36-40/res/redis-databases.png index d076640..b6f8a92 100644 Binary files a/Day36-40/res/redis-databases.png and b/Day36-40/res/redis-databases.png differ diff --git a/Day36-40/res/redis-hash.png b/Day36-40/res/redis-hash.png index 29329af..0d86fd0 100644 Binary files a/Day36-40/res/redis-hash.png and b/Day36-40/res/redis-hash.png differ diff --git a/Day36-40/res/redis-list.png b/Day36-40/res/redis-list.png index 2ccd893..00f23f3 100644 Binary files a/Day36-40/res/redis-list.png and b/Day36-40/res/redis-list.png differ diff --git a/Day36-40/res/redis-rdb-1.png b/Day36-40/res/redis-rdb-1.png index 55dcc61..b868bc5 100644 Binary files a/Day36-40/res/redis-rdb-1.png and b/Day36-40/res/redis-rdb-1.png differ diff --git a/Day36-40/res/redis-rdb-3.png b/Day36-40/res/redis-rdb-3.png index fd9983e..958702f 100644 Binary files a/Day36-40/res/redis-rdb-3.png and b/Day36-40/res/redis-rdb-3.png differ diff --git a/Day36-40/res/redis-replication.png b/Day36-40/res/redis-replication.png index c1f67a0..297b953 100644 Binary files a/Day36-40/res/redis-replication.png and b/Day36-40/res/redis-replication.png differ diff --git a/Day36-40/res/redis-security.png b/Day36-40/res/redis-security.png index 44c2fee..3c70471 100644 Binary files a/Day36-40/res/redis-security.png and b/Day36-40/res/redis-security.png differ diff --git a/Day36-40/res/redis-set.png b/Day36-40/res/redis-set.png index 1962b16..b9f15c3 100644 Binary files a/Day36-40/res/redis-set.png and b/Day36-40/res/redis-set.png differ diff --git a/Day36-40/res/redis-slow-logs.png b/Day36-40/res/redis-slow-logs.png index 474a1db..6cc46ce 100644 Binary files a/Day36-40/res/redis-slow-logs.png and b/Day36-40/res/redis-slow-logs.png differ diff --git a/Day36-40/res/redis-string.png b/Day36-40/res/redis-string.png index 7cef9df..60d9b5b 100644 Binary files a/Day36-40/res/redis-string.png and b/Day36-40/res/redis-string.png differ diff --git a/Day36-40/res/redis-zset.png b/Day36-40/res/redis-zset.png index d3df613..c87b486 100644 Binary files a/Day36-40/res/redis-zset.png and b/Day36-40/res/redis-zset.png differ diff --git a/Day41-55/41.快速上手.md b/Day41-55/41.快速上手.md index 76a9af0..2ee1ec2 100644 --- a/Day41-55/41.快速上手.md +++ b/Day41-55/41.快速上手.md @@ -33,13 +33,13 @@ HTTP响应(响应行+响应头+空行+消息体): ![](./res/http-response.png) -> 说明:这两张图是在2009年9月10日截取的,但愿这两张如同泛黄的照片般的截图能帮助你了解HTTP到底是什么样子的。 +> 说明:这两张图是在2009年9月10日凌晨获得的,但愿这两张如同泛黄的照片般的截图能帮助你了解HTTP到底是什么样子的。 ### Django概述 -Python的Web框架有上百个,比它的关键字还要多。所谓Web框架,就是用于开发Web服务器端应用的基础设施(通常指封装好的模块和一系列的工具)。事实上,即便没有Web框架,我们仍然可以通过socket或[CGI](https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3)来开发Web服务器端应用,但是这样做的成本和代价在实际开发中通常是不能接受的。通过Web框架,我们可以化繁为简,同时降低创建、更新、扩展应用程序的工作量。Python的Web框架中比较有名的有:Flask、Django、Tornado、Sanic、Pyramid、Bottle、Web2py、web.py等。 +Python的Web框架有上百个,比它的关键字还要多。所谓Web框架,就是用于开发Web服务器端应用的基础设施,说得通俗一点就是一系列封装好的模块和工具。事实上,即便没有Web框架,我们仍然可以通过socket或[CGI](https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3)来开发Web服务器端应用,但是这样做的成本和代价在商业项目中通常是不能接受的。通过Web框架,我们可以化繁为简,降低创建、更新、扩展应用程序的工作量。刚才我们说到Python有上百个Web框架,这些框架包括Django、Flask、Tornado、Sanic、Pyramid、Bottle、Web2py、web.py等。 -在基于Python的Web框架中,Django是所有重量级选手中最有代表性的一位,开发者可以基于Django快速的开发可靠的Web应用程序,因为它减少了Web开发中不必要的开销,对常用的设计和开发模式进行了封装,并对MVC架构提供了支持(MTV)。许多成功的网站和App都是基于Django框架构建的,国内比较有代表性的网站包括:知乎、豆瓣网、果壳网、搜狐闪电邮箱、101围棋网、海报时尚网、背书吧、堆糖、手机搜狐网、咕咚、爱福窝、果库等。 +在上述Python的Web框架中,Django无疑是最有代表性的重量级选手,开发者可以基于Django快速的开发可靠的Web应用程序,因为它减少了Web开发中不必要的开销,对常用的设计和开发模式进行了封装,并对MVC架构提供了支持(Django中称之为MTV架构)。许多成功的网站和应用都是基于Django框架构建的,国内比较有代表性的网站包括:知乎、豆瓣网、果壳网、搜狐闪电邮箱、101围棋网、海报时尚网、背书吧、堆糖、手机搜狐网、咕咚、爱福窝、果库等。 ![](./res/mvc.png) @@ -51,7 +51,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 1. 检查Python环境:Django 1.11需要Python 2.7或Python 3.4以上的版本;Django 2.0需要Python 3.4以上的版本;Django 2.1需要Python 3.5以上的版本。 - > 说明:我自己平时使用macOS做开发,macOS和Linux平台使用的命令跟Windows平台有较大的区别,这一点在之前也有过类似的说明,如果使用Windows平台做开发,替换一下对应的命令即可。 + > 说明:我自己平时使用macOS和Linux系统做开发,macOS和Linux系统在命令的使用上跟Windows系统还是有一些差别,如果使用Windows平台做开发,要使用Windows平台对应的命令。 ```Shell $ python3 --version @@ -64,66 +64,37 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 >>> sys.version_info ``` -2. 创建项目文件夹并切换到该目录,例如我们要实例一个OA(办公自动化)项目。 +2. 更新包管理工具并安装Django管理工具。 ```Shell - $ mkdir oa - $ cd oa + $ pip3 install -U pip + $ pip3 install django ``` -3. 创建并激活虚拟环境。 +3. 使用Django管理工具创建Django项目(项目名称为hellodjango)。 ```Shell - $ python3 -m venv venv - $ source venv/bin/activate + $ django-admin startproject hellodjango ``` > 说明:上面使用了Python自带的venv模块完成了虚拟环境的创建,当然也可以使用virtualenv或pipenv这样的工具。要激活虚拟环境,在Windows环境下可以通过"venv/Scripts/activate"执行批处理文件来实现。 -4. 更新包管理工具pip。 +4. 进入项目文件夹,创建并激活虚拟环境。 ```Shell - (venv)$ pip install -U pip + $ cd hellodjango + $ python3 -m venv venv + $ source venv/bin/activate ``` - 或 + > **提示**:上面使用了Python 3自带的`venv`模块来创建虚拟环境,当然也可以使用如`virtualenv`这样的三方工具来创建虚拟环境;激活虚拟环境后请注意终端中提示符的变化,在虚拟环境下使用Python解释器和包管理工具时,对应的命令是`python`和`pip`,而不再需要键入`python3`和`pip3`。 + +5. 在虚拟环境中安装项目依赖项。 ```Shell - (venv)$ python -m pip install -U pip - ``` - > 注意:请注意终端提示符发生的变化,前面的`(venv)`说明我们已经进入虚拟环境,而虚拟环境下的python和pip已经是Python 3的解释器和包管理工具了。 - -5. 安装Django。 - - ```Shell - (venv)$ pip install django + (venv)$ pip install django mysqlclient django-redis pillow requests ``` - 或指定版本号来安装对应的Django的版本。 - - ```Shell - (venv)$ pip install django==2.1.8 - ``` - -6. 检查Django的版本。 - - ```Shell - (venv)$ python -m django --version - (venv)$ django-admin --version - ``` - - 或 - - ```Shell - (venv)$ python - >>> import django - >>> django.get_version() - ``` - 当然,也可以通过pip来查看安装的依赖库及其版本,如: - - ```Shell - (venv)$ pip freeze - (venv)$ pip list - ``` + > **提示**:使用`pip`安装三方库时,可以通过如`django==1.11.27`的方式来指定三方库的版本。 下图展示了Django版本和Python版本的对应关系,如果在安装时没有指定版本号,将自动选择最新的版本(在写作这段内容时,Django最新的版本是2.2)。 @@ -135,25 +106,17 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 | 2.0 | 3.4、3.5、3.6、3.7 | | 2.1、2.2 | 3.5、3.6、3.7 | -7. 使用`django-admin`创建项目,项目命名为oa。 - - ```Shell - (venv)$ django-admin startproject oa . - ``` - - > 注意:上面的命令最后的那个点,它表示在当前路径下创建项目。 - - 执行上面的命令后看看生成的文件和文件夹,它们的作用如下所示: + 刚才创建的Django项目其文件和文件夹如下所示: - `manage.py`: 一个让你可以管理Django项目的工具程序。 - - `oa/__init__.py`:一个空文件,告诉Python解释器这个目录应该被视为一个Python的包。 - - `oa/settings.py`:Django项目的配置文件。 - - `oa/urls.py`:Django项目的URL声明(URL映射),就像是你的网站的“目录”。 - - `oa/wsgi.py`:项目运行在WSGI兼容Web服务器上的接口文件。 + - `hellodjango/__init__.py`:一个空文件,告诉Python解释器这个目录应该被视为一个Python的包。 + - `hellodjango/settings.py`:Django项目的配置文件。 + - `hellodjango/urls.py`:Django项目的URL声明(URL映射),就像是你的网站的“目录”。 + - `hellodjango/wsgi.py`:项目运行在WSGI兼容Web服务器上的接口文件。 > 说明:WSGI全称是Web服务器网关接口,维基百科上给出的解释是“为Python语言定义的[Web服务器](https://zh.wikipedia.org/wiki/%E7%B6%B2%E9%A0%81%E4%BC%BA%E6%9C%8D%E5%99%A8)和[Web应用程序](https://zh.wikipedia.org/wiki/%E7%BD%91%E7%BB%9C%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F)或框架之间的一种简单而通用的接口”。 -8. 启动服务器运行项目。 +6. 启动Django自带的服务器运行项目。 ```Shell (venv)$ python manage.py runserver @@ -161,21 +124,20 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 在浏览器中输入<http://127.0.0.1:8000>访问我们的服务器,效果如下图所示。 + > **说明1**:刚刚启动的是Django自带的用于开发和测试的服务器,它是一个用纯Python编写的轻量级Web服务器,但它并不是真正意义上的生产级别的服务器,千万不要将这个服务器用于和生产环境相关的任何地方。 + > + > **说明2**:用于开发的服务器在需要的情况下会对每一次的访问请求重新载入一遍Python代码。所以你不需要为了让修改的代码生效而频繁的重新启动服务器。然而,一些动作,比如添加新文件,将不会触发自动重新加载,这时你得自己手动重启服务器。 + > + > **说明3**:可以通过`python manage.py help`命令查看可用命令列表;在启动服务器时,也可以通过`python manage.py runserver 1.2.3.4:5678`来指定将服务器运行于哪个IP地址和端口。 + > + > **说明4**:可以通过Ctrl+C来终止服务器的运行。 + ![](./res/django-index-1.png) - - > 说明1:刚刚启动的是Django自带的用于开发和测试的服务器,它是一个用纯Python编写的轻量级Web服务器,但它并不是真正意义上的生产级别的服务器,千万不要将这个服务器用于和生产环境相关的任何地方。 - - > 说明2:用于开发的服务器在需要的情况下会对每一次的访问请求重新载入一遍Python代码。所以你不需要为了让修改的代码生效而频繁的重新启动服务器。然而,一些动作,比如添加新文件,将不会触发自动重新加载,这时你得自己手动重启服务器。 - - > 说明3:可以通过`python manage.py help`命令查看可用命令列表;在启动服务器时,也可以通过`python manage.py runserver 1.2.3.4:5678`来指定将服务器运行于哪个IP地址和端口。 - - > 说明4:可以通过Ctrl+C来终止服务器的运行。 - -9. 接下来我们修改项目的配置文件settings.py,Django是一个支持国际化和本地化的框架,因此刚才我们看到的默认首页也是支持国际化的,我们将默认语言修改为中文,时区设置为东八区。 +7. 修改项目的配置文件settings.py,Django是一个支持国际化和本地化的框架,因此刚才我们看到的默认首页也是支持国际化的,我们将默认语言修改为中文,时区设置为东八区。 ```Shell - (venv)$ vim oa/settings.py + (venv)$ vim hellodjango/settings.py ``` ```Python @@ -189,9 +151,10 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 # 此处省略下面的内容 ``` -10. 刷新刚才的页面。 + 刷新刚才的页面,可以看到修改语言代码和时区之后的结果。 + + ![](./res/django-index-2.png) - ![](./res/django-index-2.png) #### 动态页面 diff --git a/Day41-55/42.深入模型.md b/Day41-55/42.深入模型.md index 3ba9035..8b07012 100644 --- a/Day41-55/42.深入模型.md +++ b/Day41-55/42.深入模型.md @@ -29,10 +29,10 @@ 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'oa', - 'HOST': '127.0.0.1', + 'HOST': '1.2.3.4', 'PORT': 3306, - 'USER': 'root', - 'PASSWORD': '123456', + 'USER': 'yourname', + 'PASSWORD': 'yourpass', } } diff --git a/Day41-55/code/shop/shop/settings.py b/Day41-55/code/shop/shop/settings.py index f90e37a..deffdda 100644 --- a/Day41-55/code/shop/shop/settings.py +++ b/Day41-55/code/shop/shop/settings.py @@ -81,8 +81,8 @@ DATABASES = { 'NAME': 'shop', 'HOST': 'localhost', 'PORT': 3306, - 'USER': 'root', - 'PASSWORD': '123456', + 'USER': 'yourname', + 'PASSWORD': 'yourpass', } } diff --git a/Day41-55/code/shop_origin/shop/settings.py b/Day41-55/code/shop_origin/shop/settings.py index 337b099..53db105 100644 --- a/Day41-55/code/shop_origin/shop/settings.py +++ b/Day41-55/code/shop_origin/shop/settings.py @@ -81,8 +81,8 @@ DATABASES = { 'NAME': 'Shop', 'HOST': 'localhost', 'PORT': 3306, - 'USER': 'root', - 'PASSWORD': '123456', + 'USER': 'yourname', + 'PASSWORD': 'yourpass', } } diff --git a/Day41-55/res/CSRF.png b/Day41-55/res/CSRF.png index 6e87482..3108524 100644 Binary files a/Day41-55/res/CSRF.png and b/Day41-55/res/CSRF.png differ diff --git a/Day41-55/res/Django-Flowchart.png b/Day41-55/res/Django-Flowchart.png index ef96033..2ed0cfe 100644 Binary files a/Day41-55/res/Django-Flowchart.png and b/Day41-55/res/Django-Flowchart.png differ diff --git a/Day41-55/res/Django-MTV.png b/Day41-55/res/Django-MTV.png index 76f330e..b075e93 100644 Binary files a/Day41-55/res/Django-MTV.png and b/Day41-55/res/Django-MTV.png differ diff --git a/Day41-55/res/admin-login.png b/Day41-55/res/admin-login.png index 15ea232..6ccecb0 100644 Binary files a/Day41-55/res/admin-login.png and b/Day41-55/res/admin-login.png differ diff --git a/Day41-55/res/admin-model-create.png b/Day41-55/res/admin-model-create.png index 5d07891..ce3c78e 100644 Binary files a/Day41-55/res/admin-model-create.png and b/Day41-55/res/admin-model-create.png differ diff --git a/Day41-55/res/admin-model-delete-and-update.png b/Day41-55/res/admin-model-delete-and-update.png index 87d709c..2d02419 100644 Binary files a/Day41-55/res/admin-model-delete-and-update.png and b/Day41-55/res/admin-model-delete-and-update.png differ diff --git a/Day41-55/res/admin-model-depts.png b/Day41-55/res/admin-model-depts.png index 7214544..3a26d75 100644 Binary files a/Day41-55/res/admin-model-depts.png and b/Day41-55/res/admin-model-depts.png differ diff --git a/Day41-55/res/admin-model-emps-modified.png b/Day41-55/res/admin-model-emps-modified.png index 27c6205..d607c3a 100644 Binary files a/Day41-55/res/admin-model-emps-modified.png and b/Day41-55/res/admin-model-emps-modified.png differ diff --git a/Day41-55/res/admin-model-emps.png b/Day41-55/res/admin-model-emps.png index b9b7953..7b19f1b 100644 Binary files a/Day41-55/res/admin-model-emps.png and b/Day41-55/res/admin-model-emps.png differ diff --git a/Day41-55/res/admin-model-read.png b/Day41-55/res/admin-model-read.png index 135437b..6ba71ae 100644 Binary files a/Day41-55/res/admin-model-read.png and b/Day41-55/res/admin-model-read.png differ diff --git a/Day41-55/res/admin-model.png b/Day41-55/res/admin-model.png index 12fa9e0..78862bf 100644 Binary files a/Day41-55/res/admin-model.png and b/Day41-55/res/admin-model.png differ diff --git a/Day41-55/res/admin-welcome.png b/Day41-55/res/admin-welcome.png index 2371ffe..6ab06cf 100644 Binary files a/Day41-55/res/admin-welcome.png and b/Day41-55/res/admin-welcome.png differ diff --git a/Day41-55/res/captcha.png b/Day41-55/res/captcha.png index 59b8065..a0a5ce5 100644 Binary files a/Day41-55/res/captcha.png and b/Day41-55/res/captcha.png differ diff --git a/Day41-55/res/cookie_xstorage_indexeddb.png b/Day41-55/res/cookie_xstorage_indexeddb.png index a08c257..e21643f 100644 Binary files a/Day41-55/res/cookie_xstorage_indexeddb.png and b/Day41-55/res/cookie_xstorage_indexeddb.png differ diff --git a/Day41-55/res/django-index-1.png b/Day41-55/res/django-index-1.png index df4ba60..65ccc75 100644 Binary files a/Day41-55/res/django-index-1.png and b/Day41-55/res/django-index-1.png differ diff --git a/Day41-55/res/django-index-2.png b/Day41-55/res/django-index-2.png index 6b5edb9..7c19786 100644 Binary files a/Day41-55/res/django-index-2.png and b/Day41-55/res/django-index-2.png differ diff --git a/Day41-55/res/echarts_bar_graph.png b/Day41-55/res/echarts_bar_graph.png index a77571f..d2f40ea 100644 Binary files a/Day41-55/res/echarts_bar_graph.png and b/Day41-55/res/echarts_bar_graph.png differ diff --git a/Day41-55/res/er-graph.png b/Day41-55/res/er-graph.png index 9072a9d..0046537 100644 Binary files a/Day41-55/res/er-graph.png and b/Day41-55/res/er-graph.png differ diff --git a/Day41-55/res/http-request.png b/Day41-55/res/http-request.png index aca9287..67d0fb6 100644 Binary files a/Day41-55/res/http-request.png and b/Day41-55/res/http-request.png differ diff --git a/Day41-55/res/http-response.png b/Day41-55/res/http-response.png index f2b8ae3..0de1144 100644 Binary files a/Day41-55/res/http-response.png and b/Day41-55/res/http-response.png differ diff --git a/Day41-55/res/mvc.png b/Day41-55/res/mvc.png index 7ba14ba..b12ee5a 100644 Binary files a/Day41-55/res/mvc.png and b/Day41-55/res/mvc.png differ diff --git a/Day41-55/res/sessionid_from_cookie.png b/Day41-55/res/sessionid_from_cookie.png index 6dfc76e..23b40aa 100644 Binary files a/Day41-55/res/sessionid_from_cookie.png and b/Day41-55/res/sessionid_from_cookie.png differ diff --git a/Day41-55/res/show-depts.png b/Day41-55/res/show-depts.png index ceaecc8..65023a2 100644 Binary files a/Day41-55/res/show-depts.png and b/Day41-55/res/show-depts.png differ diff --git a/Day41-55/res/show_subjects.png b/Day41-55/res/show_subjects.png index 9e7612d..e4175e1 100644 Binary files a/Day41-55/res/show_subjects.png and b/Day41-55/res/show_subjects.png differ diff --git a/Day41-55/res/show_teachers.png b/Day41-55/res/show_teachers.png index 031ac57..66ba00a 100644 Binary files a/Day41-55/res/show_teachers.png and b/Day41-55/res/show_teachers.png differ diff --git a/Day41-55/res/web-application.png b/Day41-55/res/web-application.png index 89d2dec..5a18949 100644 Binary files a/Day41-55/res/web-application.png and b/Day41-55/res/web-application.png differ diff --git a/Day61-65/code/hello-tornado/example04.py b/Day61-65/code/hello-tornado/example04.py index 7b9204c..405b00c 100644 --- a/Day61-65/code/hello-tornado/example04.py +++ b/Day61-65/code/hello-tornado/example04.py @@ -14,8 +14,10 @@ from tornado.options import define, options, parse_command_line define('port', default=8888, type=int) +# 请求天行数据提供的API数据接口 REQ_URL = 'http://api.tianapi.com/guonei/' -API_KEY = '772a81a51ae5c780251b1f98ea431b84' +# 在天行数据网站注册后可以获得API_KEY +API_KEY = 'your_personal_api_key' class MainHandler(tornado.web.RequestHandler): diff --git a/Day61-65/code/hello-tornado/example05.py b/Day61-65/code/hello-tornado/example05.py index e2f6117..5ae1ced 100644 --- a/Day61-65/code/hello-tornado/example05.py +++ b/Day61-65/code/hello-tornado/example05.py @@ -14,8 +14,10 @@ from tornado.options import define, options, parse_command_line define('port', default=8888, type=int) +# 请求天行数据提供的API数据接口 REQ_URL = 'http://api.tianapi.com/guonei/' -API_KEY = '772a81a51ae5c780251b1f98ea431b84' +# 在天行数据网站注册后可以获得API_KEY +API_KEY = 'your_personal_api_key' class MainHandler(tornado.web.RequestHandler): diff --git a/Day61-65/code/hello-tornado/example06.py b/Day61-65/code/hello-tornado/example06.py index 7143351..bcb02bf 100644 --- a/Day61-65/code/hello-tornado/example06.py +++ b/Day61-65/code/hello-tornado/example06.py @@ -15,13 +15,13 @@ define('port', default=8888, type=int) async def connect_mysql(): return await aiomysql.connect( - host='120.77.222.217', + host='1.2.3.4', port=3306, db='hrs', charset='utf8', use_unicode=True, - user='root', - password='123456', + user='yourname', + password='yourpass', ) diff --git a/Day61-65/code/hello-tornado/example07.py b/Day61-65/code/hello-tornado/example07.py index e0d454c..df38980 100644 --- a/Day61-65/code/hello-tornado/example07.py +++ b/Day61-65/code/hello-tornado/example07.py @@ -21,13 +21,13 @@ define('port', default=8888, type=int) def get_mysql_connection(): return connect( - host='120.77.222.217', + host='1.2.3.4', port=3306, db='hrs', charset='utf8', use_unicode=True, - user='root', - password='123456', + user='yourname', + password='yourpass', ) diff --git a/Day61-65/code/project_of_tornado/backend_server.py b/Day61-65/code/project_of_tornado/backend_server.py index 610d301..973242f 100644 --- a/Day61-65/code/project_of_tornado/backend_server.py +++ b/Day61-65/code/project_of_tornado/backend_server.py @@ -19,13 +19,13 @@ from service.handlers.handlers_for_charts import ChartHandler async def connect_mysql(): return await aiomysql.connect( - host='120.77.222.217', + host='1.2.3.4', port=3306, db='hrs', charset='utf8', use_unicode=True, - user='root', - password='123456', + user='yourname', + password='yourpass', ) diff --git a/Day61-65/res/run-hello-world-app.png b/Day61-65/res/run-hello-world-app.png index f744358..a4bd7eb 100644 Binary files a/Day61-65/res/run-hello-world-app.png and b/Day61-65/res/run-hello-world-app.png differ diff --git a/Day61-65/res/websocket.png b/Day61-65/res/websocket.png index 7973399..880d6b4 100644 Binary files a/Day61-65/res/websocket.png and b/Day61-65/res/websocket.png differ diff --git a/Day61-65/res/ws_wss.png b/Day61-65/res/ws_wss.png index ac4b0e5..c71d386 100644 Binary files a/Day61-65/res/ws_wss.png and b/Day61-65/res/ws_wss.png differ diff --git a/Day66-75/res/api-image360.png b/Day66-75/res/api-image360.png index 2e4a406..6d21497 100644 Binary files a/Day66-75/res/api-image360.png and b/Day66-75/res/api-image360.png differ diff --git a/Day66-75/res/baidu-search-taobao.png b/Day66-75/res/baidu-search-taobao.png index d013ba8..b551aab 100644 Binary files a/Day66-75/res/baidu-search-taobao.png and b/Day66-75/res/baidu-search-taobao.png differ diff --git a/Day66-75/res/chrome-developer-tools.png b/Day66-75/res/chrome-developer-tools.png index aad8fc7..c4be32c 100644 Binary files a/Day66-75/res/chrome-developer-tools.png and b/Day66-75/res/chrome-developer-tools.png differ diff --git a/Day66-75/res/crawler-workflow.png b/Day66-75/res/crawler-workflow.png index 9adf5fd..4175285 100644 Binary files a/Day66-75/res/crawler-workflow.png and b/Day66-75/res/crawler-workflow.png differ diff --git a/Day66-75/res/douban-xpath.png b/Day66-75/res/douban-xpath.png index 3ecb737..9af1046 100644 Binary files a/Day66-75/res/douban-xpath.png and b/Day66-75/res/douban-xpath.png differ diff --git a/Day66-75/res/http-request.png b/Day66-75/res/http-request.png index aca9287..67d0fb6 100644 Binary files a/Day66-75/res/http-request.png and b/Day66-75/res/http-request.png differ diff --git a/Day66-75/res/http-response.png b/Day66-75/res/http-response.png index f2b8ae3..0de1144 100644 Binary files a/Day66-75/res/http-response.png and b/Day66-75/res/http-response.png differ diff --git a/Day66-75/res/image360-website.png b/Day66-75/res/image360-website.png index ffbb0d1..e394332 100644 Binary files a/Day66-75/res/image360-website.png and b/Day66-75/res/image360-website.png differ diff --git a/Day66-75/res/postman.png b/Day66-75/res/postman.png index 0e9b6ed..54159c2 100644 Binary files a/Day66-75/res/postman.png and b/Day66-75/res/postman.png differ diff --git a/Day66-75/res/redis-save.png b/Day66-75/res/redis-save.png index d721dd8..8cf5e75 100644 Binary files a/Day66-75/res/redis-save.png and b/Day66-75/res/redis-save.png differ diff --git a/Day66-75/res/scrapy-architecture.png b/Day66-75/res/scrapy-architecture.png index 5fe393f..5a96766 100644 Binary files a/Day66-75/res/scrapy-architecture.png and b/Day66-75/res/scrapy-architecture.png differ diff --git a/Day66-75/res/tesseract.gif b/Day66-75/res/tesseract.gif index 4df18a7..c5476f3 100644 Binary files a/Day66-75/res/tesseract.gif and b/Day66-75/res/tesseract.gif differ diff --git a/Day76-90/res/201205230001213115.png b/Day76-90/res/201205230001213115.png deleted file mode 100644 index 48525f6..0000000 Binary files a/Day76-90/res/201205230001213115.png and /dev/null differ diff --git a/Day76-90/res/201205230001238839.png b/Day76-90/res/201205230001238839.png deleted file mode 100644 index 0d0be92..0000000 Binary files a/Day76-90/res/201205230001238839.png and /dev/null differ diff --git a/Day76-90/res/20120523000125800.png b/Day76-90/res/20120523000125800.png deleted file mode 100644 index 4133386..0000000 Binary files a/Day76-90/res/20120523000125800.png and /dev/null differ diff --git a/Day76-90/res/result-in-jupyter.png b/Day76-90/res/result-in-jupyter.png index d2c3847..a98c76d 100644 Binary files a/Day76-90/res/result-in-jupyter.png and b/Day76-90/res/result-in-jupyter.png differ diff --git a/Day76-90/res/result1.png b/Day76-90/res/result1.png index d455137..9b1feca 100644 Binary files a/Day76-90/res/result1.png and b/Day76-90/res/result1.png differ diff --git a/Day76-90/res/result2.png b/Day76-90/res/result2.png index 05140f9..0398869 100644 Binary files a/Day76-90/res/result2.png and b/Day76-90/res/result2.png differ diff --git a/Day76-90/res/result3.png b/Day76-90/res/result3.png index aa9c81d..a9ebe77 100644 Binary files a/Day76-90/res/result3.png and b/Day76-90/res/result3.png differ diff --git a/Day76-90/res/result4.png b/Day76-90/res/result4.png index 8081ea0..a02e7f1 100644 Binary files a/Day76-90/res/result4.png and b/Day76-90/res/result4.png differ diff --git a/Day76-90/res/result5.png b/Day76-90/res/result5.png index 53f0e1d..178ed9a 100644 Binary files a/Day76-90/res/result5.png and b/Day76-90/res/result5.png differ diff --git a/Day76-90/res/result6.png b/Day76-90/res/result6.png index 576c80d..63ac845 100644 Binary files a/Day76-90/res/result6.png and b/Day76-90/res/result6.png differ diff --git a/Day76-90/res/result7.png b/Day76-90/res/result7.png index 8f3d9fa..c8c2e26 100644 Binary files a/Day76-90/res/result7.png and b/Day76-90/res/result7.png differ diff --git a/Day76-90/res/result8.png b/Day76-90/res/result8.png index 44a2d8b..694b796 100644 Binary files a/Day76-90/res/result8.png and b/Day76-90/res/result8.png differ diff --git a/Day76-90/res/result9.png b/Day76-90/res/result9.png index d9a77c2..bb6bbc3 100644 Binary files a/Day76-90/res/result9.png and b/Day76-90/res/result9.png differ diff --git a/Day91-100/100.Python面试题集.md b/Day91-100/100.Python面试题集.md index c00a910..9e12dba 100644 --- a/Day91-100/100.Python面试题集.md +++ b/Day91-100/100.Python面试题集.md @@ -2,14 +2,24 @@ 1. 说一说Python中的新式类和旧式类有什么区别。 + 答: + 2. Python中`is`运算符和`==`运算符有什么区别? + 答:请参考[《那些年我们踩过的那些坑》](../番外篇/那些年我们踩过的那些坑.md)。 + 3. Python中如何动态设置和获取对象属性? + 答:`setattr(object, name, value)`和`getattr(object, name[, default])`内置函数,其中`object`是对象,`name`是对象的属性名,`value`是属性值。这两个函数会调用对象的`__getattr__`和`__setattr__`魔术方法。 + 4. Python如何实现内存管理?有没有可能出现内存泄露的问题? + 答: + 5. 阐述列表和集合的底层实现原理。 + 答: + 6. 现有字典`d = {'a': 24, 'g': 52, 'i': 12, 'k': 33}`,如何按字典中的值对字典进行排序得到排序后的字典。 答: @@ -59,14 +69,10 @@ ''.join(reversed('hello')) ``` - 或 - ```Python 'hello'[::-1] ``` - 或 - ```Python def reverse(content): return ''.join(content[i] for i in range(len(content) - 1, -1, -1)) @@ -74,8 +80,6 @@ reverse('hello') ``` - 或 - ```Python def reverse(content): return reverse(content[1:]) + content[0] if len(content) > 1 else content @@ -98,6 +102,12 @@ 11. 写一个返回bool值的函数,判断给定的非负整数是不是回文数。 + 答: + + ```Python + + ``` + 12. 用一行代码实现求任意非负整数的阶乘。 答: @@ -118,18 +128,56 @@ 14. 删除列表中的重复元素并保留原有的顺序。 + 答: + + ```Python + + ``` + 15. 找出两个列表中的相同元素和不同元素。 + 答: + 16. 列表中的某个元素出现次数占列表元素总数的半数以上,找出这个元素。 + 答: + + ```Python + + ``` + 17. 实现对有序列表进行二分查找的算法。 + 答: + + ```Python + + ``` + 18. 输入年月日,输出这一天是这一年的第几天。 + 答: + + ```Python + + ``` + 19. 统计一个字符串中各个字符出现的次数。 + 答: + + ```Python + + ``` + 20. 在Python中如何实现单例模式? + 答: + + ```Python + + ``` + 21. 下面的代码会输出什么。 ```Python @@ -151,8 +199,20 @@ 22. 实现一个记录函数执行时间的装饰器。 + 答: + + ```Python + + ``` + 23. 写一个遍历指定目录下指定后缀名的文件的函数。 + 答: + + ```Python + + ``` + 24. 有如下所示的字典,请将其转换为CSV格式。 转换前: @@ -199,30 +259,60 @@ 27. 正则表达式对象的`search`和`match`方法有什么区别? + 答: + 28. 当做个线程竞争一个对象且该对象并非线程安全的时候应该怎么办? + 答: + 29. 说一下死锁产生的条件以及如何避免死锁的发生。 + 答: + 30. 请阐述TCP的优缺点。 + 答: + 31. HTTP请求的GET和POST有什么区别? + 答: + 32. 说一些你知道的HTTP响应状态码。 + 答: + 33. 简单阐述HTTPS的工作原理。 + 答: + 34. 阐述Django项目中一个请求的生命周期。 + 答: + 35. Django项目中实现数据接口时如何解决跨域问题。 + 答: + 36. Django项目中如何对接Redis高速缓存服务。 + 答: + 37. 请说明Cookie和Session之间的关系。 + 答: + 38. 说一下索引的原理和作用。 + 答: + 39. 是否使用过Nginx实现负载均衡?用过哪些负载均衡算法? + 答: + 40. 一个保存整数(int)的数组,除了一个元素出现过1次外,其他元素都出现过两次,请找出这个元素。 -41. 有12个外观相同的篮球,其中1个的重要和其他11个的重量不同(有可能轻有可能重),现在有一个天平可以使用,怎样才能通过最少的称重次数找出这颗与众不同的球。 \ No newline at end of file + 答: + +41. 有12个外观相同的篮球,其中1个的重要和其他11个的重量不同(有可能轻有可能重),现在有一个天平可以使用,怎样才能通过最少的称重次数找出这颗与众不同的球。 + + 答: \ No newline at end of file diff --git a/Day91-100/91.团队项目开发的问题和解决方案.md b/Day91-100/91.团队项目开发的问题和解决方案.md index 428382d..df2ac87 100644 --- a/Day91-100/91.团队项目开发的问题和解决方案.md +++ b/Day91-100/91.团队项目开发的问题和解决方案.md @@ -382,6 +382,12 @@ Git不像SVN那样一定需要中央服务器才能工作,上面我们演示 git switch -c <branch-name> ``` + 或 + + ```Shell + git checkout -b <branch-name> + ``` + 3. 在自己的分支上开发并在本地做版本控制。 4. 将自己的分支(工作成果)推到服务器。 @@ -421,21 +427,21 @@ Git不像SVN那样一定需要中央服务器才能工作,上面我们演示 创建`feature`分支: ```Shell - git switch -c myfeature develop + git switch -c feature/user develop ``` 或 ```Shell - git checkout -b myfeature develop + git checkout -b feature/user develop ``` - 将`feature`分支合并到`develop`分支: + 接下来就是在`feature`分支上进行开发并实施版本控制,这一段如何操作我们就不再赘述了。工作完成后,将`feature`分支合并到`develop`分支: ```Shell git checkout develop - git merge --no-ff myfeature - git branch -d myfeature + git merge --no-ff feature/user + git branch -d feature/user git push origin develop ``` @@ -444,7 +450,7 @@ Git不像SVN那样一定需要中央服务器才能工作,上面我们演示 创建`release`分支: ```Shell - git switch -c release-0.1 develop + git checkout -b release-0.1 develop git push -u origin release-0.1 ... ... ... git pull @@ -497,7 +503,7 @@ Git不像SVN那样一定需要中央服务器才能工作,上面我们演示 git push --tags ``` -Git-flow流程比较容易控制各个分支的状况,但是在运用上github-flow要复杂得多,因此实际使用的时候通常会安装名为`gitflow`的命令行工具或者使用图形化的Git工具(如:SmartGit、SourceTree等)来简化操作,具体的可以参考[《git-flow 的工作流程》](<https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow>)一文,因为这篇文章写得已经很好了,本文不再进行赘述。 +Git-flow流程比较容易控制各个分支的状况,但是在运用上github-flow要复杂得多,因此实际使用的时候通常会安装名为`gitflow`的命令行工具(Windows环境的Git自带了该工具)或者使用图形化的Git工具(如:SmartGit、SourceTree等)来简化操作,具体的可以参考[《git-flow 的工作流程》](<https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow>)一文,因为这篇文章写得已经很好了,本文不再进行赘述。 ### 缺陷管理 @@ -541,7 +547,7 @@ tar -xvf ZenTaoPMS.pro8.5.2.zbox_64.tar 对敏捷开发以及敏捷闭环工具不是特别了解的,可以参考[《基于JIRA的Scrum敏捷开发的项目管理》](<https://blog.51cto.com/newthink/1775427>)一文。 -#### Gitlab +#### GitLab 常用的代码托管平台和之前提到的Git私服Gitlab都提供了缺陷管理的功能,当我们要报告一个bug时,可以在如下图所示的界面创建一个新的问题票(issue ticket)。填写的内容包括: @@ -577,9 +583,7 @@ tar -xvf ZenTaoPMS.pro8.5.2.zbox_64.tar ![](./res/jenkins_new_project.png) -持续集成对于编译型语言的意义更大,对于Python这样的解释型语言,更多的时候是用于对接版本控制系统触发自动化测试并产生相应的报告。类似的功能也可以通过在Git服务上配置**Webhook**来完成,码云甚至可以直接对接[钉钉开放平台](<https://ding-doc.dingtalk.com/>)使用钉钉机器人来向项目相关人员发送即时消息。Gitlab也对CI和CD(持续交付)提供了支持,具体内容请大家参考[《GitLab CI/CD基础教程》](<https://blog.stdioa.com/2018/06/gitlab-cicd-fundmental/>)。 - - +持续集成对于编译型语言的意义更大,对于Python这样的解释型语言,更多的时候是用于对接版本控制系统触发自动化测试并产生相应的报告,类似的功能也可以通过配置**Webhook**来完成。如果要通过Docker这样的虚拟化容器进行项目打包部署或者通过K8S进行容器管理,可以在持续集成平台安装对应的插件来支持这些功能。码云甚至可以直接对接[钉钉开放平台](<https://ding-doc.dingtalk.com/>)使用钉钉机器人来向项目相关人员发送即时消息。GitLab也对CI和CD(持续交付)提供了支持,具体内容请大家参考[《GitLab CI/CD基础教程》](<https://blog.stdioa.com/2018/06/gitlab-cicd-fundmental/>)。 > **说明**: > diff --git a/Day91-100/92.使用Docker部署应用.md b/Day91-100/92.Docker容器详解.md similarity index 72% rename from Day91-100/92.使用Docker部署应用.md rename to Day91-100/92.Docker容器详解.md index 143b110..ebea4c6 100644 --- a/Day91-100/92.使用Docker部署应用.md +++ b/Day91-100/92.Docker容器详解.md @@ -1,4 +1,6 @@ -## 使用Docker部署应用 +## Docker容器详解 + +Docker是基于Go语言开发的开源应用容器引擎,遵从Apache Licence 2.0协议,可以让开发者打包应用以及应用的依赖包到一个可移植的容器中,然后发布到各种发行版本的Linux系统上。 ### Docker简介 @@ -8,7 +10,7 @@ 虚拟机(virtual machine)就是带环境安装的一种解决方案,它可以在一种操作系统里面运行另一种操作系统,比如在Windows系统里面运行Linux系统,在macOS上运行Windows,而应用程序对此毫无感知。使用过虚拟机的人都知道,虚拟机用起来跟真实系统一模一样,而对于虚拟机的宿主系统来说,虚拟机就是一个普通文件,不需要了就删掉,对宿主系统或者其他的程序并没有影响。但是虚拟机通常会占用较多的系统资源,启动和关闭也非常的缓慢,总之用户体验并没有想象中的那么好。 -Docker属于对Linux容器技术的一种封装(利用了Linux的namespace和cgroup技术),它提供了简单易用的容器使用接口,是目前最流行的 Linux 容器解决方案。Docker将应用程序与该程序的依赖打包在一个文件里面,运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。下图是虚拟机和容器的对比,左边是传统的虚拟机,右边是Docker。 +Docker属于对Linux容器技术(LXC)的一种封装(利用了Linux的namespace和cgroup技术),它提供了简单易用的容器使用接口,是目前最流行的 Linux 容器解决方案。Docker将应用程序与该程序的依赖打包在一个文件里面,运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。下图是虚拟机和容器的对比,左边是传统的虚拟机,右边是Docker。 ![](./res/docker_vs_vm.png) @@ -22,26 +24,49 @@ Docker属于对Linux容器技术的一种封装(利用了Linux的namespace和c 下面以CentOS为例讲解如何安装Docker,使用[Ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/)、[macOS](https://docs.docker.com/docker-for-mac/install/)或[Windows](https://docs.docker.com/docker-for-windows/install/)的用户可以通过点击对应的链接了解这些平台下如何进行安装。 -1. 确定操作系统内核版本(CentOS 7要求64位,内核版本3.10+;CentOS 6要求64位,内核版本2.6+),可以通过下面的命令确定Linux系统内核版本并更新底层库文件。 +1. 确定操作系统内核版本(CentOS 7要求64位,内核版本3.10+;CentOS 6要求64位,内核版本2.6+)。 -```Shell -uname -r -yum update -``` + ```Bash + uname -r + ``` -2. 在CentOS下使用yum安装Docker并启动。 +2. 更新系统底层的库文件(建议一定要执行,否则在使用Docker时可能会出现莫名其妙的问题)。 -```Shell -yum -y install docker -systemctl start docker -``` + ```Bash + yum update + ``` -3. 查看Docker的信息和版本。 +3. 移除可能存在的旧的Docker版本。 -```Shell -docker version -docker info -``` + ```Bash + yum erase -y docker docker-common docker-engine + ``` + +4. 安装yum工具包和依赖项。 + + ```Bash + yum install -y yum-utils device-mapper-persistent-data lvm2 + ``` + +5. 通过yum工具包添加yum源(安装Docker-ce的源)。 + + ```Bash + yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo + ``` + +6. 在CentOS下使用yum安装Docker-ce并启动。 + + ```Bash + yum -y install docker-ce + systemctl start docker + ``` + +7. 查看Docker的信息和版本。 + + ```Shell + docker version + docker info + ``` 接下来可以通过下载镜像和创建容器来看看Docker是否可以运转起来。可以使用下面的命令从Docker的镜像仓库下载名为hello-world的镜像文件。 @@ -299,7 +324,7 @@ select user, host, plugin, authentication_string from user where user='root'; 接下来我们试一试运行多个容器并让多个容器之间通过网络通信。我们创建4个Redis容器来实现一主三从的主从复制结构。 ```Shell -docker run -d -p 6379:6379 --name redis-master redis redis-server +docker run -d -p 6379:6379 --name redis-master redis docker run -d -p 6380:6379 --name redis-slave-1 --link redis-master:redis-master redis redis-server --replicaof redis-master 6379 docker run -d -p 6381:6379 --name redis-slave-2 --link redis-master:redis-master redis redis-server --replicaof redis-master 6379 docker run -d -p 6382:6379 --name redis-slave-3 --link redis-master:redis-master redis redis-server --replicaof redis-master 6379 @@ -336,9 +361,7 @@ repl_backlog_histlen:1988 #### 运行GitLab -GitLab是由GitLab Inc.开发的Git仓库管理工具,具有wiki、问题跟踪、持续集成等一系列的功能,分为社区版和企业版。通过Docker提供的虚拟化容器,我们可以安装社区版的Docker,命令如下所示。 - -因为GitLab需要使用SSH协议进行安全连接,我们要暴露容器的22端口,所以可以先将宿主机SSH连接的22端口修改为其他端口(如:12345),然后再进行后续的操作。 +GitLab是由GitLab Inc.开发的Git仓库管理工具,具有wiki、问题跟踪、持续集成等一系列的功能,分为社区版和企业版。通过Docker提供的虚拟化容器,我们可以安装社区版的Docker。因为GitLab需要使用SSH协议进行安全连接,我们要暴露容器的22端口,所以可以先将宿主机SSH连接的22端口修改为其他端口(如:12345),然后再进行后续的操作。 ```Shell vim /etc/ssh/sshd_config @@ -361,9 +384,7 @@ systemctl restart sshd 创建需要用于数据卷映射操作的文件夹。 ```Shell -mkdir -p /root/gitlab/config -mkdir -p /root/gitlab/logs -mkdir -p /root/gitlab/data +mkdir -p /root/gitlab/{config,logs,data} ``` 基于`gitlab/gitlab-ce`镜像创建容器,并暴露80端口(HTTP连接)和22端口(SSH连接)。 @@ -378,7 +399,7 @@ docker run -d -p 80:80 -p 22:22 --name gitlab -v /root/gitlab/config:/etc/gitlab ### 构建镜像 -通过上面的讲解,我们已经掌握了如何通过官方提供的镜像来创建容器。当然如果愿意,我们也可以用配置好的容器来生成镜像。简而言之,Docker镜像是由文件系统叠加而成的,系统的最底层是bootfs,相当于就是Linux内核的引导文件系统;接下来第二层是rootfs,这一层可以是一种或多种操作系统(如Debian或Ubuntu文件系统),Docker中的rootfs是只读状态的;Docker利用联合挂载技术将各层文件系统叠加到一起,最终的文件系统会包含有底层的文件和目录,这样的文件系统就是一个镜像。 +通过上面的讲解,我们已经掌握了如何通过官方提供的镜像来创建容器。当然如果愿意,我们也可以用配置好的容器来生成镜像。简而言之,**Docker镜像是由文件系统叠加而成的,系统的最底层是bootfs,相当于就是Linux内核的引导文件系统;接下来第二层是rootfs,这一层可以是一种或多种操作系统(如Debian或Ubuntu文件系统),Docker中的rootfs是只读状态的;Docker利用联合挂载技术将各层文件系统叠加到一起,最终的文件系统会包含有底层的文件和目录,这样的文件系统就是一个镜像**。 之前我们讲过了如何查找、列出镜像和拉取(下载)镜像,接下来看看构建镜像的两种方式: @@ -438,25 +459,77 @@ jackfrued/mywebserver latest 795b294d265a 14 seconds ago 189 MB Dockerfile使用DSL(Domain Specific Language)来构建一个Docker镜像,只要编辑好了Dockerfile文件,就可以使用`docker build`命令来构建一个新的镜像。 -我们先创建一个空文件夹并在文件夹下创建名为Dockerfile的文件。 +我们先创建一个名为myapp的文件夹来保存项目代码和Dockerfile的文件,如下所示: ```Shell -touch Dockerfile +[ECS-root temp]# tree myapp +myapp +├── api +│ ├── app.py +│ ├── requirements.txt +│ └── start.sh +└── Dockerfile ``` -编辑这个Dockerfile文件添加如下所示的内容。 +其中api是Flask项目的文件夹,其中包括了项目代码、依赖项以及启动脚本等文件,具体内容如下所示: + +app.py文件: + +```Python +from flask import Flask +from flask_restful import Resource, Api +from flask_cors import CORS + +app = Flask(__name__) +CORS(app, resources={r'/api/*': {'origins': '*'}}) +api = Api(app) + + +class Product(Resource): + + def get(self): + products = ['Ice Cream', 'Chocolate', 'Coca Cola', 'Hamburger'] + return {'products': products} + + +api.add_resource(Product, '/api/products') +``` + +requirements.txt文件: + +```INI +flask +flask-restful +flask-cors +gunicorn +``` + +start.sh文件: ```Shell -vim Dockerfile +#!/bin/bash +exec gunicorn -w 4 -b 0.0.0.0:8000 app:app ``` +> **提示**:需要给start.sh文件以执行权限,可以使用`chmod 755 start.sh`命令来做到。 + +Dockerfile文件: + ```Dockerfile -# version: 0.0.1 -FROM ubuntu:14.04 +# 指定基础镜像 +FROM python:3.7 +# 指定镜像的维护者 MAINTAINER jackfrued "jackfrued@126.com" -RUN apt-get update && apt-get install -y nginx -RUN echo 'hello, world!' > /usr/share/nginx/html/index.html -EXPOSE 80 +# 将指定文件添加到容器中指定的位置 +ADD api/* /root/api/ +# 设置工作目录 +WORKDIR /root/api +# 执行命令(安装Flask项目的依赖项) +RUN pip install -r requirements.txt -i https://pypi.doubanio.com/simple/ +# 容器启动时要执行的命令 +ENTRYPOINT ["./start.sh"] +# 暴露端口 +EXPOSE 8000 ``` 我们来解释一下上面的Dockerfile文件。Dockerfile文件通过特殊的指令来指定基础镜像(FROM指令)、创建容器后需要指定的命令(RUN指令)以及需要暴露的端口(EXPOSE)等信息。我们稍后会专门为大家介绍这些Dockfile中的指令。 @@ -464,7 +537,7 @@ EXPOSE 80 接下来我们可以使用`docker build`命令来创建镜像,如下所示。 ```Shell -docker build -t="jackfrued/webserver" . +docker build -t "jackfrued/myapp" . ``` > 提示:上面的命令最后面的`.` 千万不要漏掉了哦,它表示从当前路径下寻找Dockerfile。 @@ -476,34 +549,31 @@ docker images ``` ``` -REPOSITORY TAG IMAGE ID CREATED SIZE -jackfrued/webserver latest 87d6cb096be2 23 minutes ago 222 MB +REPOSITORY TAG IMAGE ID CREATED SIZE +jackfrued/myapp latest 6d6f026a7896 5 seconds ago 930 MB ``` 如果想知道镜像文件是如何创建出来的,可以使用下面的命令。 ```Shell -docker history jackfrued/webserver +docker history jackfrued/myapp ``` ``` -IMAGE CREATED CREATED BY SIZE -87d6cb096be2 25 minutes ago /bin/sh -c #(nop) EXPOSE 80/tcp 0 B -53d3bc3a123e 25 minutes ago /bin/sh -c service nginx start 3 B -10646b63275e 25 minutes ago /bin/sh -c echo 'hello, world!' > /usr/sha... 14 B -f3e3bf3e998e 25 minutes ago /bin/sh -c apt-get update && apt-get insta... 34.3 MB -c98e22cf5a64 26 minutes ago /bin/sh -c #(nop) MAINTAINER jackfrued "j... 0 B -2c5e00d77a67 3 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B -<missing> 3 months ago /bin/sh -c mkdir -p /run/systemd && echo '... 7 B -<missing> 3 months ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0 B -<missing> 3 months ago /bin/sh -c set -xe && echo '#!/bin/sh' >... 195 kB -<missing> 3 months ago /bin/sh -c #(nop) ADD file:1e01ab604c0cc30... 188 MB +IMAGE CREATED CREATED BY SIZE COMMENT +6d6f026a7896 31 seconds ago /bin/sh -c #(nop) EXPOSE 8000/tcp 0 B +3f7739173a79 31 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["./start.sh"] 0 B +321e6bf09bf1 32 seconds ago /bin/sh -c pip install -r requirements.txt... 13 MB +2f9bf2c89ac7 37 seconds ago /bin/sh -c #(nop) WORKDIR /root/api 0 B +86119afbe1f8 37 seconds ago /bin/sh -c #(nop) ADD multi:4b76f9c9dfaee8... 870 B +08d465e90d4d 3 hours ago /bin/sh -c #(nop) MAINTAINER jackfrued "j... 0 B +fbf9f709ca9f 12 days ago /bin/sh -c #(nop) CMD ["python3"] 0 B ``` 使用该镜像来创建容器运行Web服务器。 ```Shell -docker run -d -p 80:80 --name mywebserver jackfrued/webserver nginx -g "daemon off;" +docker run -d -p 8000:8000 --name myapp jackfrued/myapp ``` 如果希望将上面创建的镜像文件放到dockerhub仓库中,可以按照如下所示的步骤进行操作。 @@ -611,7 +681,7 @@ docker push jackfrued/webserver USER nginx ``` -8. **VOLUME**:在创建容器时添加一个数据卷的挂载点。通过数据卷操作可以实现容器间数据的共享和重用,对卷所作的修改可以马上生效而不需要重新启动容器,我们之前创建容器时使用`—volume`参数就是为了实现数据卷的映射操作。 +8. **VOLUME**:在创建容器时添加一个数据卷的挂载点。通过数据卷操作可以实现容器间数据的共享和重用,对卷所作的修改可以马上生效而不需要重新启动容器,我们之前创建容器时使用`--volume`参数就是为了实现数据卷的映射操作。 ```Dockerfile VOLUME ["/路径1", "/路径2/子路径2.1/", ...] @@ -638,7 +708,7 @@ docker push jackfrued/webserver ONBUILD RUN cd /app/src && make ``` -### 容器编排 +### 多容器管理 我们的项目可能会使用了多个容器,容器多了之后管理容器的工作就会变得麻烦。如果要对多个容器进行自动配置使得容器可以相互协作甚至实现复杂的调度,这就需要进行容器编排。Docker原生对容器编排的支持非常弱,但是可以通过社区提供的工具来实现容器编排。 @@ -649,7 +719,7 @@ docker push jackfrued/webserver 1. 安装Docker Compose。 ```Shell - curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose + curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose ``` @@ -663,30 +733,31 @@ docker push jackfrued/webserver 2. 使用Docker Compose。 - 我们先创建一个名为`composeapp`的文件夹并在该文件夹下创建两个子文件夹`product-service`和`web-site`,如下所示。 + 我们在刚才的Flask项目中引入缓存,然后再利用Flask提供的数据接口为前端页面提供数据,使用Vue.js进行页面渲染并将静态页面部署在Nginx服务器上。项目文件夹结构如下所示: ```Shell - mkdir composeapp - cd composeapp - mkdir product-service - mkdir web-site + [ECS-root ~]# tree temp + temp + ├── docker-compose.yml + ├── html + │ └── index.html + └── myapp + ├── api + │ ├── app.py + │ ├── requirements.txt + │ └── start.sh + └── Dockerfile ``` - 我们先在`product-service`文件夹下编写提供数据的API接口程序。 - - ```Shell - vim product-service/api.py - ``` - - 我们用Flask来实现一个非常简单的数据接口服务程序。 + 修改后的app.py文件代码如下所示: ```Python from pickle import dumps, loads from flask import Flask from flask_restful import Resource, Api - from redis import Redis from flask_cors import CORS + from redis import Redis app = Flask(__name__) CORS(app, resources={r'/api/*': {'origins': '*'}}) @@ -698,52 +769,18 @@ docker push jackfrued/webserver def get(self): data = redis.get('products') - if not data: + if data: + products = loads(data) + else: products = ['Ice Cream', 'Chocolate', 'Coca Cola', 'Hamburger'] redis.set('products', dumps(products)) - else: - products = loads(data) return {'products': products} api.add_resource(Product, '/api/products') - - if __name__ == '__main__': - app.run(host='0.0.0.0', port=8000, debug=True) ``` - 由于上面的项目需要依赖`flask`、 `flask-restful`等三方库,所以我们再添加一个指明依赖库的文件并将其命名为`requirements.txt`,其内容如下所示。 - - ```Shell - vim product-service/requirements.txt - ``` - - ``` - flask - flask-restful - flask-cors - redis - ``` - - 稍后我们会将上面的接口服务放在一个容器中运行,为此我们先编写一个Dockerfile文件以便创建对应的镜像,其内容如下所示。 - - ```Shell - vim product-service/Dockerfile - ``` - - ```Dockerfile - FROM python:3 - ADD . /root/product-service - WORKDIR /root/product-service - RUN pip install -r requirements.txt - CMD ["python", "api.py"] - ``` - - 我们再去到`web-site`目录下创建一个页面,稍后我们会通一个容器来提供Nginx服务并运行该页面,而这个页面会访问我们刚才部署的数据接口服务获取数据并通过Vue.js将数据渲染到页面上。 - - ```Shell - vim web-site/index.html - ``` + html文件夹用来保存静态页面,稍后我们会通一个运行Nginx的容器来向浏览器提供静态页面。index.html文件的内容如下所示: ```HTML <!DOCTYPE html> @@ -777,68 +814,63 @@ docker push jackfrued/webserver </html> ``` - 接下来,我们要通过一个YAML文件来创建三个容器并指明容器之间的依赖关系。 - - ```Shell - vim docker-compose.yml - ``` + 接下来,我们要通过docker-compose.yml文件来创建三个容器并指明容器之间的依赖关系。 ```YAML version: '3' services: - - product-service: - build: ./product-service + api-server: + build: ./myapp ports: - '8000:8000' links: - redis-master - - web-site: + web-server: image: nginx ports: - '80:80' volumes: - - ./web-site:/usr/share/nginx/html - + - ./html:/usr/share/nginx/html redis-master: image: redis expose: - '6379' ``` - 有了这个YAML文件,我们就可以使用`docker-compose`命令来创建和管理这三个容器,其命令如下所示。 + 有了这个YAML文件,我们就可以使用`docker-compose`命令来创建容器运行项目,其命令如下所示: ```Shell - docker-compose up + [ECS-root temp]# docker-compose up + Creating network "temp_default" with the default driver + Creating temp_web-server_1 ... done + Creating temp_redis-master_1 ... done + Creating temp_api-server_1 ... done + Attaching to temp_redis-master_1, temp_web-server_1, temp_api-server_1 + redis-master_1 | 1:C 05 Dec 2019 11:57:26.828 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo + redis-master_1 | 1:C 05 Dec 2019 11:57:26.828 # Redis version=5.0.6, bits=64, commit=00000000, modified=0, pid=1, just started + redis-master_1 | 1:C 05 Dec 2019 11:57:26.828 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf + redis-master_1 | 1:M 05 Dec 2019 11:57:26.830 * Running mode=standalone, port=6379. + redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. + redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 # Server initialized + redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. + redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. + redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 * Ready to accept connections + api-server_1 | [2019-12-05 11:57:27 +0000] [1] [INFO] Starting gunicorn 20.0.4 + api-server_1 | [2019-12-05 11:57:27 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) + api-server_1 | [2019-12-05 11:57:27 +0000] [1] [INFO] Using worker: sync + api-server_1 | [2019-12-05 11:57:27 +0000] [8] [INFO] Booting worker with pid: 8 + api-server_1 | [2019-12-05 11:57:27 +0000] [9] [INFO] Booting worker with pid: 9 + api-server_1 | [2019-12-05 11:57:27 +0000] [10] [INFO] Booting worker with pid: 10 + api-server_1 | [2019-12-05 11:57:27 +0000] [11] [INFO] Booting worker with pid: 11 ``` - ``` - Creating network "composeapp_default" with the default driver - Building product-service - Step 1/5 : FROM python:3 - ---> e497dabd8450 - Step 2/5 : ADD . /root/product-service - ---> fbe62813d595 - Removing intermediate container 6579e845565a - Step 3/5 : WORKDIR /root/product-service - ---> 3a722675e3b1 - Removing intermediate container 57fc490436ce - Step 4/5 : RUN pip install -r requirements.txt - ---> Running in cadc2d0c1b9b - ... ... - ---> fc747fc11f4a - Removing intermediate container cadc2d0c1b9b - Step 5/5 : CMD python api.py - ---> Running in ecbbd2a69906 - ---> 637e760f2e5b - Removing intermediate container ecbbd2a69906 - Successfully built 637e760f2e5b - WARNING: Image for service product-service was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. - Creating composeapp_redis-master_1 ... done - Creating composeapp_web-site_1 ... done - Creating composeapp_product-service_1 ... done - Attaching to composeapp_redis-master_1, composeapp_web-site_1, composeapp_product-service_1 - ... ... + 要停止容器的运行,可以使用下面的命令。 + + ```Shell + docker-compose down ``` +#### Kubernetes(K8S) + +实际的生产环境中常常需要部署和管理多个协同工作的容器,docker compose解决了多容器创建和管理的问题,但是实际项目中,我们还需要Kubernetes(以下都简称为K8S)来提供一个跨主机集群的容器调度平台。K8S可以进行自动化容器的部署、扩展和操作,从而提供以容器为中心的基础架构。该项目是谷歌在2014年启动的项目,建立在谷歌公司十余年运维经验的基础之上,而且谷歌自己的应用也是运行在容器上的。 + diff --git a/Day91-100/94.网络API接口设计.md b/Day91-100/94.网络API接口设计.md index 159017e..36842a4 100644 --- a/Day91-100/94.网络API接口设计.md +++ b/Day91-100/94.网络API接口设计.md @@ -1,12 +1,14 @@ ## 网络API接口设计 -目前许多的Web应用和移动应用都使用了前后端分离的开发模式,前后端分离简单的说就是前端或移动端通过网络API接口和后台进行交互。API是应用程序的编程接口的缩写;网络API通常指的是基于一个URL(统一资源定位符)可以访问到的资源,也就是说通过这个URL我们可以让服务器对某个资源进行操作并返回操作的结果,复杂的业务逻辑被隐藏在简单的API接口中。URL的通用格式如下所示: +目前许多的Web应用和移动应用都使用了前后端分离的开发模式,前后端分离简单的说就是前端或移动端通过网络API接口和后台进行交互,获得接口中提供的数据并负责用户界面的渲染。API是应用程序的编程接口的缩写,网络API通常指的是基于一个URL(统一资源定位符)可以访问到的资源,也就是说通过这个URL我们就可以请求服务器对某个资源进行操作并返回操作的结果。大家可以想想,网络API接口不也是一种封装吗,简单的说就是将复杂的业务逻辑隐藏在简单的API接口中。 + +URL的通用格式如下所示: ``` 协议://用户名:口令@主机:端口/路径1/.../路径N/资源名 ``` -> 说明:URL中的用户名(有可能不需要提供用户名)、口令(有可能不需要提供口令)、端口(有可能使用默认端口)、路径(资源有可能直接位于根路径`/`下)并不是必需的部分,可以根据需要进行设置。 +> **说明**:URL中的用户名(有可能不需要提供用户名)、口令(有可能不需要提供口令)、端口(有可能使用默认端口)、路径(资源有可能直接位于根路径`/`下)并不是必需的部分,可以根据需要进行设置。 网络API通常基于HTTP或HTTPS进行访问,基于HTTP/HTTPS最大的好处就在于访问起来非常的简单方便,而且可以跨语言、跨应用进行访问和互操作。 @@ -14,7 +16,7 @@ #### 关键问题 -为移动端或者PC端设计网络API接口一个非常重要的原则是:根据业务实体而不是用户界面或操作来设计。如果API接口的设计是根据用户的操作或者界面上的功能设置来设计,随着需求的变更,用户界面也会进行调整,需要的数据也在发生变化,那么后端开发者就要不停的调整API,或者给一个API设计出多个版本,这些都会使项目的开发和维护成本增加。 +为移动端或者PC端设计网络API接口一个非常重要的原则是:**根据业务实体而不是用户界面或操作来设计API接口**。如果API接口的设计是根据用户的操作或者界面上的功能设置来设计,随着需求的变更,用户界面也会进行调整,需要的数据也在发生变化,那么后端开发者就要不停的调整API,或者给一个API设计出多个版本,这些都会使项目的开发和维护成本增加。我们可以将业务实体理解为服务器提供的资源,而URL就是资源的定位符(标识符),这种方式是最为简单自然的。对于相对复杂的用户操作,我们可以提供一个“门面”(设计模式中的“门面模式”),通过该“门面”把多个接口的功能组装起来即可。 下面是某个网站开放API的接口,可以看出API的设计是围绕业务实体来进行的,而且都做到了“见名知意”。 @@ -28,9 +30,9 @@ | comments/destroy | 删除一条评论 | | comments/reply | 回复一条评论 | -需要说明的是,上面的API接口并不是REST风格的。REST是一种网络应用架构风格,被认为最适合分布式的网络应用。关于REST的知识,可以阅读阮一峰老师的[《理解RESTful架构》](http://www.ruanyifeng.com/blog/2011/09/restful.html)以及[《RESTful API设计指南》](http://www.ruanyifeng.com/blog/2014/05/restful_api.html),当然这两篇文章大家也要批判的阅读,因为上面阐述的观点并不完全正确,有些内容甚至是自相矛盾的。 +需要说明的是,**上面的API接口并不是REST风格的**。REST是一种网络应用架构风格,被认为最适合分布式的网络应用。关于REST的知识,可以阅读阮一峰的[《理解RESTful架构》](http://www.ruanyifeng.com/blog/2011/09/restful.html)以及[《RESTful API设计指南》](http://www.ruanyifeng.com/blog/2014/05/restful_api.html),当然这两篇文章大家也要批判的阅读,因为上面阐述的观点并不完全正确,有些内容甚至是自相矛盾的。 -API接口返回的数据通常都是**JSON**或**XML**格式,我们这里不会讲述XML的知识,因为这种格式几乎已经被淘汰掉了。对于JSON格式的数据,我们需要做到不要返回null这的值,因为这样的值一旦处置失当,会给前端和移动端开发带来不必要的麻烦(因为开发者有可能会使用强类型语言)。要解决这个问题可以从源头入手,在设计数据库的时候,尽量给每个字段都加上“not null”约束或者设置合理的默认值约束。 +API接口返回的数据通常都是**JSON**或**XML**格式,XML这种数据格式目前基本已经被弃用了。对于JSON格式的数据,我们需要做到不要返回null这的值,因为这样的值一旦处置失当,会给前端和移动端开发带来不必要的麻烦(因为开发者有可能会使用强类型语言)。要解决这个问题可以从源头入手,在设计数据库的时候,尽量给每个字段都加上“not null”约束或者设置合理的默认值约束。 #### 其他问题 @@ -42,9 +44,7 @@ API接口返回的数据通常都是**JSON**或**XML**格式,我们这里不 下面以设计评论接口为例,简单说明接口文档应该如何撰写。 -#### 评论接口 - -全局返回状态码 +首先,我们可以定义全局返回状态码。 | 返回码 | 返回信息 | 说明 | | ------ | ------------ | ---------------------------------- | @@ -54,7 +54,9 @@ API接口返回的数据通常都是**JSON**或**XML**格式,我们这里不 | 10003 | 评论已被删除 | 查看评论时评论因不和谐因素已被删除 | | 10004 | …… | …… | -1. **GET** `/articles/{article-id}/comments/` +1. 获取文章评论。 + + URL:**GET** `/articles/{article-id}/comments/` 开发者:王大锤 @@ -103,7 +105,9 @@ API接口返回的数据通常都是**JSON**或**XML**格式,我们这里不 } ``` -2. **POST** `/articles/{article-id}/comments` +2. 新增文章评论。 + + **POST** `/articles/{article-id}/comments` 开发者:王大锤 @@ -140,5 +144,5 @@ API接口返回的数据通常都是**JSON**或**XML**格式,我们这里不 -> 提示:如果没有接口文档撰写经验,可以使用在线接口文档编辑平台RAP2或YAPI来进行接口文档撰写,也可以参考我的[《方天下(租房项目)接口文档》](../番外篇/方天下(租房项目)接口文档.md)来了解如何撰写接口文档。 +> **提示**:如果没有接口文档撰写经验,可以使用在线接口文档编辑平台[RAP2](<http://rap2.taobao.org/>)或[YAPI](<http://yapi.demo.qunar.com/>)来进行接口文档撰写。 diff --git a/Day91-100/95.使用Django开发商业项目.md b/Day91-100/95.使用Django开发商业项目.md index e1a796f..5229ab1 100644 --- a/Day91-100/95.使用Django开发商业项目.md +++ b/Day91-100/95.使用Django开发商业项目.md @@ -675,7 +675,7 @@ class MyMiddleware(object): ``` ```Python -class MyMiddleware(object): +class MyMiddleware: def __init__(self): pass @@ -853,7 +853,7 @@ CACHES = { 'CONNECTION_POOL_KWARGS': { 'max_connections': 1000, }, - 'PASSWORD': '1qaz2wsx', + 'PASSWORD': 'yourpass', } }, # 页面缓存 @@ -868,7 +868,7 @@ CACHES = { 'CONNECTION_POOL_KWARGS': { 'max_connections': 500, }, - 'PASSWORD': '1qaz2wsx', + 'PASSWORD': 'yourpass', } }, # 会话缓存 @@ -884,7 +884,7 @@ CACHES = { 'CONNECTION_POOL_KWARGS': { 'max_connections': 2000, }, - 'PASSWORD': '1qaz2wsx', + 'PASSWORD': 'yourpass', } }, # 接口数据缓存 @@ -899,7 +899,7 @@ CACHES = { 'CONNECTION_POOL_KWARGS': { 'max_connections': 500, }, - 'PASSWORD': '1qaz2wsx', + 'PASSWORD': 'yourpass', } }, } @@ -1700,9 +1700,9 @@ Celery是一个本身不提供队列服务,官方推荐使用RabbitMQ或Redis # 将来实际部署项目的时候生产者、消费者、消息队列可能都是不同节点 beat_schedule={ 'task1': { - 'task': 'common.tasks.show_msg', + 'task': 'common.tasks.scheduled_task', 'schedule': crontab('*', '*', '*', '*', '*'), - 'args': ('刘强东,奶茶妹妹喊你回家喝奶啦', ) + 'args': ('...', ) }, }, ) @@ -1710,8 +1710,8 @@ Celery是一个本身不提供队列服务,官方推荐使用RabbitMQ或Redis ```Python @app.task - def show_msg(content): - print(content) + def scheduled_task(*args, **kwargs): + pass ``` 7. 启动Celery创建执行定时任务的beat(消息的生产者)。 @@ -1781,7 +1781,7 @@ CORS_ORIGIN_ALLOW_ALL = True >>> signer.unsign(value) 'hello, world!' >>> ->>> signer = Signer(salt='1qaz2wsx') +>>> signer = Signer(salt='yoursalt') >>> signer.sign('hello, world!') 'hello, world!:9vEvG6EA05hjMDB5MtUr33nRA_M' >>> @@ -2475,6 +2475,6 @@ TOTAL 267 176 34% [mysqld] slow_query_log=ON slow_query_log_file=/usr/local/mysql/data/slow.log - long_query_time=1。 + long_query_time=1 ``` diff --git a/Day91-100/97.电商网站技术要点剖析.md b/Day91-100/97.电商网站技术要点剖析.md index 5644568..bdf1911 100644 --- a/Day91-100/97.电商网站技术要点剖析.md +++ b/Day91-100/97.电商网站技术要点剖析.md @@ -305,7 +305,7 @@ if alipay.verify(params, params.pop('sign')): 1. 秒杀:秒杀是通常意味着要在很短的时间处理极高的并发,系统在短时间需要承受平时百倍以上的流量,因此秒杀架构是一个比较复杂的问题,其核心思路是流量控制和性能优化,需要从前端(通过JavaScript实现倒计时、避免重复提交和限制频繁刷新)到后台各个环节的配合。流量控制主要是限制只有少部分流量进入服务后端(毕竟最终只有少部分用户能够秒杀成功),同时在物理架构上使用缓存(一方面是因为读操作多写操作少;另外可以将库存放在Redis中,利用DECR原语实现减库存;同时也可以利用Redis来进行限流,道理跟限制频繁发送手机验证码是一样的)和消息队列(消息队列最为重要的作用就是“削峰”和“上下游节点解耦合”)来进行优化;此外还要采用无状态服务设计,这样才便于进行水平扩展(通过增加设备来为系统扩容)。 2. 超卖现象:比如某商品的库存为1,此时用户1和用户2并发购买该商品,用户1提交订单后该商品的库存被修改为0,而此时用户2并不知道的情况下提交订单,该商品的库存再次被修改为-1这就是超卖现象。解决超卖现象有三种常见的思路: - 悲观锁控制:查询商品数量的时候就用`select ... for update`对数据加锁,这样的话用户1查询库存时,用户2因无法读取库存数量被阻塞,直到用户1提交或者回滚了更新库存的操作后才能继续,从而解决了超卖问题。但是这种做法对并发访问量很高的商品来说性能太过糟糕,实际开发中可以在库存小于某个值时才考虑加锁,但是总的来说这种做法不太可取。 - - 乐观锁控制:查询商品数量不用加锁,更新库存的时候设定商品数量必须与之前查询数量相同才能更新,否则说明其他事务已经更新了库存,必须重新发出请求。这种做法要求事务隔离级别为可重复读(repeatable read),否则仍然会产生问题。 + - 乐观锁控制:查询商品数量不用加锁,更新库存的时候设定商品数量必须与之前查询数量相同才能更新,否则说明其他事务已经更新了库存,必须重新发出请求。 - 尝试减库存:将上面的查询(`select`)和更新(`update`)操作合并为一条SQL操作,更新库存的时候,在`where`筛选条件中加上`库存>=购买数量`或`库存-购买数量>=0`的条件,这种做法要求事务隔离级别为读提交(read committed)。 > 提示:有兴趣的可以自己在知乎上看看关于这类问题的讨论。 @@ -333,21 +333,22 @@ ElasticSearch的安装和配置可以参考[《ElasticSearch之Docker安装》]( - haystack(django-haystack / drf-haystack) + whoosh + Jieba - haystack (django-haystack / drf-haystack)+ elasticsearch - requests + elasticsearch +- django-elasticsearch-dsl ####安装和使用ElasticSearch 1. 使用Docker安装ElasticSearch。 ```Shell - docker pull elasticsearch:6.5.3 - docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xmx512m" --name es elasticsearch:6.5.3 + docker pull elasticsearch:7.6.0 + docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xmx512m" --name es elasticsearch:7.6.0 ``` > 说明:上面创建容器时通过`-e`参数指定了使用单机模式和Java虚拟机最小最大可用堆空间的大小,堆空间大小可以根据服务器实际能够提供给ElasticSearch的内存大小来决定,默认为2G。 2. 创建数据库。 - 请求:PUT - `http://1.2.3.4:9200/demo` + 请求:PUT - `http://1.2.3.4:9200/demo/` 响应: @@ -361,7 +362,7 @@ ElasticSearch的安装和配置可以参考[《ElasticSearch之Docker安装》]( 3. 查看创建的数据库。 - 请求:GET - `http://1.2.3.4:9200/demo` + 请求:GET - `http://1.2.3.4:9200/demo/` 响应: @@ -527,18 +528,19 @@ ElasticSearch的安装和配置可以参考[《ElasticSearch之Docker安装》]( 2. 下载和ElasticSearch版本对应的[ik](https://github.com/medcl/elasticsearch-analysis-ik)和[pinyin](https://github.com/medcl/elasticsearch-analysis-pinyin)插件。 ```Shell + yum install -y wget cd plugins/ mkdir ik cd ik - wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.3/elasticsearch-analysis-ik-6.5.3.zip - unzip elasticsearch-analysis-ik-6.5.3.zip - rm -f elasticsearch-analysis-ik-6.5.3.zip + wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.0/elasticsearch-analysis-ik-7.6.0.zip + unzip elasticsearch-analysis-ik-7.6.0.zip + rm -f elasticsearch-analysis-ik-7.6.0.zip cd .. mkdir pinyin cd pinyin - wget https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v6.5.3/elasticsearch-analysis-pinyin-6.5.3.zip - unzip elasticsearch-analysis-pinyin-6.5.3.zip - rm -f elasticsearch-analysis-pinyin-6.5.3.zip + wget https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v7.6.0/elasticsearch-analysis-pinyin-7.6.0.zip + unzip elasticsearch-analysis-pinyin-7.6.0.zip + rm -f elasticsearch-analysis-pinyin-7.6.0.zip ``` 3. 退出容器,重启ElasticSearch。 diff --git a/Day91-100/98.项目部署上线和性能调优.md b/Day91-100/98.项目部署上线和性能调优.md index 2e84476..7fd6da2 100644 --- a/Day91-100/98.项目部署上线和性能调优.md +++ b/Day91-100/98.项目部署上线和性能调优.md @@ -65,30 +65,36 @@ 2. 下载Python源代码。 ```Shell - wget https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tar.xz + wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tar.xz ``` -3. 解压缩和解归档。 +3. 验证下载文件。 - ```Shell - xz -d Python-3.7.1.tar.xz - tar -xvf Python-3.7.1.tar + ```Bash + md5sum Python-3.7.6.tar.xz ``` -4. 执行配置生成Makefile(构建文件)。 +4. 解压缩和解归档。 ```Shell - cd Python-3.7.1 + xz -d Python-3.7.6.tar.xz + tar -xvf Python-3.7.6.tar + ``` + +5. 执行安装前的配置(生成Makefile文件)。 + + ```Shell + cd Python-3.7.6 ./configure --prefix=/usr/local/python37 --enable-optimizations ``` -5. 构建和安装。 +6. 构建和安装。 ```Shell make && make install ``` -6. 配置PATH环境变量(用户或系统环境变量)并激活。 +7. 配置PATH环境变量(用户或系统环境变量)并激活。 ```Shell vim ~/.bash_profile @@ -108,13 +114,13 @@ source /etc/profile ``` -7. 注册软链接(符号链接)- 这一步不是必须的,但通常会比较有用。 +8. 注册软链接(符号链接)- 这一步不是必须的,但通常会比较有用。 ```Shell ln -s /usr/local/python37/bin/python3 /usr/bin/python3 ``` -8. 测试Python环境是否更新成功(安装Python 3一定不能破坏原来的Python 2)。 +9. 测试Python环境是否更新成功(安装Python 3一定不能破坏原来的Python 2)。 ```Shell python3 --version @@ -286,7 +292,7 @@ pip install -r code/teamproject/requirements.txt ```Nginx # 配置用户 - user root; + user nginx; # 工作进程数(建议跟CPU的核数量一致) worker_processes auto; # 错误日志 @@ -432,8 +438,9 @@ http { location / { proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-For $remote_addr; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_pass http://fangtx; } @@ -529,7 +536,21 @@ root 上面创建Docker容器时使用的`-v`参数(`--volume`)表示映射数据卷,冒号前是宿主机的目录,冒号后是容器中的目录,这样相当于将宿主机中的目录挂载到了容器中。 -3. 创建和配置slave。 +3. 备份主表中的数据(如果需要的话)。 + + ```SQL + mysql> flush table with read lock; + ``` + + ```Bash + mysqldump -u root -p 123456 -A -B > /root/backup/mysql/mybak$(date +"%Y%m%d%H%M%S").sql + ``` + + ```SQL + mysql> unlock table; + ``` + +4. 创建和配置slave。 ```Shell docker run -d -p 3308:3306 --name mysql-slave-1 \ @@ -798,7 +819,7 @@ class MasterSlaveRouter(object): [program:celery] ; Set full path to celery program if using virtualenv - command=/root/project/venv/bin/python manage.py celery -A fangtx worker + command=/root/project/venv/bin/celery -A fangtx worker user=root numprocs=1 stdout_logfile=/var/log/supervisor/celery.log @@ -831,33 +852,33 @@ class MasterSlaveRouter(object): 1. 常用开源软件。 - | 功能 | 开源方案 | - | ------------------- | ------------------------- | - | 版本控制工具 | Git、Mercurial、SVN | - | 缺陷管理 | Redmine、Mantis | - | 负载均衡 | Nginx、LVS、HAProxy | - | 邮件服务 | Postfix、Sendmail | - | HTTP服务 | Nginx、Apache | - | 消息队列 | RabbitMQ、ZeroMQ、Redis | - | 文件系统 | FastDFS | - | 基于位置服务(LBS) | MongoDB、Redis | - | 监控服务 | Nagios、Zabbix | - | 关系型数据库 | MySQL、PostgreSQL | - | 非关系型数据库 | MongoDB、Redis、Cassandra | - | 搜索引擎 | ElasticSearch、Solr | - | 缓存服务 | Mamcached、Redis | + | 功能 | 开源方案 | + | ------------------- | ------------------------------- | + | 版本控制工具 | Git、Mercurial、SVN | + | 缺陷管理 | Redmine、Mantis | + | 负载均衡 | Nginx、LVS、HAProxy | + | 邮件服务 | Postfix、Sendmail | + | HTTP服务 | Nginx、Apache | + | 消息队列 | RabbitMQ、ZeroMQ、Redis、Kafka | + | 文件系统 | FastDFS | + | 基于位置服务(LBS) | MongoDB、Redis | + | 监控服务 | Nagios、Zabbix | + | 关系型数据库 | MySQL、PostgreSQL | + | 非关系型数据库 | MongoDB、Redis、Cassandra、TiDB | + | 搜索引擎 | ElasticSearch、Solr | + | 缓存服务 | Mamcached、Redis | 2. 常用云服务。 - | 功能 | 可用的云服务 | - | -------------- | --------------------------------------- | - | 团队协作工具 | Teambition、钉钉 | - | 代码托管平台 | Github、Gitee、CODING | - | 邮件服务 | SendCloud | - | 云存储(CDN) | 七牛、OSS、LeanCloud、Bmob、又拍云、AWS | - | 移动端推送 | 极光、友盟、百度 | - | 即时通信 | 环信、融云 | - | 短信服务 | 云片、极光、Luosimao、又拍云 | - | 第三方登录 | 友盟、ShareSDK | - | 网站监控和统计 | 阿里云监控、监控宝、百度云观测、小鸟云 | + | 功能 | 可用的云服务 | + | -------------- | -------------------------------------- | + | 团队协作工具 | Teambition、钉钉 | + | 代码托管平台 | Github、Gitee、CODING | + | 邮件服务 | SendCloud | + | 云存储(CDN) | 七牛、OSS、LeanCloud、Bmob、又拍云、S3 | + | 移动端推送 | 极光、友盟、百度 | + | 即时通信 | 环信、融云 | + | 短信服务 | 云片、极光、Luosimao、又拍云 | + | 第三方登录 | 友盟、ShareSDK | + | 网站监控和统计 | 阿里云监控、监控宝、百度云观测、小鸟云 | diff --git a/Day91-100/99.面试中的公共问题.md b/Day91-100/99.面试中的公共问题.md index ec5648d..81ff84d 100644 --- a/Day91-100/99.面试中的公共问题.md +++ b/Day91-100/99.面试中的公共问题.md @@ -22,7 +22,7 @@ 1. 开发中用过哪些标准库和三方库。 - > 标准库:sys / os / re / math / random / logging / json / pickle / shelve / socket / datetime / hashlib / configparser / urllib / itertools / collections / functools / threading / multiprocess / timeit / atexit / abc / asyncio / base64 / concurrent.futures / copy / csv / operator / enum / heapq / http / profile / pstats / ssl / unitest / uuid + > 标准库:sys / os / re / math / random / logging / json / pickle / shelve / socket / datetime / hashlib / configparser / urllib / itertools / collections / functools / threading / multiprocess / timeit / atexit / abc / asyncio / base64 / concurrent.futures / copy / csv / operator / enum / heapq / http / profile / pstats / ssl / unittest / uuid > > 三方库:openpyxl / xlrd / xlwt / PyPDF2 / ReportLab / PyYAML / jieba / pillow / requests / urllib3 / responses / aiohttp / BeautifulSoup4 / lxml / pyquery / PyMySQL / psycopg2 / redis / PyMongo / Peewee / SQLAlchemy / alipay / PyJWT / itsdangerous / celery / flower / elasticsearch-dsl-py / PyCrypto / Paramiko / logbook / nose / pytest / coverage / Selenium / lineprofiler / memoryprofiler / matplotlib / pygal / OpenCV diff --git a/Day91-100/res/01.django_single_server.png b/Day91-100/res/01.django_single_server.png index 70d3bf3..4328439 100644 Binary files a/Day91-100/res/01.django_single_server.png and b/Day91-100/res/01.django_single_server.png differ diff --git a/Day91-100/res/02.django_dedicated_db_server.png b/Day91-100/res/02.django_dedicated_db_server.png index 143eb6e..f3b6d53 100644 Binary files a/Day91-100/res/02.django_dedicated_db_server.png and b/Day91-100/res/02.django_dedicated_db_server.png differ diff --git a/Day91-100/res/03.django_dedicated_static_server.png b/Day91-100/res/03.django_dedicated_static_server.png index f13a247..42f4672 100644 Binary files a/Day91-100/res/03.django_dedicated_static_server.png and b/Day91-100/res/03.django_dedicated_static_server.png differ diff --git a/Day91-100/res/04.django_load_balance.png b/Day91-100/res/04.django_load_balance.png index 16e7515..81995b5 100644 Binary files a/Day91-100/res/04.django_load_balance.png and b/Day91-100/res/04.django_load_balance.png differ diff --git a/Day91-100/res/05.django_massive_cluster.png b/Day91-100/res/05.django_massive_cluster.png index 9163fb2..b6850b4 100644 Binary files a/Day91-100/res/05.django_massive_cluster.png and b/Day91-100/res/05.django_massive_cluster.png differ diff --git a/Day91-100/res/Celery_RabitMQ.png b/Day91-100/res/Celery_RabitMQ.png index 4140130..feb260c 100644 Binary files a/Day91-100/res/Celery_RabitMQ.png and b/Day91-100/res/Celery_RabitMQ.png differ diff --git a/Day91-100/res/Producer-Broker-Consumer-Arrangement.png b/Day91-100/res/Producer-Broker-Consumer-Arrangement.png index b073a60..1967208 100644 Binary files a/Day91-100/res/Producer-Broker-Consumer-Arrangement.png and b/Day91-100/res/Producer-Broker-Consumer-Arrangement.png differ diff --git a/Day91-100/res/algorithm_complexity_1.png b/Day91-100/res/algorithm_complexity_1.png index 952889d..2d57aed 100644 Binary files a/Day91-100/res/algorithm_complexity_1.png and b/Day91-100/res/algorithm_complexity_1.png differ diff --git a/Day91-100/res/algorithm_complexity_2.png b/Day91-100/res/algorithm_complexity_2.png index 4c14249..0ff66f3 100644 Binary files a/Day91-100/res/algorithm_complexity_2.png and b/Day91-100/res/algorithm_complexity_2.png differ diff --git a/Day91-100/res/alipay_web_developer.png b/Day91-100/res/alipay_web_developer.png index 0716552..0e5b674 100644 Binary files a/Day91-100/res/alipay_web_developer.png and b/Day91-100/res/alipay_web_developer.png differ diff --git a/Day91-100/res/aliyun-certificate.png b/Day91-100/res/aliyun-certificate.png index c19a30b..dcf9ba5 100644 Binary files a/Day91-100/res/aliyun-certificate.png and b/Day91-100/res/aliyun-certificate.png differ diff --git a/Day91-100/res/aliyun-dnslist.png b/Day91-100/res/aliyun-dnslist.png index e6eda2f..2e1d9af 100644 Binary files a/Day91-100/res/aliyun-dnslist.png and b/Day91-100/res/aliyun-dnslist.png differ diff --git a/Day91-100/res/aliyun-domain.png b/Day91-100/res/aliyun-domain.png index 64a1f48..5976d3b 100644 Binary files a/Day91-100/res/aliyun-domain.png and b/Day91-100/res/aliyun-domain.png differ diff --git a/Day91-100/res/aliyun-keeprecord.png b/Day91-100/res/aliyun-keeprecord.png index d9d39bc..576f324 100644 Binary files a/Day91-100/res/aliyun-keeprecord.png and b/Day91-100/res/aliyun-keeprecord.png differ diff --git a/Day91-100/res/aliyun-resolve-settings.png b/Day91-100/res/aliyun-resolve-settings.png index 3f9363a..6ac4f62 100644 Binary files a/Day91-100/res/aliyun-resolve-settings.png and b/Day91-100/res/aliyun-resolve-settings.png differ diff --git a/Day91-100/res/builtin-middlewares.png b/Day91-100/res/builtin-middlewares.png index 59acb4b..d2eb9dc 100644 Binary files a/Day91-100/res/builtin-middlewares.png and b/Day91-100/res/builtin-middlewares.png differ diff --git a/Day91-100/res/celery_architecture.png b/Day91-100/res/celery_architecture.png index d3118a0..3e64790 100644 Binary files a/Day91-100/res/celery_architecture.png and b/Day91-100/res/celery_architecture.png differ diff --git a/Day91-100/res/click-jacking.png b/Day91-100/res/click-jacking.png index 9d92979..7cc3ef6 100644 Binary files a/Day91-100/res/click-jacking.png and b/Day91-100/res/click-jacking.png differ diff --git a/Day91-100/res/company_architecture.png b/Day91-100/res/company_architecture.png index ff3f9cb..d25d5dd 100644 Binary files a/Day91-100/res/company_architecture.png and b/Day91-100/res/company_architecture.png differ diff --git a/Day91-100/res/django-middleware.png b/Day91-100/res/django-middleware.png index 9885582..2281851 100644 Binary files a/Day91-100/res/django-middleware.png and b/Day91-100/res/django-middleware.png differ diff --git a/Day91-100/res/django-mtv.png b/Day91-100/res/django-mtv.png index 76f330e..b075e93 100644 Binary files a/Day91-100/res/django-mtv.png and b/Day91-100/res/django-mtv.png differ diff --git a/Day91-100/res/django_request_response_cycle.png b/Day91-100/res/django_request_response_cycle.png index 6c6ba2e..ec1cc90 100644 Binary files a/Day91-100/res/django_request_response_cycle.png and b/Day91-100/res/django_request_response_cycle.png differ diff --git a/Day91-100/res/docker_logo.png b/Day91-100/res/docker_logo.png index 19ff797..4ee9699 100644 Binary files a/Day91-100/res/docker_logo.png and b/Day91-100/res/docker_logo.png differ diff --git a/Day91-100/res/docker_vs_vm.png b/Day91-100/res/docker_vs_vm.png index 0eb6fea..e6aa9bb 100644 Binary files a/Day91-100/res/docker_vs_vm.png and b/Day91-100/res/docker_vs_vm.png differ diff --git a/Day91-100/res/dockerhub-repo.png b/Day91-100/res/dockerhub-repo.png index f9e1ab8..0bb02e3 100644 Binary files a/Day91-100/res/dockerhub-repo.png and b/Day91-100/res/dockerhub-repo.png differ diff --git a/Day91-100/res/er-graph.png b/Day91-100/res/er-graph.png index c8415be..2e843af 100644 Binary files a/Day91-100/res/er-graph.png and b/Day91-100/res/er-graph.png differ diff --git a/Day91-100/res/git-flow-detail.png b/Day91-100/res/git-flow-detail.png index 3f942a8..452b73b 100644 Binary files a/Day91-100/res/git-flow-detail.png and b/Day91-100/res/git-flow-detail.png differ diff --git a/Day91-100/res/git-flow.png b/Day91-100/res/git-flow.png index 8426a5b..2f7b54e 100644 Binary files a/Day91-100/res/git-flow.png and b/Day91-100/res/git-flow.png differ diff --git a/Day91-100/res/git-logo.png b/Day91-100/res/git-logo.png index efaec25..e8e1656 100644 Binary files a/Day91-100/res/git-logo.png and b/Day91-100/res/git-logo.png differ diff --git a/Day91-100/res/git-rebase.png b/Day91-100/res/git-rebase.png index 8896bd1..f503683 100644 Binary files a/Day91-100/res/git-rebase.png and b/Day91-100/res/git-rebase.png differ diff --git a/Day91-100/res/git-reset.png b/Day91-100/res/git-reset.png index d9f7f2e..e40ffdd 100644 Binary files a/Day91-100/res/git-reset.png and b/Day91-100/res/git-reset.png differ diff --git a/Day91-100/res/git_repository.png b/Day91-100/res/git_repository.png index d85bdbd..7c9b673 100644 Binary files a/Day91-100/res/git_repository.png and b/Day91-100/res/git_repository.png differ diff --git a/Day91-100/res/gitee-add-members.png b/Day91-100/res/gitee-add-members.png index 129926d..969ba74 100644 Binary files a/Day91-100/res/gitee-add-members.png and b/Day91-100/res/gitee-add-members.png differ diff --git a/Day91-100/res/gitee-create-project.png b/Day91-100/res/gitee-create-project.png index c632d63..fbe2c35 100644 Binary files a/Day91-100/res/gitee-create-project.png and b/Day91-100/res/gitee-create-project.png differ diff --git a/Day91-100/res/gitee-project-index.png b/Day91-100/res/gitee-project-index.png index 6f6e893..286185a 100644 Binary files a/Day91-100/res/gitee-project-index.png and b/Day91-100/res/gitee-project-index.png differ diff --git a/Day91-100/res/gitee-pull-request.png b/Day91-100/res/gitee-pull-request.png index da808aa..ccc1c71 100644 Binary files a/Day91-100/res/gitee-pull-request.png and b/Day91-100/res/gitee-pull-request.png differ diff --git a/Day91-100/res/gitlab-about.png b/Day91-100/res/gitlab-about.png index b4ba6c1..107e5e1 100644 Binary files a/Day91-100/res/gitlab-about.png and b/Day91-100/res/gitlab-about.png differ diff --git a/Day91-100/res/gitlab-new-issue.png b/Day91-100/res/gitlab-new-issue.png index e26e9cd..8e98dc8 100644 Binary files a/Day91-100/res/gitlab-new-issue.png and b/Day91-100/res/gitlab-new-issue.png differ diff --git a/Day91-100/res/hadoop_ecosystem.png b/Day91-100/res/hadoop_ecosystem.png index 8028fa8..f4079c9 100644 Binary files a/Day91-100/res/hadoop_ecosystem.png and b/Day91-100/res/hadoop_ecosystem.png differ diff --git a/Day91-100/res/http-request.png b/Day91-100/res/http-request.png index aca9287..67d0fb6 100644 Binary files a/Day91-100/res/http-request.png and b/Day91-100/res/http-request.png differ diff --git a/Day91-100/res/http-response.png b/Day91-100/res/http-response.png index f2b8ae3..0de1144 100644 Binary files a/Day91-100/res/http-response.png and b/Day91-100/res/http-response.png differ diff --git a/Day91-100/res/jenkins-create-admin.png b/Day91-100/res/jenkins-create-admin.png index abc5efd..74e2e59 100644 Binary files a/Day91-100/res/jenkins-create-admin.png and b/Day91-100/res/jenkins-create-admin.png differ diff --git a/Day91-100/res/jenkins-unlock.png b/Day91-100/res/jenkins-unlock.png index 5dee191..9f29a39 100644 Binary files a/Day91-100/res/jenkins-unlock.png and b/Day91-100/res/jenkins-unlock.png differ diff --git a/Day91-100/res/jenkins_new_project.png b/Day91-100/res/jenkins_new_project.png index 8019ada..2469c6f 100644 Binary files a/Day91-100/res/jenkins_new_project.png and b/Day91-100/res/jenkins_new_project.png differ diff --git a/Day91-100/res/mvc.png b/Day91-100/res/mvc.png index 7ba14ba..b12ee5a 100644 Binary files a/Day91-100/res/mvc.png and b/Day91-100/res/mvc.png differ diff --git a/Day91-100/res/oauth2.png b/Day91-100/res/oauth2.png index 7a07bd3..c9ca82d 100644 Binary files a/Day91-100/res/oauth2.png and b/Day91-100/res/oauth2.png differ diff --git a/Day91-100/res/power-designer-pdm.png b/Day91-100/res/power-designer-pdm.png index 5fba456..c29672d 100644 Binary files a/Day91-100/res/power-designer-pdm.png and b/Day91-100/res/power-designer-pdm.png differ diff --git a/Day91-100/res/pylint.png b/Day91-100/res/pylint.png index 92a9671..f793a72 100644 Binary files a/Day91-100/res/pylint.png and b/Day91-100/res/pylint.png differ diff --git a/Day91-100/res/python_jobs_chengdu.png b/Day91-100/res/python_jobs_chengdu.png index 6b18fa9..43eb46b 100644 Binary files a/Day91-100/res/python_jobs_chengdu.png and b/Day91-100/res/python_jobs_chengdu.png differ diff --git a/Day91-100/res/python_salary_chengdu.png b/Day91-100/res/python_salary_chengdu.png index fade359..e211664 100644 Binary files a/Day91-100/res/python_salary_chengdu.png and b/Day91-100/res/python_salary_chengdu.png differ diff --git a/Day91-100/res/rbac-basic.png b/Day91-100/res/rbac-basic.png index 3cf0025..1f95128 100644 Binary files a/Day91-100/res/rbac-basic.png and b/Day91-100/res/rbac-basic.png differ diff --git a/Day91-100/res/rbac-full.png b/Day91-100/res/rbac-full.png index 5692833..2dfbe0d 100644 Binary files a/Day91-100/res/rbac-full.png and b/Day91-100/res/rbac-full.png differ diff --git a/Day91-100/res/requirements_by_xmind.png b/Day91-100/res/requirements_by_xmind.png index aac6447..ae1fd18 100644 Binary files a/Day91-100/res/requirements_by_xmind.png and b/Day91-100/res/requirements_by_xmind.png differ diff --git a/Day91-100/res/selenium_ide.png b/Day91-100/res/selenium_ide.png index 4d2f275..a98824f 100644 Binary files a/Day91-100/res/selenium_ide.png and b/Day91-100/res/selenium_ide.png differ diff --git a/Day91-100/res/shopping-pdm.png b/Day91-100/res/shopping-pdm.png index 2b93a21..2516ba3 100644 Binary files a/Day91-100/res/shopping-pdm.png and b/Day91-100/res/shopping-pdm.png differ diff --git a/Day91-100/res/the-daily-scrum-in-the-sprint-cycle.png b/Day91-100/res/the-daily-scrum-in-the-sprint-cycle.png index 2cbb999..e4b9da2 100644 Binary files a/Day91-100/res/the-daily-scrum-in-the-sprint-cycle.png and b/Day91-100/res/the-daily-scrum-in-the-sprint-cycle.png differ diff --git a/Day91-100/res/uml-class-diagram.png b/Day91-100/res/uml-class-diagram.png index 1c4affc..5d79fe6 100644 Binary files a/Day91-100/res/uml-class-diagram.png and b/Day91-100/res/uml-class-diagram.png differ diff --git a/Day91-100/res/uml-graph.png b/Day91-100/res/uml-graph.png index def109b..d7df43a 100644 Binary files a/Day91-100/res/uml-graph.png and b/Day91-100/res/uml-graph.png differ diff --git a/Day91-100/res/uml.png b/Day91-100/res/uml.png index 98168cb..c756dba 100644 Binary files a/Day91-100/res/uml.png and b/Day91-100/res/uml.png differ diff --git a/Day91-100/res/unlock-jenkins.png b/Day91-100/res/unlock-jenkins.png index 9b88c19..25c26bd 100644 Binary files a/Day91-100/res/unlock-jenkins.png and b/Day91-100/res/unlock-jenkins.png differ diff --git a/Day91-100/res/web-application.png b/Day91-100/res/web-application.png index 89d2dec..5a18949 100644 Binary files a/Day91-100/res/web-application.png and b/Day91-100/res/web-application.png differ diff --git a/Day91-100/res/zentao-index.png b/Day91-100/res/zentao-index.png index 5c8491b..34195a7 100644 Binary files a/Day91-100/res/zentao-index.png and b/Day91-100/res/zentao-index.png differ diff --git a/Day91-100/res/zentao-login.png b/Day91-100/res/zentao-login.png index 532700a..206f126 100644 Binary files a/Day91-100/res/zentao-login.png and b/Day91-100/res/zentao-login.png differ diff --git a/README.md b/README.md index 2ae29d0..cc8c2ab 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ ## Python - 100天从新手到大师 -> 作者:骆昊 +> **作者**:骆昊 > -> 最近有很多想学习Python的小伙伴陆陆续续加入我们的交流群,目前我们的交流群人数已经超过一万人。我们的目标是打造一个优质的Python交流社区,一方面为想学习Python的初学者扫平入门过程中的重重障碍;另一方为新入行的开发者提供问道的途径,帮助他们迅速成长为优秀的职业人;此外,有经验的开发者可以利用这个平台把自己的工作经验无偿分享或有偿提供出来,让大家都能够得到职业技能以及综合素质的全面提升。之前的公开课和线下技术交流活动因为工作的关系荒废了一段时间了,但是各位小伙伴仍然活跃在交流群并一如既往的支持我们,在此向大家表示感谢。近期开始持续更新前15天和最后10天的内容,前15天是写给初学者的,我希望把上手的难度进一步降低,例子程序更加简单清晰;最后10天是Python项目实战和面试相关的东西,我希望内容更详实和完整,尤其是第100天的面试题部分;创作不易,感谢大家的打赏支持,这些钱不会用于购买咖啡而是通过腾讯公益平台捐赠给需要帮助的人([点击](./更新日志.md)了解捐赠情况)。 +> **说明**:从项目上线到获得8w+星标以来,一直收到反馈说基础部分(前15天的内容)对新手来说是比较困难的,建议有配套视频进行讲解。最近把基础部分的内容重新创建了一个名为[“Python-Core-50-Courses”](<https://github.com/jackfrued/Python-Core-50-Courses>)的项目,**用更为简单通俗的方式重写了这部分内容并附带了视频讲解**,初学者可以关注下这个新项目。国内用户如果访问GitHub比较慢的话,也可以关注我的知乎号[Python-Jack](https://www.zhihu.com/people/jackfrued)上的[“从零开始学Python”](<https://zhuanlan.zhihu.com/c_1216656665569013760>)专栏,专栏会持续更新,还有大家比较期待的“数据分析”的内容也即将上线,**欢迎大家关注我在知乎的专栏、文章和回答**。 +> +> 创作不易,感谢大家的打赏支持,这些钱基本不会用于购买咖啡,而是通过腾讯公益、美团公益、水滴筹等平台捐赠给需要帮助的人([点击](./更新日志.md)了解捐赠情况)。需要加入QQ交流群的可以扫描下面的二维码,交流群会为大家提供**学习资源**和**问题解答**,还会持续为大家带来**免费的线上Python体验课和行业公开课**,敬请关注。 ![](./res/python-qq-group.png) @@ -43,11 +45,11 @@ 给初学者的几个建议: -- Make English as your working language. -- Practice makes perfect. -- All experience comes from mistakes. -- Don't be one of the leeches. -- Either stand out or kicked out. +- Make English as your working language. (让英语成为你的工作语言) +- Practice makes perfect. (熟能生巧) +- All experience comes from mistakes. (所有的经验都源于你犯过的错误) +- Don't be one of the leeches. (不要当伸手党) +- Either outstanding or out. (要么出众,要么出局) ### Day01~15 - [Python语言基础](./Day01-15) @@ -314,6 +316,7 @@ - 配置和使用uWSGI - 动静分离和Nginx配置 - 配置HTTPS +- 配置域名解析 ### Day56~60 - [实战Flask](./Day56-65) @@ -421,7 +424,9 @@ - 爬取房地产行业数据 - 爬取二手车交易平台数据 -### Day76~90 - [数据处理和机器学习](./Day76-90) +### Day76~90 - [数据分析和机器学习](./Day76-90) + +> **温馨提示**:数据分析和机器学习的内容在code文件夹中,是用jupyter notebook书写的代码和笔记,需要先启动jupyter notebook再打开对应的文件进行学习。2020年会持续补充相关文档,希望大家持续关注。 #### Day76 - [机器学习基础](./Day76-90/76.机器学习基础.md) @@ -451,7 +456,7 @@ #### Day89 - [Tensorflow实战](./Day76-90/89.Tensorflow实战.md) -#### Day90 - [推荐系统](./Day76-90/90.推荐系统.md) +#### Day90 - [推荐系统实战](./Day76-90/90.推荐系统实战.md) ### Day91~100 - [团队项目开发](./Day91-100) @@ -461,16 +466,19 @@ - 经典过程模型(瀑布模型) - 可行性分析(研究做还是不做),输出《可行性分析报告》。 - 需求分析(研究做什么),输出《需求规格说明书》和产品界面原型图。 - - 概要设计和详细设计,输出概念模型图、物理模型图、类图、时序图等。 + - 概要设计和详细设计,输出概念模型图(ER图)、物理模型图、类图、时序图等。 - 编码 / 测试。 - 上线 / 维护。 + + 瀑布模型最大的缺点是无法拥抱需求变化,整套流程结束后才能看到产品,团队士气低落。 - 敏捷开发(Scrum)- 产品所有者、Scrum Master、研发人员 - Sprint - 产品的Backlog(用户故事、产品原型)。 - 计划会议(评估和预算)。 - 日常开发(站立会议、番茄工作法、结对编程、测试先行、代码重构……)。 - 修复bug(问题描述、重现步骤、测试人员、被指派人)。 - - 评审会议(Showcase)。 - - 回顾会议(当前周期做得好和不好的地方)。 + - 发布版本。 + - 评审会议(Showcase,用户需要参与)。 + - 回顾会议(对当前迭代周期做一个总结)。 > 补充:敏捷软件开发宣言 > @@ -487,13 +495,13 @@ > 敏捷团队通常人数为8-10人。 - > 工作量估算:将开发任务量化,包括原型、Logo设计、UI设计、前端开发等,尽量把每个工作分解到最小任务量,最小任务量标准为工作时间不能超过两天,然后估算总体项目时间。把每个任务都贴在白板上面,白板上分三部分:to do(待完成)、in progress(进行中)和done(已完成)。 + > 工作量估算:将开发任务量化,包括原型、Logo设计、UI设计、前端开发等,尽量把每个工作分解到最小任务量,最小任务量标准为工作时间不能超过两天,然后估算总体项目时间。把每个任务都贴在看板上面,看板上分三部分:to do(待完成)、in progress(进行中)和done(已完成)。 2. 项目团队组建 - 团队的构成和角色 - > 说明:谢谢付祥英女士绘制了下面这张精美的公司组织架构图。 + > 说明:谢谢**付祥英**女士帮助我绘制了下面这张精美的公司组织架构图。 ![company_architecture](./res/company_architecture.png) @@ -515,7 +523,7 @@ - 敏捷闭环工具:[禅道](https://www.zentao.net/)、[JIRA](https://www.atlassian.com/software/jira/features) - 持续集成:[Jenkins](https://jenkins.io/)、[Travis-CI](https://travis-ci.org/) - 请参考[《团队项目开发》](Day91-100/团队项目开发.md)。 + 请参考[《团队项目开发的问题和解决方案》](Day91-100/91.团队项目开发的问题和解决方案.md)。 ##### 项目选题和理解业务 @@ -567,14 +575,14 @@ python manage.py inspectdb > app/models.py ``` -#### 第92天:[使用Docker部署应用](./Day91-100/92.使用Docker部署应用.md) +#### 第92天:[Docker容器详解](./Day91-100/92.Docker容器详解.md) 1. Docker简介 2. 安装Docker 3. 使用Docker创建容器(Nginx、MySQL、Redis、Gitlab、Jenkins) 4. 构建Docker镜像(Dockerfile的编写和相关指令) 5. 容器编排(Docker-compose) -6. 集群管理 +6. 集群管理(Kubernetes) #### 第93天:[MySQL性能优化](./Day91-100/93.MySQL性能优化.md) diff --git a/res/agile-scrum-sprint-cycle.png b/res/agile-scrum-sprint-cycle.png index 2cbb999..b9bb663 100644 Binary files a/res/agile-scrum-sprint-cycle.png and b/res/agile-scrum-sprint-cycle.png differ diff --git a/res/company_architecture.png b/res/company_architecture.png index ff3f9cb..d25d5dd 100644 Binary files a/res/company_architecture.png and b/res/company_architecture.png differ diff --git a/res/create-new-repo.png b/res/create-new-repo.png deleted file mode 100644 index d2f070f..0000000 Binary files a/res/create-new-repo.png and /dev/null differ diff --git a/res/dns-configuration.png b/res/dns-configuration.png deleted file mode 100644 index eaa8ec3..0000000 Binary files a/res/dns-configuration.png and /dev/null differ diff --git a/res/donation.png b/res/donation.png deleted file mode 100644 index 9c76603..0000000 Binary files a/res/donation.png and /dev/null differ diff --git a/res/donation1.png b/res/donation1.png new file mode 100644 index 0000000..6eae41b Binary files /dev/null and b/res/donation1.png differ diff --git a/res/donation2.png b/res/donation2.png index f998528..3d9512a 100644 Binary files a/res/donation2.png and b/res/donation2.png differ diff --git a/res/donation3.png b/res/donation3.png new file mode 100644 index 0000000..947702e Binary files /dev/null and b/res/donation3.png differ diff --git a/res/donation4.png b/res/donation4.png new file mode 100644 index 0000000..664865a Binary files /dev/null and b/res/donation4.png differ diff --git a/res/hadoop_ecosystem.png b/res/hadoop_ecosystem.png index 8028fa8..7884121 100644 Binary files a/res/hadoop_ecosystem.png and b/res/hadoop_ecosystem.png differ diff --git a/res/hexo-default-index.png b/res/hexo-default-index.png deleted file mode 100644 index 51c4ae8..0000000 Binary files a/res/hexo-default-index.png and /dev/null differ diff --git a/res/int-is-comparation.png b/res/int-is-comparation.png deleted file mode 100644 index 9b59899..0000000 Binary files a/res/int-is-comparation.png and /dev/null differ diff --git a/res/power-designer-pdm.png b/res/power-designer-pdm.png index 5fba456..eec300d 100644 Binary files a/res/power-designer-pdm.png and b/res/power-designer-pdm.png differ diff --git a/res/pycharm-activate.png b/res/pycharm-activate.png deleted file mode 100644 index 4857818..0000000 Binary files a/res/pycharm-activate.png and /dev/null differ diff --git a/res/pycharm-activation.png b/res/pycharm-activation.png new file mode 100644 index 0000000..ca003b3 Binary files /dev/null and b/res/pycharm-activation.png differ diff --git a/res/pycharm-comm-django-1.png b/res/pycharm-comm-django-1.png deleted file mode 100644 index 615c74e..0000000 Binary files a/res/pycharm-comm-django-1.png and /dev/null differ diff --git a/res/pycharm-comm-django-2.png b/res/pycharm-comm-django-2.png deleted file mode 100644 index 94049bb..0000000 Binary files a/res/pycharm-comm-django-2.png and /dev/null differ diff --git a/res/pycharm-comm-django-3.png b/res/pycharm-comm-django-3.png deleted file mode 100644 index 292283c..0000000 Binary files a/res/pycharm-comm-django-3.png and /dev/null differ diff --git a/res/pycharm-comm-django-4.png b/res/pycharm-comm-django-4.png deleted file mode 100644 index 6bb75bf..0000000 Binary files a/res/pycharm-comm-django-4.png and /dev/null differ diff --git a/res/pycharm-comm-django-5.png b/res/pycharm-comm-django-5.png deleted file mode 100644 index 81c5f6f..0000000 Binary files a/res/pycharm-comm-django-5.png and /dev/null differ diff --git a/res/pycharm-comm-django-6.png b/res/pycharm-comm-django-6.png deleted file mode 100644 index d5a71f1..0000000 Binary files a/res/pycharm-comm-django-6.png and /dev/null differ diff --git a/res/pycharm-comm-django-7.png b/res/pycharm-comm-django-7.png deleted file mode 100644 index 3c0f664..0000000 Binary files a/res/pycharm-comm-django-7.png and /dev/null differ diff --git a/res/pycharm-comm-django-8.png b/res/pycharm-comm-django-8.png deleted file mode 100644 index 83c5ad7..0000000 Binary files a/res/pycharm-comm-django-8.png and /dev/null differ diff --git a/res/pycharm-create-launcher-script.png b/res/pycharm-create-launcher-script.png deleted file mode 100644 index f71b421..0000000 Binary files a/res/pycharm-create-launcher-script.png and /dev/null differ diff --git a/res/pycharm-create-launcher.png b/res/pycharm-create-launcher.png new file mode 100644 index 0000000..9abef2e Binary files /dev/null and b/res/pycharm-create-launcher.png differ diff --git a/res/pycharm-import-settings.png b/res/pycharm-import-settings.png index 4b1d066..dd07c5d 100644 Binary files a/res/pycharm-import-settings.png and b/res/pycharm-import-settings.png differ diff --git a/res/pycharm-install-plugins.png b/res/pycharm-install-plugins.png new file mode 100644 index 0000000..530fb5a Binary files /dev/null and b/res/pycharm-install-plugins.png differ diff --git a/res/pycharm-installation.png b/res/pycharm-installation.png new file mode 100644 index 0000000..00bde72 Binary files /dev/null and b/res/pycharm-installation.png differ diff --git a/res/pycharm-new-project.png b/res/pycharm-new-project.png deleted file mode 100644 index 0af2a2d..0000000 Binary files a/res/pycharm-new-project.png and /dev/null differ diff --git a/res/pycharm-plugins.png b/res/pycharm-plugins.png deleted file mode 100644 index 3ad2e09..0000000 Binary files a/res/pycharm-plugins.png and /dev/null differ diff --git a/res/pycharm-prof-django-2.png b/res/pycharm-prof-django-2.png deleted file mode 100644 index 091ac45..0000000 Binary files a/res/pycharm-prof-django-2.png and /dev/null differ diff --git a/res/pycharm-prof-django-3.png b/res/pycharm-prof-django-3.png deleted file mode 100644 index 537a43d..0000000 Binary files a/res/pycharm-prof-django-3.png and /dev/null differ diff --git a/res/pycharm-project-wizard.png b/res/pycharm-project-wizard.png new file mode 100644 index 0000000..e674dd4 Binary files /dev/null and b/res/pycharm-project-wizard.png differ diff --git a/res/pycharm-run-result.png b/res/pycharm-run-result.png new file mode 100644 index 0000000..321339a Binary files /dev/null and b/res/pycharm-run-result.png differ diff --git a/res/pycharm-set-ui-theme.png b/res/pycharm-set-ui-theme.png deleted file mode 100644 index 12ad8bf..0000000 Binary files a/res/pycharm-set-ui-theme.png and /dev/null differ diff --git a/res/pycharm-ui-themes.png b/res/pycharm-ui-themes.png new file mode 100644 index 0000000..ceda975 Binary files /dev/null and b/res/pycharm-ui-themes.png differ diff --git a/res/pycharm-welcome.png b/res/pycharm-welcome.png index 5f3effa..30a0ec3 100644 Binary files a/res/pycharm-welcome.png and b/res/pycharm-welcome.png differ diff --git a/res/pycharm-workspace.png b/res/pycharm-workspace.png index 9133f00..fac84a4 100644 Binary files a/res/pycharm-workspace.png and b/res/pycharm-workspace.png differ diff --git a/res/pylint.png b/res/pylint.png index 92a9671..78576ae 100644 Binary files a/res/pylint.png and b/res/pylint.png differ diff --git a/res/python-bj-salary.png b/res/python-bj-salary.png index 0577a6c..76440a9 100644 Binary files a/res/python-bj-salary.png and b/res/python-bj-salary.png differ diff --git a/res/python-built-in-functions.png b/res/python-built-in-functions.png index 7e8a9b7..dfa4d2b 100644 Binary files a/res/python-built-in-functions.png and b/res/python-built-in-functions.png differ diff --git a/res/python-job-all.png b/res/python-job-all.png index 3247045..30af773 100644 Binary files a/res/python-job-all.png and b/res/python-job-all.png differ diff --git a/res/python-job-chengdu.png b/res/python-job-chengdu.png index e377ef7..0e6053e 100644 Binary files a/res/python-job-chengdu.png and b/res/python-job-chengdu.png differ diff --git a/res/python-qq-group.png b/res/python-qq-group.png index 023d21d..018cdb6 100644 Binary files a/res/python-qq-group.png and b/res/python-qq-group.png differ diff --git a/res/python-salary-beijing.png b/res/python-salary-beijing.png index 7f8b906..88f4dfc 100644 Binary files a/res/python-salary-beijing.png and b/res/python-salary-beijing.png differ diff --git a/res/python-salary-chengdu.png b/res/python-salary-chengdu.png index 2fcf994..c7fc7a7 100644 Binary files a/res/python-salary-chengdu.png and b/res/python-salary-chengdu.png differ diff --git a/res/python-salary.png b/res/python-salary.png index 9068170..667afa1 100644 Binary files a/res/python-salary.png and b/res/python-salary.png differ diff --git a/res/python-top-10.png b/res/python-top-10.png index ae7d654..9057d38 100644 Binary files a/res/python-top-10.png and b/res/python-top-10.png differ diff --git a/res/python-tutor-visualize.png b/res/python-tutor-visualize.png deleted file mode 100644 index 3cde51f..0000000 Binary files a/res/python-tutor-visualize.png and /dev/null differ diff --git a/res/python-tutor-visualize2.png b/res/python-tutor-visualize2.png deleted file mode 100644 index dbe1d9a..0000000 Binary files a/res/python-tutor-visualize2.png and /dev/null differ diff --git a/res/python_jobs_chengdu.png b/res/python_jobs_chengdu.png index 6b18fa9..355280b 100644 Binary files a/res/python_jobs_chengdu.png and b/res/python_jobs_chengdu.png differ diff --git a/res/python_salary_chengdu.png b/res/python_salary_chengdu.png index fade359..e7072d1 100644 Binary files a/res/python_salary_chengdu.png and b/res/python_salary_chengdu.png differ diff --git a/res/rbac-basic.png b/res/rbac-basic.png index 3cf0025..d47f477 100644 Binary files a/res/rbac-basic.png and b/res/rbac-basic.png differ diff --git a/res/rbac-full.png b/res/rbac-full.png index 5692833..1078596 100644 Binary files a/res/rbac-full.png and b/res/rbac-full.png differ diff --git a/res/requirements_by_xmind.png b/res/requirements_by_xmind.png index aac6447..3178e9a 100644 Binary files a/res/requirements_by_xmind.png and b/res/requirements_by_xmind.png differ diff --git a/res/result-of-dis.png b/res/result-of-dis.png deleted file mode 100644 index 79ce275..0000000 Binary files a/res/result-of-dis.png and /dev/null differ diff --git a/res/uml-class-diagram.png b/res/uml-class-diagram.png index 1c4affc..5d79fe6 100644 Binary files a/res/uml-class-diagram.png and b/res/uml-class-diagram.png differ diff --git a/res/wanwang.png b/res/wanwang.png deleted file mode 100644 index 59d988d..0000000 Binary files a/res/wanwang.png and /dev/null differ diff --git a/res/zen-of-python.png b/res/zen-of-python.png index d879db0..9d9e8fb 100644 Binary files a/res/zen-of-python.png and b/res/zen-of-python.png differ diff --git a/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/droste.png b/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/droste.png index 5d4d71a..6f016a5 100644 Binary files a/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/droste.png and b/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/droste.png differ diff --git a/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/eight_queen.png b/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/eight_queen.png index 84ed539..dbe6fcd 100644 Binary files a/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/eight_queen.png and b/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/eight_queen.png differ diff --git a/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/maze.png b/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/maze.png index 82bf4c4..ee7e330 100644 Binary files a/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/maze.png and b/公开课/文档/第06次公开课-算法入门系列2-在水一方/res/maze.png differ diff --git a/更新日志.md b/更新日志.md index 6c258fb..68dbe37 100644 --- a/更新日志.md +++ b/更新日志.md @@ -1,5 +1,28 @@ ## 更新日志 +### 2020年4月8日 + +1. 将基础部分(第1天到第15天)内容重新创建了一个名为“Python-Core-50-Courses”的仓库,更新了部分内容。 + +2. 更新了README.md文件。 + +3. 将近期收到的打赏通过腾讯公益捐出,助力“儿童健康课堂”。 + + ![](res/donation4.png) + +### 2020年3月8日 + +1. 更新了最后10天的部分文档。 + +2. 通过美团公益将近期打赏捐助给受疫情影响儿童。 + + ![](res/donation3.png) + +### 2020年3月1日 + +1. 优化了项目中的部分图片资源。 +2. 更新了部分文档。 + ### 2019年9月23日 1. 计划在国庆长假结束前,完成第91天到第100天内容的更新,包括最新的Python面试题集。 @@ -29,7 +52,7 @@ 1. 今天终于结束了出差的日子,回来先把最近收到的打赏通过腾讯公益平台全部捐赠给了绿之叶,总共捐出了111笔打赏。 - ![](./res/donation.png) + ![](./res/donation1.png) ### 2019年7月9日 diff --git a/玩转PyCharm.md b/玩转PyCharm.md index cf74f5d..3069dbd 100644 --- a/玩转PyCharm.md +++ b/玩转PyCharm.md @@ -2,94 +2,78 @@ PyCharm是由JetBrains公司开发的提供给Python专业的开发者的一个集成开发环境,它最大的优点是能够大大提升Python开发者的工作效率,为开发者集成了很多用起来非常顺手的功能,包括代码调试、高亮语法、代码跳转、智能提示、自动补全、单元测试、版本控制等等。此外,PyCharm还提供了对一些高级功能的支持,包括支持基于Django框架的Web开发。 -### PyCharm的安装 +### PyCharm的下载和安装 -可以在[JetBrains公司的官方网站]()找到PyCharm的[下载链接](https://www.jetbrains.com/pycharm/download/),有两个可供下载的版本一个是社区版一个是专业版,社区版在[Apache许可证](https://zh.wikipedia.org/wiki/Apache%E8%AE%B8%E5%8F%AF%E8%AF%81)下发布,专业版在专用许可证下发布(需要购买授权下载后可试用30天),其拥有许多额外功能。安装PyCharm需要有JRE(Java运行时环境)的支持,如果没有可以在安装过程中选择在线下载安装。 +可以在[JetBrains公司的官方网站](<https://www.jetbrains.com/>)找到PyCharm的[下载链接](https://www.jetbrains.com/pycharm/download/),有两个可供下载的版本,一个是社区版(PyCharm CE),一个是专业版(PyCharm Professional)。社区版在Apache许可证下发布,可以免费使用;专业版在专用许可证下发布,需要购买授权后才能使用,但新用户可以试用30天。很显然,专业版提供了更为强大的功能和对企业级开发的各种支持,但是对于初学者来说,社区版已经足够强大和好用了。安装PyCharm只需要直接运行下载的安装程序,然后持续的点击“Next”(下一步)按钮就可以啦。下面是我在Windows系统下安装PyCharm的截图,安装完成后点击“Finish”(结束)按钮关闭安装向导,然后可以通过双击桌面的快捷方式来运行PyCharm。 -> 说明:如果你是一名学生,希望购买PyCharm来使用,可以看看[教育优惠官方申请指南](https://sales.jetbrains.com/hc/zh-cn/articles/207154369)。 +![](res/pycharm-installation.png) ### 首次使用的设置 -第一次使用PyCharm时,会有一个导入设置的向导,如果之前没有使用PyCharm或者没有保存过设置的就直接选择“Do not import settings”进入下一步即可。 +第一次使用PyCharm时,会有一个导入设置的向导,如果之前没有使用PyCharm或者没有保存过设置的就直接选择“Do not import settings”进入下一步即可,下面是我在macOS系统下第一次使用PyCharm时的截图。 ![](./res/pycharm-import-settings.png) -专业版的PyCharm是需要激活的,**强烈建议为优秀的软件支付费用**,如果不用做商业用途,我们可以暂时选择试用30天或者使用社区版的PyCharm。 +专业版的PyCharm是需要激活的,**强烈建议大家在条件允许的情况下支付费用来支持优秀的产品**,如果不用做商业用途或者不需要使用PyCharm的高级功能,我们可以暂时选择试用30天或者使用社区版的PyCharm。如果你是一名学生,希望购买PyCharm来使用,可以看看[教育优惠官方申请指南](https://sales.jetbrains.com/hc/zh-cn/articles/207154369)。如下图所示,我们需要点击“Evaluate”按钮来试用专业版PyCharm。 -![](./res/pycharm-activate.png) +![](./res/pycharm-activation.png) - 接下来是选择UI主题,这个可以根据个人喜好进行选择。 +接下来是选择UI主题,可以根据个人喜好进行选择,深色的主题比较护眼而浅色的主题对比度更好。 -![](./res/pycharm-set-ui-theme.png) +![](./res/pycharm-ui-themes.png) - 再接下来是创建可以在终端(命令行)中使用PyCharm项目的启动脚本,当然也可以直接跳过这一步。 +再接下来是创建可以在“终端”或“命令行提示符”中运行PyCharm的启动脚本,当然也可以不做任何勾选,直接点击“Next: Featured plugins”按钮进入下一环节。 -![](./res/pycharm-create-launcher-script.png) +![](./res/pycharm-create-launcher.png) -然后可以选择需要安装哪些插件,我们可以暂时什么都不安装等需要的时候再来决定。 +然后可以选择需要安装哪些插件,我们可以暂时什么都不安装,等需要的时候再来决定。 -![](./res/pycharm-plugins.png) +![](./res/pycharm-install-plugins.png) + +最后点击上图右下角的“Start using PyCharm”(开始使用PyCharm)就可以开启你的PyCharm之旅了。 ### 用PyCharm创建项目 -点击上图中的“Start using PyCharm”按钮就可以开始使用PyCharm啦,首先来到的是一个欢迎页,在欢迎页上我们可以选择“创建新项目”、“打开已有项目”和“从版本控制系统中检出项目”。 +启动PyCharm之后会来到一个欢迎页,在欢迎页上我们可以选择“创建新项目”(Create New Project)、“打开已有项目”(Open)和“从版本控制系统中检出项目”(Get from Version Control)。 ![](./res/pycharm-welcome.png) -如果选择了“Create New Project”来创建新项目就会打一个创建项目的向导页。 +如果选择了“Create New Project”来创建新项目就会打一个创建项目的向导页。下图所示是PyCharm专业版创建新项目的向导页,可以看出专业版支持的项目类型非常的多,而社区版只能创建纯Python项目(Pure Python),没有这一系列的选项。 -![](./res/pycharm-new-project.png) +![](./res/pycharm-project-wizard.png) -在如上图所示的界面中,我们可以选择创建项目的模板,包括了纯Python项目、基于各种不同框架的Web项目、Web前端项目、跨平台项目等各种不同的项目模板。如果选择Python的项目,那么有一个非常重要的设定是选择“New environment…”(创建新的虚拟环境)还是使用“Existing Interpreter”(已经存在的解释器)。前者肯定是更好的选择,因为新的虚拟环境不会对系统环境变量中配置的Python环境造成影响,简单举个例子就是你在虚拟环境下安装或者更新了任何三方库,它并不会对系统原有的Python解释器造成任何的影响,但代价是需要额外的存储空间来建立这个虚拟环境。 +接下来,我们要为项目创建专属的虚拟环境,每个Python项目最好都在自己专属的虚拟环境中运行,因为每个项目对Python解释器和三方库的需求并不相同,虚拟环境对不同的项目进行了隔离。在上图所示的界面在,我们可以选择新建虚拟环境(New environment using Virtualenv),这里的“Virtualenv”是PyCharm默认选择的创建虚拟环境的工具,我们就保留这个默认的选项就可以了。 -项目创建完成后就可以开始新建各种文件来书写Python代码了。 +项目创建完成后就可以开始新建各种文件来书写Python代码了,如下图所示。左侧是项目浏览器,可以看到刚才创建的项目文件夹以及虚拟环境文件夹。我们可以在项目上点击鼠标右键,选择“New”,在选择“Python File”来创建Python代码文件,下图中我们创建了两个Python文件,分别是`poker_game.py`和`salary_system.py`。当然,如果愿意,也可以使用复制粘贴的方式把其他地方的Python代码文件复制到项目文件夹下。 ![](./res/pycharm-workspace.png) -在工作窗口的右键菜单中可以找到“Run ...”和“Debug ...”菜单项,通过这两个菜单项我们就可以运行和调试我们的代码啦。建议关注一下菜单栏中的“Code”、“Refactor”和“Tools”菜单,这里面为编写Python代码提供了很多有用的帮助。 +在工作窗口点击鼠标右键可以在上下文菜单中找到“Run”选项,例如要运行`salary_system.py`文件,右键菜单会显示“Run 'salary_system'”选项,点击这个选项我们就可以运行Python代码啦,运行结果在屏幕下方的窗口可以看到,如下图所示。 -### 创建Django项目 +![](res/pycharm-run-result.png) -#### 专业版 +### 常用操作和快捷键 -PyCharm专业版提供了对Django、Flask、Google App Engine、web2py等Python Web框架以及SQL、UML、前端语言和框架、远程调试、虚拟化部署等功能的支持,如果使用PyCharm专业版,在创建项目时可以直接选择创建Django项目并设置模板语言以及放置模板页的文件夹。 +PyCharm为写Python代码提供了自动补全和高亮语法功能,这也是PyCharm作为集成开发环境(IDE)的基本功能。PyCharm的“File”菜单有一个“Settings”菜单项(macOS上是在“PyCharm”菜单的“Preferences…”菜单项),这个菜单项会打开设置窗口,可以在此处对PyCharm进行设置,如下图所示。 -![](./res/pycharm-prof-django-3.png) +![](/Users/Hao/Desktop/Python-Core-50-Courses/res/pycharm-settings.png) -创建好项目之后,打开终端输入`pip list`命令,可以看到项目所需的依赖项已经安装好了,而且可以直接点击屏幕右上方的运行或调试按钮来直接运行Django项目。 +PyCharm的菜单项中有一个非常有用的“Code”菜单,菜单中提供了自动生成代码、自动补全代码、格式化代码、移动代码等选项,这些功能对开发者来说是非常有用的,大家可以尝试使用这些菜单项或者记住它们对应的快捷键,例如在macOS上,格式化代码这个菜单项对应的快捷键是`alt+command+L`。除此之外,“Refactor”菜单也非常有用,它提供了一些重构代码的选项。所谓重构是在不改变代码执行结果的前提下调整代码的结构,这也是资深程序员的一项重要技能。还有一个值得一提的菜单是“VCS”,VCS是“Version Control System”(版本控制系统)的缩写,这个菜单提供了对代码版本管理的支持。版本控制的知识会在其他的课程中为大家讲解。 -![](./res/pycharm-prof-django-2.png) +下表列出了一些PyCharm中特别常用的快捷键,当然如果愿意,也可以通过设置窗口中“Keymap”菜单项自定义快捷键,PyCharm本身也针对不同的操作系统和使用习惯对快捷键进行了分组。 -#### 社区版 +| 快捷键 | 作用 | +| --------------------------------------------- | -------------------------------------- | +| `command + j` | 显示可用的代码模板 | +| `command + b` | 查看函数、类、方法的定义 | +| `ctrl + space` | 万能代码提示快捷键,一下不行按两下 | +| `command + alt + l` | 格式化代码 | +| `alt + enter` | 万能代码修复快捷键 | +| `ctrl + /` | 注释/反注释代码 | +| `shift + shift` | 万能搜索快捷键 | +| `command + d` / `command + y` | 复制/删除一行代码 | +| `command + shift + -` / `command + shift + +` | 折叠/展开所有代码 | +| `F2` | 快速定位到错误代码 | +| `command+ alt + F7` | 查看哪些地方用到了指定的函数、类、方法 | -PyCharm社区版只能创建Python项目,如果项目中需要Django的支持,可以自行安装依赖库并创建Django项目。 - -![](./res/pycharm-comm-django-1.png) - -创建好Python项目之后,可以打开屏幕下方的终端(Terminal),并通过`pip install`安装Django项目的依赖项,可以通过`-i https://pypi.doubanio.com/simple`来指定下载依赖库的镜像仓库。 - -![](./res/pycharm-comm-django-2.png) - -当然也可以在项目的设置菜单中找到解释器配置,并选择要添加的依赖项。 - -![](./res/pycharm-comm-django-7.png) - -下面是搜索依赖项的界面,可以通过点击“Install Package”按钮来安装指定的依赖项;也可以通过点击“Manage Repositories”按钮来指定下载依赖项的仓库,国内用户推荐使用豆瓣镜像<http://pypi.doubanio.com/simple>。 - -![](./res/pycharm-comm-django-8.png) - -接下来可以在终端中输入`django-amdin startproject`指令来创建项目。 - -![](./res/pycharm-comm-django-3.png) - -如果要运行项目,可以在终端中输入`python manage.py runserver`启动测试服务器。当然,也可以点击屏幕右上方的“Add Configuration”按钮,进入如下所示的配置界面,并点击窗口左上角的“+”来添加一个运行配置。 - -![](./res/pycharm-comm-django-4.png) - -在配置窗口的右侧,指定要执行的脚本路径(Django项目的manage.py文件的位置)和运行参数(runserver),运行参数的后面还可以跟IP地址和端口。 - -![](./res/pycharm-comm-django-5.png) - -注意到窗口的右上角了吗?现在可以点击运行或调试按钮来启动测试服务器运行项目了。 - -![](./res/pycharm-comm-django-6.png) \ No newline at end of file +> **说明**:Windows系统下如果使用PyCharm的默认设置,可以将上面的`command`键换成`ctrl`键即可,唯一的例外是`ctrl + space`那个快捷键,因为它跟Windows系统切换输入法的快捷键是冲突的,所以在Windows系统下默认没有与之对应的快捷键。 \ No newline at end of file diff --git a/番外篇/res/create-new-repo.png b/番外篇/res/create-new-repo.png new file mode 100644 index 0000000..36d0f10 Binary files /dev/null and b/番外篇/res/create-new-repo.png differ diff --git a/番外篇/res/dns-configuration.png b/番外篇/res/dns-configuration.png new file mode 100644 index 0000000..a706082 Binary files /dev/null and b/番外篇/res/dns-configuration.png differ diff --git a/番外篇/res/hexo-default-index.png b/番外篇/res/hexo-default-index.png new file mode 100644 index 0000000..1884217 Binary files /dev/null and b/番外篇/res/hexo-default-index.png differ diff --git a/番外篇/res/int-is-comparation.png b/番外篇/res/int-is-comparation.png new file mode 100644 index 0000000..3031664 Binary files /dev/null and b/番外篇/res/int-is-comparation.png differ diff --git a/番外篇/res/python-tutor-visualize.png b/番外篇/res/python-tutor-visualize.png new file mode 100644 index 0000000..4a27f21 Binary files /dev/null and b/番外篇/res/python-tutor-visualize.png differ diff --git a/番外篇/res/python-tutor-visualize2.png b/番外篇/res/python-tutor-visualize2.png new file mode 100644 index 0000000..1580552 Binary files /dev/null and b/番外篇/res/python-tutor-visualize2.png differ diff --git a/番外篇/res/result-of-dis.png b/番外篇/res/result-of-dis.png new file mode 100644 index 0000000..cf9ffc6 Binary files /dev/null and b/番外篇/res/result-of-dis.png differ diff --git a/番外篇/res/wanwang.png b/番外篇/res/wanwang.png new file mode 100644 index 0000000..360cf64 Binary files /dev/null and b/番外篇/res/wanwang.png differ diff --git a/番外篇/一个小例子助你彻底理解协程.md b/番外篇/一个小例子助你彻底理解协程.md new file mode 100644 index 0000000..646266a --- /dev/null +++ b/番外篇/一个小例子助你彻底理解协程.md @@ -0,0 +1,53 @@ +## 一个小例子助你彻底理解协程 + +协程,可能是Python中最让初学者困惑的知识点之一,它也是Python中实现并发编程的一种重要方式。Python中可以使用多线程和多进程来实现并发,这两种方式相对来说是大家比较熟悉的。事实上,还有一种实现并发的方式叫做异步编程,而协程就是实现异步编程的必要方式。 + +所谓协程,可以简单的理解为多个相互协作的子程序。在同一个线程中,当一个子程序阻塞时,我们可以让程序马上从一个子程序切换到另一个子程序,从而避免CPU因程序阻塞而闲置,这样就可以提升CPU的利用率,相当于用一种协作的方式加速了程序的执行。所以,我们可以言简意赅的说:**协程实现了协作式并发**。 + +接下来用一个小例子帮助大家理解什么是协作式并发,先看看下面的代码。 + +```Python +import time + + +def display(num): + time.sleep(1) + print(num) + + +for num in range(10): + display(num) +``` + +上面这段代码相信大家很容看懂,程序会输出0到9的数字,每隔1秒中输出一个数字,因此整个程序的执行需要大约10秒时间。值得注意的是,因为没有使用多线程或多进程,程序中只有一个执行单元,而`time.sleep(1)`的休眠操作会让整个线程停滞1秒钟,对于上面的代码来说,在这段时间里面CPU是完全闲置的没有做什么事情。 + +我们再来看看使用协程会发生什么事情。从Python 3.5开始,使用协程实现协作式编发有了更为便捷的语法,我们可以使用`async`来定义异步函数,可以使用`await`让一个阻塞的子程序将CPU让给与它协作的子程序。在Python 3.7中,`asyanc`和`await`成为了正式的关键字,让开发者有一种喜大普奔的感觉。我们先看看如何定义一个异步函数。 + +```Python +import asyncio + + +async def display(num): + await asyncio.sleep(1) + print(num) +``` + +接下来敲黑板说重点。异步函数不同于普通函数,调用普通函数会得到返回值,而调用异步函数会得到一个协程对象。我们需要将协程对象放到一个事件循环中才能达到与其他协程对象协作的效果,因为事件循环会负责处理子程序切换的操作,简单的说就是让阻塞的子程序让出CPU给可以执行的子程序。 + +我们先通过下面的列表生成式来代码10个协程对象,跟刚才在循环中调用display函数的道理一致。 + +```Python +coroutines = [display(num) for num in range(10)] +``` + +通过下面的代码可以获取事件循环并将协程对象放入事件循环中。 + +```Python +loop = asyncio.get_event_loop() +loop.run_until_complete(asyncio.wait(coroutines)) +loop.close() +``` + +执行上面的代码会发现,10个分别会阻塞1秒钟的协程总共只阻塞了约1秒种的时间,这就说明**协程对象一旦阻塞会将CPU让出而不是让CPU处于闲置状态**,这样就大大的**提升了CPU的利用率**。而且我们还会注意到,0到9的数字并不是按照我们创建协程对象的顺序打印出来的,这正是我们想要的结果啊;另外,多次执行该程序会发现每次输出的结果都不太一样,这正是并发程序本身执行顺序不确定性造成的结果。 + +上面的例子来自于著名的“花书”(《Python高级并发编程》),为了让大家对协程的体会更加深刻,对原书的代码做了小的改动,这个例子虽然简单,但是它已经让你体会到了协作式并发的魅力。在商业项目中,如果需要使用协作式并发,还可以将系统默认的事件循环替换为`uvloop`提供的事件循环,这样会获得更好的性能,因为`uvloop`是基于著名的跨平台异步I/O库libuv实现的。另外,如果要做基于HTTP的网络编程,三方库**aiohttp**是不错的选择,它基于asyncio实现了异步的HTTP服务器和客户端。 \ No newline at end of file diff --git a/番外篇/为什么我选择了Python.md b/番外篇/我为什么选择了Python.md similarity index 98% rename from 番外篇/为什么我选择了Python.md rename to 番外篇/我为什么选择了Python.md index d09ca7a..88973bd 100644 --- a/番外篇/为什么我选择了Python.md +++ b/番外篇/我为什么选择了Python.md @@ -1,4 +1,4 @@ -## 为什么我选择了Python +## 我为什么选择了Python 目前,Python语言的发展势头在国内国外都是不可阻挡的,Python凭借其简单优雅的语法,强大的生态圈从众多语言中脱颖而出,如今已经是稳坐编程语言排行榜前三的位置。国内很多Python开发者都是从Java开发者跨界过来的,我自己也不例外。我简单的跟大家交代一下,我为什么选择了Python。 diff --git a/番外篇/用函数还是用复杂的表达式.md b/番外篇/用函数还是用复杂的表达式.md index b084543..5fa64cc 100644 --- a/番外篇/用函数还是用复杂的表达式.md +++ b/番外篇/用函数还是用复杂的表达式.md @@ -1,4 +1,4 @@ -## 要不要使用复杂表达式 +## 用函数还是用复杂的表达式 Perl语言的原作者*Larry Wall*曾经说过,伟大的程序员都有三个优点:懒惰、暴躁和自负。乍一看这三个词语没有一个是褒义词,但在程序员的世界里,这三个词有不同的意义。首先,懒惰会促使程序员去写一些省事儿的程序来辅助自己或别人更好的完成工作,这样我们就无需做那些重复和繁琐的劳动;同理能够用3行代码解决的事情,我们也绝不会写出10行代码来。其次,暴躁会让程序员主动的去完成一些你还没有提出的工作,去优化自己的代码让它更有效率,能够3秒钟完成的任务,我们绝不能容忍1分钟的等待。最后,自负会促使程序员写出可靠无误的代码,我们写代码不是为了接受批评和指责,而是为了让其他人来膜拜。 diff --git a/番外篇/FTX(租房项目)接口文档.md b/番外篇/租房网项目接口文档.md similarity index 98% rename from 番外篇/FTX(租房项目)接口文档.md rename to 番外篇/租房网项目接口文档.md index 67c99f4..2b3f575 100644 --- a/番外篇/FTX(租房项目)接口文档.md +++ b/番外篇/租房网项目接口文档.md @@ -1,4 +1,4 @@ -## FTX项目(租房网站)接口文档 +## 租房网项目接口文档 0. 用户登录 - **POST** `/api/login/` @@ -8,7 +8,7 @@ 最后修改时间: - 接口说明:登录成功后,会在`tb_user_token`表中保存或更新用户令牌(token)。 + 接口说明:登录成功后,会生成或更新用户令牌(token)。 使用帮助:测试数据库中预设了四个可供使用的账号,如下表所示。 diff --git a/番外篇/那些年我们踩过的那些坑.md b/番外篇/那些年我们踩过的那些坑.md index f9cd50d..e8a92f0 100644 --- a/番外篇/那些年我们踩过的那些坑.md +++ b/番外篇/那些年我们踩过的那些坑.md @@ -7,7 +7,7 @@ - `is`比较的是两个整数对象的id值是否相等,也就是比较两个引用是否代表了内存中同一个地址。 - `==`比较的是两个整数对象的内容是否相等,使用`==`时其实是调用了对象的`__eq__()`方法。 -知道了`is`和`==`的区别之后,我们可以来看看下面的代码,了解Python中整数比较有哪些坑: +知道了`is`和`==`的区别之后,我们可以来看看下面的代码,了解Python中整数比较有哪些坑,**以CPython解释器为例**,大家先看看下面的代码。 ```Python def main(): @@ -36,7 +36,7 @@ if __name__ == '__main__': main() ``` -上面代码的部分运行结果如下图所示,出现这个结果的原因是Python出于对性能的考虑所做的一项优化。对于整数对象,Python把一些频繁使用的整数对象缓存起来,保存到一个叫`small_ints`的链表中,在Python的整个生命周期内,任何需要引用这些整数对象的地方,都不再重新创建新的对象,而是直接引用缓存中的对象。Python把频繁使用的整数对象的值定在[-5, 256]这个区间,如果需要这个范围的整数,就直接从`small_ints`中获取引用而不是临时创建新的对象。因为大于256或小于-5的整数不在该范围之内,所以就算两个整数的值是一样,但它们是不同的对象。 +上面代码的部分运行结果如下图所示。这个结果是因为CPython出于性能优化的考虑,把频繁使用的整数对象用一个叫`small_ints`的对象池缓存起来造成的。`small_ints`缓存的整数值被设定为`[-5, 256]`这个区间,也就是说,如果使用CPython解释器,在任何引用这些整数的地方,都不需要重新创建`int`对象,而是直接引用缓存池中的对象。如果整数不在该范围内,那么即便两个整数的值相同,它们也是不同的对象。 ![](./res/int-is-comparation.png) @@ -58,7 +58,7 @@ if __name__ == "__main__": main() ``` -程序的执行结果已经用注释写在代码上了。够坑吧!看上去`a`、`b`和`c`的值都是一样的,但是`is`运算的结果却不一样。为什么会出现这样的结果,首先我们来说说Python程序中的代码块。所谓代码块是程序的一个最小的基本执行单位,一个模块文件、一个函数体、一个类、交互式命令中的单行代码都叫做一个代码块。上面的代码由两个代码块构成,`a = 257`是一个代码块,`main`函数是另外一个代码块。Python内部为了进一步提高性能,凡是在一个代码块中创建的整数对象,如果值不在`small_ints`缓存范围之内,但在同一个代码块中已经存在一个值与其相同的整数对象了,那么就直接引用该对象,否则创建一个新的对象出来,这条规则对不在`small_ints`范围的负数并不适用,对负数值浮点数也不适用,但对非负浮点数和字符串都是适用的,这一点读者可以自行证明。所以 `b is c`返回了`True`,而`a`和`b`不在同一个代码块中,虽然值都是257,但却是两个不同的对象,`is`运算的结果自然是`False`了。 +程序的执行结果已经用注释写在代码上了。够坑吧!看上去`a`、`b`和`c`的值都是一样的,但是`is`运算的结果却不一样。为什么会出现这样的结果,首先我们来说说Python程序中的代码块。所谓代码块是程序的一个最小的基本执行单位,一个模块文件、一个函数体、一个类、交互式命令中的单行代码都叫做一个代码块。上面的代码由两个代码块构成,`a = 257`是一个代码块,`main`函数是另外一个代码块。CPython内部为了进一步提高性能,凡是在一个代码块中创建的整数对象,如果值不在`small_ints`缓存范围之内,但在同一个代码块中已经存在一个值与其相同的整数对象了,那么就直接引用该对象,否则创建一个新的对象出来,在早些时候的CPython中,这条规则对负数并不适用,但对非负整数、浮点数和字符串都是适用的,这一点读者可以自行证明。所以 `b is c`返回了`True`,而`a`和`b`不在同一个代码块中,虽然值都是257,但却是两个不同的对象,`is`运算的结果自然是`False`了。 为了验证刚刚的结论,我们可以借用`dis`模块(听名字就知道是进行反汇编的模块)从字节码的角度来看看这段代码。如果不理解什么是字节码,可以先看看[《谈谈 Python 程序的运行原理》]((http://www.cnblogs.com/restran/p/4903056.html))这篇文章。可以先用`import dis`导入`dis`模块并按照如下所示的方式修改代码。 ```Python