|
@ -5,36 +5,36 @@
|
|||
#### Python的历史
|
||||
|
||||
1. 1989年圣诞节:Guido von Rossum开始写Python语言的编译器。
|
||||
2. 1991年2月:第一个Python编译器(同时也是解释器)诞生,它是用C语言实现的(后面又出现了Java和C#实现的版本Jython和IronPython,除此之外还有PyPy、Brython、Pyston等其他实现),可以调用C语言的库函数。在最早的版本中,Python已经提供了对“类”,“函数”,“异常处理”等构造块的支持,还有对列表、字典等核心数据类型,同时支持以模块为基础来构造应用程序。
|
||||
2. 1991年2月:第一个Python编译器(同时也是解释器)诞生,它是用C语言实现的(后面),可以调用C语言的库函数。在最早的版本中,Python已经提供了对“类”,“函数”,“异常处理”等构造块的支持,还有对列表、字典等核心数据类型,同时支持以模块为基础来构造应用程序。
|
||||
3. 1994年1月:Python 1.0正式发布。
|
||||
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等版本,有兴趣的读者可以自行了解。
|
||||
|
||||
#### Windows环境
|
||||
|
||||
|
@ -55,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
|
||||
```
|
||||
|
@ -93,7 +93,7 @@ source .bash_profile
|
|||
|
||||
macOS也自带了Python 2.x版本,可以通过[Python的官方网站](https://www.python.org)提供的安装文件(pkg文件)安装Python 3.x的版本。默认安装完成后,可以通过在终端执行`python`命令来启动2.x版本的Python解释器,启动3.x版本的Python解释器需要执行`python3`命令。
|
||||
|
||||
### 从终端运行Python程序
|
||||
### 运行Python程序
|
||||
|
||||
#### 确认Python的版本
|
||||
|
||||
|
@ -102,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
|
||||
|
@ -139,7 +139,7 @@ python hello.py
|
|||
python3 hello.py
|
||||
```
|
||||
|
||||
### 代码中的注释
|
||||
#### 代码中的注释
|
||||
|
||||
注释是编程语言的一个重要组成部分,用于在源代码中解释代码的作用从而增强程序的可读性和可维护性,当然也可以将源代码中不需要参与运行的代码段通过注释来去掉,这一点在调试程序的时候经常用到。注释在随源代码进入预处理器或编译时会被移除,不会在目标代码中保留也不会影响程序的执行结果。
|
||||
|
||||
|
@ -154,15 +154,11 @@ python3 hello.py
|
|||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
print('hello, world!')
|
||||
# print("你好,世界!")
|
||||
print('你好', '世界')
|
||||
print('hello', 'world', sep=', ', end='!')
|
||||
print('goodbye, world', end='!\n')
|
||||
# print("你好, 世界!")
|
||||
```
|
||||
|
||||
### 其他工具介绍
|
||||
### Python开发工具
|
||||
|
||||
#### IDLE - 自带的集成开发工具
|
||||
|
||||
|
@ -172,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
|
||||
|
@ -188,11 +184,11 @@ pip3 install ipython
|
|||
|
||||
![](./res/python-ipython.png)
|
||||
|
||||
#### Sublime - 高级文本编辑器
|
||||
#### Sublime Text - 高级文本编辑器
|
||||
|
||||
![](./res/python-sublime.png)
|
||||
|
||||
- 首先可以通过[官方网站](https://www.sublimetext.com/)下载安装程序安装Sublime 3或Sublime 2。
|
||||
- 首先可以通过[官方网站](https://www.sublimetext.com/)下载安装程序安装Sublime Text 3或Sublime Text 2。
|
||||
|
||||
- 安装包管理工具。
|
||||
1. 通过快捷键Ctrl+`或者在View菜单中选择Show Console打开控制台,输入下面的代码。
|
||||
|
@ -207,9 +203,7 @@ pip3 install ipython
|
|||
```Python
|
||||
import urllib2,os;pf='Package Control.sublime-package';ipp=sublime.installed_packages_path();os.makedirs(ipp)ifnotos.path.exists(ipp)elseNone;urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler()));open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read());print('Please restart Sublime Text to finish installation')
|
||||
```
|
||||
2. 手动安装浏览器输入 https://sublime.wbond.net/Package%20Control.sublime-package 下载这个文件
|
||||
下载好以后,打开sublime text,选择菜单Preferences->Browse Packages... 打开安装目录
|
||||
此时会进入到一个叫做Packages的目录下,点击进入上一层目录Sublime Text3,在此目录下有一个文件夹叫做Installed Packages,把刚才下载的文件放到这里就可以了。然后重启sublime text3,观察Preferences菜单最下边是否有Package Settings 和Package Control两个选项,如果有,则代表安装成功了。
|
||||
2. 在浏览器中输入 https://sublime.wbond.net/Package%20Control.sublime-package 下载包管理工具的安装包,并找到安装Sublime目录下名为"Installed Packages"的目录,把刚才下载的文件放到这个文件加下,然后重启Sublime Text就搞定了。
|
||||
|
||||
|
||||
- 安装插件。通过Preference菜单的Package Control或快捷键Ctrl+Shift+P打开命令面板,在面板中输入Install Package就可以找到安装插件的工具,然后再查找需要的插件。我们推荐大家安装以下几个插件:
|
||||
|
@ -220,7 +214,7 @@ pip3 install ipython
|
|||
- Python PEP8 Autoformat - PEP8规范自动格式化插件。
|
||||
- ConvertToUTF8 - 将本地编码转换为UTF-8。
|
||||
|
||||
> 说明:事实上[Visual Studio Code]()可能是更好的选择,它不用花钱并提供了更为完整和强大的功能,有兴趣的读者可以自行研究。
|
||||
> **说明**:事实上[Visual Studio Code](<https://code.visualstudio.com/>)可能是更好的选择,它不用花钱并提供了更为完整和强大的功能,有兴趣的读者可以自行研究。
|
||||
|
||||
#### PyCharm - Python开发神器
|
||||
|
||||
|
@ -230,18 +224,17 @@ PyCharm的安装、配置和使用在[《玩转PyCharm》](../玩转PyCharm.md)
|
|||
|
||||
### 练习
|
||||
|
||||
1. 在Python交互环境中查看下面的代码结果,并试着将这些内容翻译成中文。
|
||||
1. 在Python交互式环境中输入下面的代码并查看结果,请尝试将看到的内容翻译成中文。
|
||||
|
||||
```Python
|
||||
import this
|
||||
```
|
||||
|
||||
> 说明:当前键入上面的命令后会在交互式环境中看到如下所示的输出,这段内容被称为“Python之禅”,里面讲述的道理不仅仅适用于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
|
||||
|
@ -259,3 +252,5 @@ PyCharm的安装、配置和使用在[《玩转PyCharm》](../玩转PyCharm.md)
|
|||
|
||||
turtle.mainloop()
|
||||
```
|
||||
|
||||
> **提示**:本章提供的代码中还有画国旗和画小猪佩奇的代码,有兴趣的读者请自行研究。
|
||||
|
|
|
@ -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,33 +37,59 @@
|
|||
|
||||
```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`函数对变量的类型进行检查。程序设计中函数的概念跟数学上函数的概念是一致的,数学上的函数相信大家并不陌生,它包括了函数名、自变量和因变量。如果暂时不理解这个概念也不要紧,我们会在后续的章节中专门讲解函数的定义和使用。
|
||||
|
||||
```Python
|
||||
"""
|
||||
使用input()函数获取键盘输入
|
||||
使用int()进行类型转换
|
||||
用占位符格式化输出的字符串
|
||||
使用type()检查变量的类型
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
a = 100
|
||||
b = 12.345
|
||||
c = 1 + 5j
|
||||
d = 'hello, world'
|
||||
e = True
|
||||
print(type(a)) # <class 'int'>
|
||||
print(type(b)) # <class 'float'>
|
||||
print(type(c)) # <class 'complex'>
|
||||
print(type(d)) # <class 'str'>
|
||||
print(type(e)) # <class 'bool'>
|
||||
```
|
||||
|
||||
可以使用Python中内置的函数对变量类型进行转换。
|
||||
|
||||
- `int()`:将一个数值或字符串转换成整数,可以指定进制。
|
||||
- `float()`:将一个字符串转换成浮点数。
|
||||
- `str()`:将指定的对象转换成字符串形式,可以指定编码。
|
||||
- `chr()`:将整数转换成该编码对应的字符串(一个字符)。
|
||||
- `ord()`:将字符串(一个字符)转换成对应的编码(整数)。
|
||||
|
||||
下面的代码通过键盘输入两个整数来实现对两个整数的算术运算。
|
||||
|
||||
```Python
|
||||
"""
|
||||
使用input()函数获取键盘输入(字符串)
|
||||
使用int()函数将输入的字符串转换成整数
|
||||
使用print()函数输出带占位符的字符串
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
a = int(input('a = '))
|
||||
b = int(input('b = '))
|
||||
print('%d + %d = %d' % (a, b, a + b))
|
||||
|
@ -75,38 +101,11 @@ print('%d %% %d = %d' % (a, b, a % b))
|
|||
print('%d ** %d = %d' % (a, b, a ** b))
|
||||
```
|
||||
|
||||
```Python
|
||||
"""
|
||||
使用type()检查变量的类型
|
||||
|
||||
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))
|
||||
```
|
||||
|
||||
在对变量类型进行转换时可以使用Python的内置函数。
|
||||
|
||||
- `int()`:将一个数值或字符串转换成整数,可以指定进制。
|
||||
- `float()`:将一个字符串转换成浮点数。
|
||||
- `str()`:将指定的对象转换成字符串形式,可以指定编码。
|
||||
- `chr()`:将整数转换成该编码对应的字符串(一个字符)。
|
||||
- `ord()`:将字符串(一个字符)转换成对应的编码(整数)。
|
||||
> **说明**:上面的print函数中输出的字符串使用了占位符语法,其中`%d`是整数的占位符,`%f`是小数的占位符,`%%`表示百分号(因为百分号代表了占位符,所以带占位符的字符串中要表示百分号必须写成`%%`),字符串之后的`%`后面跟的变量值会替换掉占位符然后输出到终端中,运行上面的程序,看看程序执行结果就明白啦。
|
||||
|
||||
### 运算符
|
||||
|
||||
Python支持多种运算符,下表大致按照优先级从高到低的顺序列出了所有的运算符,我们会陆续使用到它们。
|
||||
Python支持多种运算符,下表大致按照优先级从高到低的顺序列出了所有的运算符,运算符的优先级指的是多个运算符同时出现时,先做什么运算然后再做什么运算。除了我们之前已经用过的赋值运算符和算术运算符,我们稍后会陆续讲到其他运算符的使用。
|
||||
|
||||
| 运算符 | 描述 |
|
||||
| ------------------------------------------------------------ | ------------------------------ |
|
||||
|
@ -127,61 +126,83 @@ Python支持多种运算符,下表大致按照优先级从高到低的顺序
|
|||
|
||||
>**说明:** 在实际开发中,如果搞不清楚运算符的优先级,可以使用括号来确保运算的执行顺序。
|
||||
|
||||
下面的例子演示了运算符的使用。
|
||||
#### 赋值运算符
|
||||
|
||||
赋值运算符应该是最为常见的运算符,它的作用是将右边的值赋给左边的变量。下面的例子演示了赋值运算符和复合赋值运算符的使用。
|
||||
|
||||
```Python
|
||||
"""
|
||||
运算符的使用
|
||||
赋值运算符和复合赋值运算符
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
a = 10
|
||||
b = 3
|
||||
a += b # 相当于:a = a + b
|
||||
a *= a + 2 # 相当于:a = a * (a + 2)
|
||||
print(a) # 算一下这里会输出什么
|
||||
```
|
||||
|
||||
a = 5
|
||||
b = 10
|
||||
c = 3
|
||||
d = 4
|
||||
e = 5
|
||||
a += b
|
||||
a -= c
|
||||
a *= d
|
||||
a /= e
|
||||
print("a = ", 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 flag1
|
||||
print("flag1 = ", flag1)
|
||||
print("flag2 = ", flag2)
|
||||
print("flag3 = ", flag3)
|
||||
print("flag4 = ", flag4)
|
||||
print("flag5 = ", flag5)
|
||||
print(flag1 is True)
|
||||
print(flag2 is not False)
|
||||
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
|
||||
```
|
||||
|
||||
> **说明**:比较运算符的优先级高于赋值运算符,所以`flag0 = 1 == 1`先做`1 == 1`产生布尔值`True`,再将这个值赋值给变量`flag0`。`print`函数可以输出多个值,多个值之间可以用`,`进行分隔,输出的内容之间默认以空格分开。
|
||||
|
||||
### 练习
|
||||
|
||||
#### 练习1:华氏温度转摄氏温度。
|
||||
#### 练习1:华氏温度转换为摄氏温度。
|
||||
|
||||
> 提示:华氏温度到摄氏温度的转换公式为:$C=(F - 32) \div 1.8$。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
将华氏温度转换为摄氏温度
|
||||
F = 1.8C + 32
|
||||
|
||||
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:输入圆的半径计算计算周长和面积。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
输入半径计算圆的周长和面积
|
||||
|
@ -189,18 +210,17 @@ 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)
|
||||
```
|
||||
|
||||
#### 练习3:输入年份判断是不是闰年。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
输入年份 如果是闰年输出True 否则输出False
|
||||
|
@ -208,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`。
|
|
@ -15,21 +15,18 @@
|
|||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
username = input('请输入用户名: ')
|
||||
password = input('请输入口令: ')
|
||||
# 如果希望输入口令时 终端中没有回显 可以使用getpass模块的getpass函数
|
||||
# import getpass
|
||||
# password = getpass.getpass('请输入口令: ')
|
||||
# 用户名是admin且密码是123456则身份验证成功否则身份验证失败
|
||||
if username == 'admin' and password == '123456':
|
||||
print('身份验证成功!')
|
||||
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)
|
||||
|
||||
|
@ -83,7 +80,9 @@ print('f(%.2f) = %.2f' % (x, y))
|
|||
|
||||
### 练习
|
||||
|
||||
#### 练习1:英制单位与公制单位互换
|
||||
#### 练习1:英制单位英寸与公制单位厘米互换。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -92,7 +91,6 @@ print('f(%.2f) = %.2f' % (x, y))
|
|||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
value = float(input('请输入长度: '))
|
||||
unit = input('请输入单位: ')
|
||||
if unit == 'in' or unit == '英寸':
|
||||
|
@ -103,50 +101,19 @@ else:
|
|||
print('请输入有效的单位')
|
||||
```
|
||||
|
||||
#### 练习2:掷骰子决定做什么
|
||||
#### 练习2:百分制成绩转换为等级制成绩。
|
||||
|
||||
> **要求**:如果输入的成绩在90分以上(含90分)输出A;80分-90分(不含90分)输出B;70分-80分(不含80分)输出C;60分-70分(不含70分)输出D;60分以下输出E。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
掷骰子决定做什么事情
|
||||
百分制成绩转换为等级制成绩
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
from random import randint
|
||||
|
||||
face = randint(1, 6)
|
||||
if face == 1:
|
||||
result = '唱首歌'
|
||||
elif face == 2:
|
||||
result = '跳个舞'
|
||||
elif face == 3:
|
||||
result = '学狗叫'
|
||||
elif face == 4:
|
||||
result = '做俯卧撑'
|
||||
elif face == 5:
|
||||
result = '念绕口令'
|
||||
else:
|
||||
result = '讲冷笑话'
|
||||
print(result)
|
||||
```
|
||||
> **说明:** 上面的代码中使用了random模块的randint函数生成指定范围的随机数来模拟掷骰子。
|
||||
|
||||
#### 练习3:百分制成绩转等级制
|
||||
|
||||
```Python
|
||||
"""
|
||||
百分制成绩转等级制成绩
|
||||
90分以上 --> A
|
||||
80分~89分 --> B
|
||||
70分~79分 --> C
|
||||
60分~69分 --> D
|
||||
60分以下 --> E
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
score = float(input('请输入成绩: '))
|
||||
if score >= 90:
|
||||
grade = 'A'
|
||||
|
@ -160,71 +127,27 @@ else:
|
|||
grade = 'E'
|
||||
print('对应的等级是:', grade)
|
||||
```
|
||||
#### 练习4:输入三条边长如果能构成三角形就计算周长和面积
|
||||
#### 练习3:输入三条边长,如果能构成三角形就计算周长和面积。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
判断输入的边长能否构成三角形
|
||||
如果能则计算出三角形的周长和面积
|
||||
判断输入的边长能否构成三角形,如果能则计算出三角形的周长和面积
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
a = float(input('a = '))
|
||||
b = float(input('b = '))
|
||||
c = float(input('c = '))
|
||||
if a + b > c and a + c > b and b + c > a:
|
||||
print('周长: %f' % (a + b + c))
|
||||
p = (a + b + c) / 2
|
||||
area = math.sqrt(p * (p - a) * (p - b) * (p - c))
|
||||
area = (p * (p - a) * (p - b) * (p - c)) ** 0.5
|
||||
print('面积: %f' % (area))
|
||||
else:
|
||||
print('不能构成三角形')
|
||||
```
|
||||
> **说明:** 上面的代码中使用了`math`模块的`sqrt`函数来计算平方根。用边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。
|
||||
> **说明:** 上面使用的通过边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。
|
||||
|
||||
#### 练习5:个人所得税计算器。
|
||||
|
||||
```Python
|
||||
"""
|
||||
输入月收入和五险一金计算个人所得税
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
salary = float(input('本月收入: '))
|
||||
insurance = float(input('五险一金: '))
|
||||
diff = salary - insurance - 3500
|
||||
if diff <= 0:
|
||||
rate = 0
|
||||
deduction = 0
|
||||
elif diff < 1500:
|
||||
rate = 0.03
|
||||
deduction = 0
|
||||
elif diff < 4500:
|
||||
rate = 0.1
|
||||
deduction = 105
|
||||
elif diff < 9000:
|
||||
rate = 0.2
|
||||
deduction = 555
|
||||
elif diff < 35000:
|
||||
rate = 0.25
|
||||
deduction = 1005
|
||||
elif diff < 55000:
|
||||
rate = 0.3
|
||||
deduction = 2755
|
||||
elif diff < 80000:
|
||||
rate = 0.35
|
||||
deduction = 5505
|
||||
else:
|
||||
rate = 0.45
|
||||
deduction = 13505
|
||||
tax = abs(diff * rate - deduction)
|
||||
print('个人所得税: ¥%.2f元' % tax)
|
||||
print('实际到手收入: ¥%.2f元' % (diff + 3500 - tax))
|
||||
```
|
||||
>**说明:** 上面的代码中使用了Python内置的`abs()`函数取绝对值来处理`-0`的问题。
|
||||
|
|
|
@ -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`类型,`range`可以用来产生一个不变的数值序列,而且这个序列通常都是用在循环中的,例如:
|
||||
需要说明的是上面代码中的`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)
|
||||
|
@ -96,7 +98,7 @@ if counter > 7:
|
|||
print('你的智商余额明显不足')
|
||||
```
|
||||
|
||||
> **说明:** 上面的代码中使用了`break`关键字来提前终止循环,需要注意的是`break`只能终止它所在的那个循环,这一点在使用嵌套的循环结构(下面会讲到)需要引起注意。除了`break`之外,还有另一个关键字是`continue`,它可以用来放弃本次循环后续的代码直接让循环进入下一轮。
|
||||
上面的代码中使用了`break`关键字来提前终止循环,需要注意的是`break`只能终止它所在的那个循环,这一点在使用嵌套的循环结构(下面会讲到)需要引起注意。除了`break`之外,还有另一个关键字是`continue`,它可以用来放弃本次循环后续的代码直接让循环进入下一轮。
|
||||
|
||||
和分支结构一样,循环结构也是可以嵌套的,也就是说在循环中还可以构造循环结构。下面的例子演示了如何通过嵌套的循环来输出一个九九乘法表。
|
||||
|
||||
|
@ -116,7 +118,11 @@ for i in range(1, 10):
|
|||
|
||||
### 练习
|
||||
|
||||
#### 练习1:输入一个数判断是不是素数。
|
||||
#### 练习1:输入一个正整数判断是不是素数。
|
||||
|
||||
> **提示**:素数指的是只能被1和自身整除的大于1的整数。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -141,11 +147,15 @@ else:
|
|||
print('%d不是素数' % num)
|
||||
```
|
||||
|
||||
#### 练习2:输入两个正整数,计算最大公约数和最小公倍数。
|
||||
#### 练习2:输入两个正整数,计算它们的最大公约数和最小公倍数。
|
||||
|
||||
> **提示**:两个数的最大公约数是两个数的公共因子中最大的那个数;两个数的最小公倍数则是能够同时被两个数整除的最小的那个数。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
输入两个正整数计算最大公约数和最小公倍数
|
||||
输入两个正整数计算它们的最大公约数和最小公倍数
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
|
@ -154,8 +164,11 @@ Date: 2018-03-01
|
|||
|
||||
x = int(input('x = '))
|
||||
y = int(input('y = '))
|
||||
# 如果x大于y就交换x和y的值
|
||||
if x > y:
|
||||
# 通过下面的操作将y的值赋给x, 将x的值赋给y
|
||||
x, y = y, x
|
||||
# 从两个数中较的数开始做递减的循环
|
||||
for factor in range(x, 0, -1):
|
||||
if x % factor == 0 and y % factor == 0:
|
||||
print('%d和%d的最大公约数是%d' % (x, y, factor))
|
||||
|
@ -163,29 +176,37 @@ for factor in range(x, 0, -1):
|
|||
break
|
||||
```
|
||||
|
||||
#### 练习3:打印三角形图案。
|
||||
|
||||
```Python
|
||||
"""
|
||||
打印各种三角形图案
|
||||
#### 练习3:打印如下所示的三角形图案。
|
||||
|
||||
```
|
||||
*
|
||||
**
|
||||
***
|
||||
****
|
||||
*****
|
||||
```
|
||||
|
||||
```
|
||||
*
|
||||
**
|
||||
***
|
||||
****
|
||||
*****
|
||||
```
|
||||
|
||||
```
|
||||
*
|
||||
***
|
||||
*****
|
||||
*******
|
||||
*********
|
||||
```
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
"""
|
||||
打印三角形图案
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
|
|
|
@ -1,13 +1,130 @@
|
|||
## 构造程序逻辑
|
||||
|
||||
分支和循环结构会帮助我们将程序中逻辑建立起来,将来我们的程序无论简单复杂,都是由顺序结构、分支结构、循环结构构成的。对于编程语言的初学者来说,首先要锻炼的是将人类自然语言描述的解决问题的步骤和方法翻译成代码的能力,其次就是熟练的运用之前学过的运算符、表达式以及最近的两个章节讲解的分支结构和循环结构的知识。有了这些基本的能力才能够通过计算机程序去解决各种各样的现实问题。所以,开始做练习吧!
|
||||
学完前面的几个章节后,我觉得有必要在这里带大家做一些练习来巩固之前所学的知识,虽然迄今为止我们学习的内容只是Python的冰山一角,但是这些内容已经足够我们来构建程序中的逻辑。对于编程语言的初学者来说,在学习了Python的核心语言元素(变量、类型、运算符、表达式、分支结构、循环结构等)之后,必须做的一件事情就是尝试用所学知识去解决现实中的问题,换句话说就是锻炼自己把用人类自然语言描述的算法(解决问题的方法和步骤)翻译成Python代码的能力,而这件事情必须通过大量的练习才能达成。
|
||||
|
||||
### 练习清单
|
||||
我们在本章为大家整理了一些经典的案例和习题,希望通过这些例子,一方面帮助大家巩固之前所学的Python知识,另一方面帮助大家了解如何建立程序中的逻辑以及如何运用一些简单的算法解决现实中的问题。
|
||||
|
||||
1. 寻找[“水仙花数”](https://baike.baidu.com/item/%E6%B0%B4%E4%BB%99%E8%8A%B1%E6%95%B0)。
|
||||
2. 寻找[“完美数”](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E6%95%B0/370913)。
|
||||
3. [“百钱百鸡”](https://baike.baidu.com/item/%E7%99%BE%E9%B8%A1%E7%99%BE%E9%92%B1/5857320)问题。
|
||||
4. 生成[“斐波拉切数列”](https://baike.baidu.com/item/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97/99145)。
|
||||
5. Craps赌博游戏。
|
||||
### 经典的例子
|
||||
|
||||
> **提示**:练习的参考答案在code/Day05目录下。
|
||||
1. 寻找**水仙花数**。
|
||||
|
||||
> **说明**:水仙花数也被称为超完全数字不变数、自恋数、自幂数、阿姆斯特朗数,它是一个3位数,该数字每个位上数字的立方之和正好等于它本身,例如:$1^3 + 5^3+ 3^3=153$。
|
||||
|
||||
```Python
|
||||
"""
|
||||
找出所有水仙花数
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
for num in range(100, 1000):
|
||||
low = num % 10
|
||||
mid = num // 10 % 10
|
||||
high = num // 100
|
||||
if num == low ** 3 + mid ** 3 + high ** 3:
|
||||
print(num)
|
||||
```
|
||||
|
||||
在上面的代码中,我们通过整除和求模运算分别找出了一个三位数的个位、十位和百位,这种小技巧在实际开发中还是常用的。用类似的方法,我们还可以实现将一个正整数反转,例如:将12345变成54321,代码如下所示。
|
||||
|
||||
```Python
|
||||
"""
|
||||
正整数的反转
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
num = int(input('num = '))
|
||||
reversed_num = 0
|
||||
while num > 0:
|
||||
reversed_num = reversed_num * 10 + num % 10
|
||||
num //= 10
|
||||
print(reversed_num)
|
||||
```
|
||||
|
||||
2. **百钱百鸡**问题。
|
||||
|
||||
> **说明**:百钱百鸡是我国古代数学家[张丘建](https://baike.baidu.com/item/%E5%BC%A0%E4%B8%98%E5%BB%BA/10246238)在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?翻译成现代文是:公鸡5元一只,母鸡3元一只,小鸡1元三只,用100块钱买一百只鸡,问公鸡、母鸡、小鸡各有多少只?
|
||||
|
||||
```Python
|
||||
"""
|
||||
《百钱百鸡》问题
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
|
||||
for x in range(0, 20):
|
||||
for y in range(0, 33):
|
||||
z = 100 - x - y
|
||||
if 5 * x + 3 * y + z / 3 == 100:
|
||||
print('公鸡: %d只, 母鸡: %d只, 小鸡: %d只' % (x, y, z))
|
||||
```
|
||||
|
||||
上面使用的方法叫做**穷举法**,也称为**暴力搜索法**,这种方法通过一项一项的列举备选解决方案中所有可能的候选项并检查每个候选项是否符合问题的描述,最终得到问题的解。这种方法看起来比较笨拙,但对于运算能力非常强大的计算机来说,通常都是一个可行的甚至是不错的选择,而且问题的解如果存在,这种方法一定能够找到它。
|
||||
|
||||
3. **CRAPS赌博游戏**。
|
||||
|
||||
> **说明**:CRAPS又称花旗骰,是美国拉斯维加斯非常受欢迎的一种的桌上赌博游戏。该游戏使用两粒骰子,玩家通过摇两粒骰子获得点数进行游戏。简单的规则是:玩家第一次摇骰子如果摇出了7点或11点,玩家胜;玩家第一次如果摇出2点、3点或12点,庄家胜;其他点数玩家继续摇骰子,如果玩家摇出了7点,庄家胜;如果玩家摇出了第一次摇的点数,玩家胜;其他点数,玩家继续要骰子,直到分出胜负。
|
||||
|
||||
```Python
|
||||
"""
|
||||
Craps赌博游戏
|
||||
我们设定玩家开始游戏时有1000元的赌注
|
||||
游戏结束的条件是玩家输光所有的赌注
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
from random import randint
|
||||
|
||||
money = 1000
|
||||
while money > 0:
|
||||
print('你的总资产为:', money)
|
||||
needs_go_on = False
|
||||
while True:
|
||||
debt = int(input('请下注: '))
|
||||
if 0 < debt <= money:
|
||||
break
|
||||
first = randint(1, 6) + randint(1, 6)
|
||||
print('玩家摇出了%d点' % first)
|
||||
if first == 7 or first == 11:
|
||||
print('玩家胜!')
|
||||
money += debt
|
||||
elif first == 2 or first == 3 or first == 12:
|
||||
print('庄家胜!')
|
||||
money -= debt
|
||||
else:
|
||||
needs_go_on = True
|
||||
while needs_go_on:
|
||||
needs_go_on = False
|
||||
current = randint(1, 6) + randint(1, 6)
|
||||
print('玩家摇出了%d点' % current)
|
||||
if current == 7:
|
||||
print('庄家胜')
|
||||
money -= debt
|
||||
elif current == first:
|
||||
print('玩家胜')
|
||||
money += debt
|
||||
else:
|
||||
needs_go_on = True
|
||||
print('你破产了, 游戏结束!')
|
||||
```
|
||||
|
||||
###有用的练习
|
||||
|
||||
1. 生成**斐波那契数列**的前20个数。
|
||||
|
||||
> **说明**:斐波那契数列(Fibonacci sequence),又称黄金分割数列,是意大利数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)在《计算之书》中提出一个在理想假设条件下兔子成长率的问题而引入的数列,所以这个数列也被戏称为"兔子数列"。斐波那契数列的特点是数列的前两个数都是1,从第三个数开始,每个数都是它前面两个数的和,形如:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...。斐波那契数列在现代物理、准晶体结构、化学等领域都有直接的应用。
|
||||
|
||||
2. 找出10000以内的**完美数**。
|
||||
|
||||
> **说明**:完美数又称为完全数或完备数,它的所有的真因子(即除了自身以外的因子)的和(即因子函数)恰好等于它本身。例如:6($6=1+2+3$)和28($28=1+2+4+7+14$)就是完美数。完美数有很多神奇的特性,有兴趣的可以自行了解。
|
||||
|
||||
3. 输出**100以内所有的素数**。
|
||||
|
||||
> **说明**:素数指的是只能被1和自身整除的正整数(不包括1)。
|
||||
|
||||
上面练习的参考答案在本章对应的代码目录中,如果需要帮助请读者自行查看参考答案。
|
|
@ -13,8 +13,10 @@
|
|||
```Python
|
||||
"""
|
||||
输入M和N计算C(M,N)
|
||||
"""
|
||||
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
m = int(input('m = '))
|
||||
n = int(input('n = '))
|
||||
fm = 1
|
||||
|
@ -23,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)
|
||||
```
|
||||
|
||||
### 函数的作用
|
||||
|
@ -40,13 +42,14 @@ print(fm // fn // fmn)
|
|||
在了解了如何定义函数后,我们可以对上面的代码进行重构,所谓重构就是在不影响代码执行结果的前提下对代码的结构进行调整,重构之后的代码如下所示。
|
||||
|
||||
```Python
|
||||
def factorial(num):
|
||||
"""
|
||||
求阶乘
|
||||
"""
|
||||
输入M和N计算C(M,N)
|
||||
|
||||
:param num: 非负整数
|
||||
:return: num的阶乘
|
||||
"""
|
||||
Version: 0.1
|
||||
Author: 骆昊
|
||||
"""
|
||||
def fac(num):
|
||||
"""求阶乘"""
|
||||
result = 1
|
||||
for n in range(1, num + 1):
|
||||
result *= n
|
||||
|
@ -56,27 +59,22 @@ 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模块中其实已经有一个factorial函数了,事实上要计算阶乘可以直接使用这个现成的函数而不用自己定义。下面例子中的某些函数其实Python中也是内置了,我们这里是为了讲解函数的定义和使用才把它们又实现了一遍,实际开发中不建议做这种低级的重复性的工作。
|
||||
> **说明:** Python的`math`模块中其实已经有一个名为`factorial`函数实现了阶乘运算,事实上求阶乘并不用自己定义函数。下面的例子中,我们讲的函数在Python标准库已经实现过了,我们这里是为了讲解函数的定义和使用才把它们又实现了一遍,**实际开发中并不建议做这种低级的重复劳动**。
|
||||
|
||||
|
||||
### 函数的参数
|
||||
|
||||
函数是绝大多数编程语言中都支持的一个代码的“构建块”,但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。在Python中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持[函数的重载](https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD),因为我们在定义一个函数的时候可以让它有多种不同的使用方式,下面是两个小例子。
|
||||
函数是绝大多数编程语言中都支持的一个代码的"构建块",但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。在Python中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持[函数的重载](https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD),因为我们在定义一个函数的时候可以让它有多种不同的使用方式,下面是两个小例子。
|
||||
|
||||
```Python
|
||||
from random import randint
|
||||
|
||||
|
||||
def roll_dice(n=2):
|
||||
"""
|
||||
摇色子
|
||||
|
||||
:param n: 色子的个数
|
||||
:return: n颗色子点数之和
|
||||
"""
|
||||
"""摇色子"""
|
||||
total = 0
|
||||
for _ in range(n):
|
||||
total += randint(1, 6)
|
||||
|
@ -84,6 +82,7 @@ def roll_dice(n=2):
|
|||
|
||||
|
||||
def add(a=0, b=0, c=0):
|
||||
"""三个数相加"""
|
||||
return a + b + c
|
||||
|
||||
|
||||
|
@ -105,7 +104,6 @@ print(add(c=50, a=100, b=200))
|
|||
|
||||
```Python
|
||||
# 在参数名前面的*表示args是一个可变参数
|
||||
# 即在调用add函数时可以传入0个或多个参数
|
||||
def add(*args):
|
||||
total = 0
|
||||
for val in args:
|
||||
|
@ -113,6 +111,7 @@ def add(*args):
|
|||
return total
|
||||
|
||||
|
||||
# 在调用add函数时可以传入0个或多个参数
|
||||
print(add())
|
||||
print(add(1))
|
||||
print(add(1, 2))
|
||||
|
@ -139,21 +138,21 @@ foo()
|
|||
|
||||
当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。
|
||||
|
||||
module1.py
|
||||
`module1.py`
|
||||
|
||||
```Python
|
||||
def foo():
|
||||
print('hello, world!')
|
||||
```
|
||||
|
||||
module2.py
|
||||
`module2.py`
|
||||
|
||||
```Python
|
||||
def foo():
|
||||
print('goodbye, world!')
|
||||
```
|
||||
|
||||
test.py
|
||||
`test.py`
|
||||
|
||||
```Python
|
||||
from module1 import foo
|
||||
|
@ -169,7 +168,7 @@ foo()
|
|||
|
||||
也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。
|
||||
|
||||
test.py
|
||||
`test.py`
|
||||
|
||||
```Python
|
||||
import module1 as m1
|
||||
|
@ -181,7 +180,7 @@ m2.foo()
|
|||
|
||||
但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个`foo`,因为后导入的foo覆盖了之前导入的`foo`。
|
||||
|
||||
test.py
|
||||
`test.py`
|
||||
|
||||
```Python
|
||||
from module1 import foo
|
||||
|
@ -191,7 +190,7 @@ from module2 import foo
|
|||
foo()
|
||||
```
|
||||
|
||||
test.py
|
||||
`test.py`
|
||||
|
||||
```Python
|
||||
from module2 import foo
|
||||
|
@ -201,9 +200,9 @@ from module1 import foo
|
|||
foo()
|
||||
```
|
||||
|
||||
需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是“\_\_main\_\_”。
|
||||
需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是"\_\_main\_\_"。
|
||||
|
||||
module3.py
|
||||
`module3.py`
|
||||
|
||||
```Python
|
||||
def foo():
|
||||
|
@ -223,7 +222,7 @@ if __name__ == '__main__':
|
|||
bar()
|
||||
```
|
||||
|
||||
test.py
|
||||
`test.py`
|
||||
|
||||
```Python
|
||||
import module3
|
||||
|
@ -235,8 +234,11 @@ import module3
|
|||
|
||||
#### 练习1:实现计算求最大公约数和最小公倍数的函数。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
def gcd(x, y):
|
||||
"""求最大公约数"""
|
||||
(x, y) = (y, x) if x > y else (x, y)
|
||||
for factor in range(x, 0, -1):
|
||||
if x % factor == 0 and y % factor == 0:
|
||||
|
@ -244,13 +246,17 @@ def gcd(x, y):
|
|||
|
||||
|
||||
def lcm(x, y):
|
||||
"""求最小公倍数"""
|
||||
return x * y // gcd(x, y)
|
||||
```
|
||||
|
||||
#### 练习2:实现判断一个数是不是回文数的函数。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
def is_palindrome(num):
|
||||
"""判断一个数是不是回文数"""
|
||||
temp = num
|
||||
total = 0
|
||||
while temp > 0:
|
||||
|
@ -261,9 +267,12 @@ def is_palindrome(num):
|
|||
|
||||
#### 练习3:实现判断一个数是不是素数的函数。
|
||||
|
||||
参考答案:
|
||||
|
||||
```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
|
||||
|
@ -271,6 +280,8 @@ def is_prime(num):
|
|||
|
||||
#### 练习4:写一个程序判断输入的正整数是不是回文素数。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
if __name__ == '__main__':
|
||||
num = int(input('请输入正整数: '))
|
||||
|
@ -278,7 +289,9 @@ if __name__ == '__main__':
|
|||
print('%d是回文素数' % num)
|
||||
```
|
||||
|
||||
通过上面的程序可以看出,当我们将代码中重复出现的和相对独立的功能抽取成函数后,我们可以组合使用这些函数来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。
|
||||
> **注意**:通过上面的程序可以看出,当我们**将代码中重复出现的和相对独立的功能抽取成函数**后,我们可以**组合使用这些函数**来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。
|
||||
|
||||
### 变量的作用域
|
||||
|
||||
最后,我们来讨论一下Python中有关变量作用域的问题。
|
||||
|
||||
|
@ -286,7 +299,8 @@ if __name__ == '__main__':
|
|||
def foo():
|
||||
b = 'hello'
|
||||
|
||||
def bar(): # Python中可以在函数内部再定义函数
|
||||
# Python中可以在函数内部再定义函数
|
||||
def bar():
|
||||
c = True
|
||||
print(a)
|
||||
print(b)
|
||||
|
@ -302,7 +316,7 @@ if __name__ == '__main__':
|
|||
foo()
|
||||
```
|
||||
|
||||
上面的代码能够顺利的执行并且打印出100和“hello”,但我们注意到了,在`bar`函数的内部并没有定义`a`和`b`两个变量,那么`a`和`b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些隐含标识符`min`、`len`等都属于内置作用域)。
|
||||
上面的代码能够顺利的执行并且打印出100、hello和True,但我们注意到了,在`bar`函数的内部并没有定义`a`和`b`两个变量,那么`a`和`b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些标识符,我们之前用过的`input`、`print`、`int`等都属于内置作用域。
|
||||
|
||||
再看看下面这段代码,我们希望通过函数调用修改全局变量`a`的值,但实际上下面的代码是做不到的。
|
||||
|
||||
|
@ -335,9 +349,9 @@ if __name__ == '__main__':
|
|||
|
||||
我们可以使用`global`关键字来指示`foo`函数中的变量`a`来自于全局作用域,如果全局作用域中没有`a`,那么下面一行的代码就会定义变量`a`并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用`nonlocal`关键字来指示变量来自于嵌套作用域,请大家自行试验。
|
||||
|
||||
在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对[迪米特法则](https://zh.wikipedia.org/zh-hans/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在函数调用结束后依然可以访问,这时候就需要使用[闭包](https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),这个我们在后续的内容中进行讲解。
|
||||
在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对[迪米特法则](https://zh.wikipedia.org/zh-hans/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在定义它的函数调用结束后依然可以使用它的值,这时候就需要使用[闭包](https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),这个我们在后续的内容中进行讲解。
|
||||
|
||||
> **说明:** 很多人经常会将“闭包”一词和[“匿名函数”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们是不同的概念,如果想提前了解这个概念,推荐看看[维基百科](https://zh.wikipedia.org/wiki/)或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。
|
||||
> **说明:** 很多人经常会将“闭包”和[“匿名函数”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们并不是一回事,如果想了解这个概念,可以看看[维基百科](https://zh.wikipedia.org/wiki/)的解释或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。
|
||||
|
||||
说了那么多,其实结论很简单,从现在开始我们可以将Python代码按照下面的格式进行书写,这一点点的改进其实就是在我们理解了函数和作用域的基础上跨出的巨大的一步。
|
||||
|
||||
|
|
|
@ -2,185 +2,249 @@
|
|||
|
||||
### 使用字符串
|
||||
|
||||
第二次世界大战促使了现代电子计算机的诞生,最初的目的用计算机来快速的完成导弹弹道的计算,因此在计算机刚刚诞生的那个年代,计算机处理的信息基本上都是数值型的信息,而世界上的第一台电子计算机ENIAC每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机更多的时间需要处理的数据可能都是以文本的方式存在的,如果我们希望通过Python程序操作本这些文本信息,就必须要先了解字符串类型以及与它相关的知识。
|
||||
第二次世界大战促使了现代电子计算机的诞生,最初计算机被应用于导弹弹道的计算,而在计算机诞生后的很多年时间里,计算机处理的信息基本上都是数值型的信息。世界上的第一台电子计算机叫ENIAC(电子数值积分计算机),诞生于美国的宾夕法尼亚大学,每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机处理得更多的数据可能都是以文本的方式存在的,如果我们希望通过Python程序操作本这些文本信息,就必须要先了解字符串类型以及与它相关的知识。
|
||||
|
||||
所谓**字符串**,就是由零个或多个字符组成的有限序列,一般记为![$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](./res/formula_5.png)。
|
||||
|
||||
我们可以通过下面的代码来了解字符串的使用。
|
||||
所谓**字符串**,就是由零个或多个字符组成的有限序列,一般记为![$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](./res/formula_5.png)。在Python程序中,如果我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。
|
||||
|
||||
```Python
|
||||
def main():
|
||||
str1 = 'hello, world!'
|
||||
# 通过len函数计算字符串的长度
|
||||
print(len(str1)) # 13
|
||||
# 获得字符串首字母大写的拷贝
|
||||
print(str1.capitalize()) # Hello, world!
|
||||
# 获得字符串变大写后的拷贝
|
||||
print(str1.upper()) # HELLO, WORLD!
|
||||
# 从字符串中查找子串所在位置
|
||||
print(str1.find('or')) # 8
|
||||
print(str1.find('shit')) # -1
|
||||
# 与find类似但找不到子串时会引发异常
|
||||
# print(str1.index('or'))
|
||||
# print(str1.index('shit'))
|
||||
# 检查字符串是否以指定的字符串开头
|
||||
print(str1.startswith('He')) # False
|
||||
print(str1.startswith('hel')) # True
|
||||
# 检查字符串是否以指定的字符串结尾
|
||||
print(str1.endswith('!')) # True
|
||||
# 将字符串以指定的宽度居中并在两侧填充指定的字符
|
||||
print(str1.center(50, '*'))
|
||||
# 将字符串以指定的宽度靠右放置左侧填充指定的字符
|
||||
print(str1.rjust(50, ' '))
|
||||
str2 = 'abc123456'
|
||||
# 从字符串中取出指定位置的字符(下标运算)
|
||||
print(str2[2]) # c
|
||||
# 字符串切片(从指定的开始索引到指定的结束索引)
|
||||
print(str2[2:5]) # c12
|
||||
print(str2[2:]) # c123456
|
||||
print(str2[2::2]) # c246
|
||||
print(str2[::2]) # ac246
|
||||
print(str2[::-1]) # 654321cba
|
||||
print(str2[-3:-1]) # 45
|
||||
# 检查字符串是否由数字构成
|
||||
print(str2.isdigit()) # False
|
||||
# 检查字符串是否以字母构成
|
||||
print(str2.isalpha()) # False
|
||||
# 检查字符串是否以数字和字母构成
|
||||
print(str2.isalnum()) # True
|
||||
str3 = ' jackfrued@126.com '
|
||||
print(str3)
|
||||
# 获得字符串修剪左右两侧空格的拷贝
|
||||
print(str3.strip())
|
||||
s1 = 'hello, world!'
|
||||
s2 = "hello, world!"
|
||||
# 以三个双引号或单引号开头的字符串可以折行
|
||||
s3 = """
|
||||
hello,
|
||||
world!
|
||||
"""
|
||||
print(s1, s2, s3, end='')
|
||||
```
|
||||
|
||||
可以在字符串中使用`\`(反斜杠)来表示转义,也就是说`\`后面的字符不再是它原来的意义,例如:`\n`不是代表反斜杠和字符n,而是表示换行;而`\t`也不是代表反斜杠和字符t,而是表示制表符。所以如果想在字符串中表示`'`要写成`\'`,同理想表示`\`要写成`\\`。可以运行下面的代码看看会输出什么。
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```Python
|
||||
s1 = '\'hello, world!\''
|
||||
s2 = '\n\\hello, world!\\\n'
|
||||
print(s1, s2, end='')
|
||||
```
|
||||
|
||||
在`\`后面还可以跟一个八进制或者十六进制数来表示字符,例如`\141`和`\x61`都代表小写字母`a`,前者是八进制的表示法,后者是十六进制的表示法。也可以在`\`后面跟Unicode字符编码来表示字符,例如`\u9a86\u660a`代表的是中文“骆昊”。运行下面的代码,看看输出了什么。
|
||||
|
||||
```Python
|
||||
s1 = '\141\142\143\x61\x62\x63'
|
||||
s2 = '\u9a86\u660a'
|
||||
print(s1, s2)
|
||||
```
|
||||
|
||||
如果不希望字符串中的`\`表示转义,我们可以通过在字符串的最前面加上字母`r`来加以说明,再看看下面的代码又会输出什么。
|
||||
|
||||
```Python
|
||||
s1 = r'\'hello, world!\''
|
||||
s2 = r'\n\\hello, world!\\\n'
|
||||
print(s1, s2, end='')
|
||||
```
|
||||
|
||||
Python为字符串类型提供了非常丰富的运算符,我们可以使用`+`运算符来实现字符串的拼接,可以使用`*`运算符来重复一个字符串的内容,可以使用`in`和`not in`来判断一个字符串是否包含另外一个字符串(成员运算),我们也可以用`[]`和`[:]`运算符从字符串取出某个字符或某些字符(切片运算),代码如下所示。
|
||||
|
||||
```Python
|
||||
s1 = 'hello ' * 3
|
||||
print(s1) # hello hello hello
|
||||
s2 = 'world'
|
||||
s1 += s2
|
||||
print(s1) # hello hello hello world
|
||||
print('ll' in s1) # True
|
||||
print('good' in s1) # False
|
||||
str2 = 'abc123456'
|
||||
# 从字符串中取出指定位置的字符(下标运算)
|
||||
print(str2[2]) # c
|
||||
# 字符串切片(从指定的开始索引到指定的结束索引)
|
||||
print(str2[2:5]) # c12
|
||||
print(str2[2:]) # c123456
|
||||
print(str2[2::2]) # c246
|
||||
print(str2[::2]) # ac246
|
||||
print(str2[::-1]) # 654321cba
|
||||
print(str2[-3:-1]) # 45
|
||||
```
|
||||
|
||||
在Python中,我们还可以通过一系列的方法来完成对字符串的处理,代码如下所示。
|
||||
|
||||
```Python
|
||||
str1 = 'hello, world!'
|
||||
# 通过内置函数len计算字符串的长度
|
||||
print(len(str1)) # 13
|
||||
# 获得字符串首字母大写的拷贝
|
||||
print(str1.capitalize()) # Hello, world!
|
||||
# 获得字符串每个单词首字母大写的拷贝
|
||||
print(str1.title()) # Hello, World!
|
||||
# 获得字符串变大写后的拷贝
|
||||
print(str1.upper()) # HELLO, WORLD!
|
||||
# 从字符串中查找子串所在位置
|
||||
print(str1.find('or')) # 8
|
||||
print(str1.find('shit')) # -1
|
||||
# 与find类似但找不到子串时会引发异常
|
||||
# print(str1.index('or'))
|
||||
# print(str1.index('shit'))
|
||||
# 检查字符串是否以指定的字符串开头
|
||||
print(str1.startswith('He')) # False
|
||||
print(str1.startswith('hel')) # True
|
||||
# 检查字符串是否以指定的字符串结尾
|
||||
print(str1.endswith('!')) # True
|
||||
# 将字符串以指定的宽度居中并在两侧填充指定的字符
|
||||
print(str1.center(50, '*'))
|
||||
# 将字符串以指定的宽度靠右放置左侧填充指定的字符
|
||||
print(str1.rjust(50, ' '))
|
||||
str2 = 'abc123456'
|
||||
# 检查字符串是否由数字构成
|
||||
print(str2.isdigit()) # False
|
||||
# 检查字符串是否以字母构成
|
||||
print(str2.isalpha()) # False
|
||||
# 检查字符串是否以数字和字母构成
|
||||
print(str2.isalnum()) # True
|
||||
str3 = ' jackfrued@126.com '
|
||||
print(str3)
|
||||
# 获得字符串修剪左右两侧空格之后的拷贝
|
||||
print(str3.strip())
|
||||
```
|
||||
|
||||
我们之前讲过,可以用下面的方式来格式化输出字符串。
|
||||
|
||||
```Python
|
||||
a, b = 5, 10
|
||||
print('%d * %d = %d' % (a, b, a * b))
|
||||
```
|
||||
|
||||
当然,我们也可以用字符串提供的方法来完成字符串的格式,代码如下所示。
|
||||
|
||||
```Python
|
||||
a, b = 5, 10
|
||||
print('{0} * {1} = {2}'.format(a, b, a * b))
|
||||
```
|
||||
|
||||
Python 3.6以后,格式化字符串还有更为简洁的书写方式,就是在字符串前加上字母`f`,我们可以使用下面的语法糖来简化上面的代码。
|
||||
|
||||
```Python
|
||||
a, b = 5, 10
|
||||
print(f'{a} * {b} = {a * b}')
|
||||
```
|
||||
|
||||
除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典。
|
||||
|
||||
### 使用列表
|
||||
|
||||
下面的代码演示了如何定义列表、使用下标访问列表元素以及添加和删除元素的操作。
|
||||
不知道大家是否注意到,刚才我们讲到的字符串类型(`str`)和之前我们讲到的数值类型(`int`和`float`)有一些区别。数值类型是标量类型,也就是说这种类型的对象没有可以访问的内部结构;而字符串类型是一种结构化的、非标量类型,所以才会有一系列的属性和方法。接下来我们要介绍的列表(`list`),也是一种结构化的、非标量类型,它是值的有序序列,每个值都可以通过索引进行标识,定义列表可以将列表的元素放在`[]`中,多个元素用`,`进行分隔,可以使用`for`循环对列表元素进行遍历,也可以使用`[]`或`[:]`运算符取出列表中的一个或多个元素。
|
||||
|
||||
下面的代码演示了如何定义列表、如何遍历列表以及列表的下标运算。
|
||||
|
||||
```Python
|
||||
def main():
|
||||
list1 = [1, 3, 5, 7, 100]
|
||||
print(list1)
|
||||
list2 = ['hello'] * 5
|
||||
print(list2)
|
||||
# 计算列表长度(元素个数)
|
||||
print(len(list1))
|
||||
# 下标(索引)运算
|
||||
print(list1[0])
|
||||
print(list1[4])
|
||||
# print(list1[5]) # IndexError: list index out of range
|
||||
print(list1[-1])
|
||||
print(list1[-3])
|
||||
list1[2] = 300
|
||||
print(list1)
|
||||
# 添加元素
|
||||
list1.append(200)
|
||||
list1.insert(1, 400)
|
||||
list1 += [1000, 2000]
|
||||
print(list1)
|
||||
print(len(list1))
|
||||
# 删除元素
|
||||
list1 = [1, 3, 5, 7, 100]
|
||||
print(list1) # [1, 3, 5, 7, 100]
|
||||
# 乘号表示列表元素的重复
|
||||
list2 = ['hello'] * 3
|
||||
print(list2) # ['hello', 'hello', 'hello']
|
||||
# 计算列表长度(元素个数)
|
||||
print(len(list1)) # 5
|
||||
# 下标(索引)运算
|
||||
print(list1[0]) # 1
|
||||
print(list1[4]) # 100
|
||||
# print(list1[5]) # IndexError: list index out of range
|
||||
print(list1[-1]) # 100
|
||||
print(list1[-3]) # 5
|
||||
list1[2] = 300
|
||||
print(list1) # [1, 3, 300, 7, 100]
|
||||
# 通过循环用下标遍历列表元素
|
||||
for index in range(len(list1)):
|
||||
print(list1[index])
|
||||
# 通过for循环遍历列表元素
|
||||
for elem in list1:
|
||||
print(elem)
|
||||
# 通过enumerate函数处理列表之后再遍历可以同时获得元素索引和值
|
||||
for index, elem in enumerate(list1):
|
||||
print(index, elem)
|
||||
```
|
||||
|
||||
下面的代码演示了如何向列表中添加元素以及如何从列表中移除元素。
|
||||
|
||||
```Python
|
||||
list1 = [1, 3, 5, 7, 100]
|
||||
# 添加元素
|
||||
list1.append(200)
|
||||
list1.insert(1, 400)
|
||||
# 合并两个列表
|
||||
# list1.extend([1000, 2000])
|
||||
list1 += [1000, 2000]
|
||||
print(list1) # [1, 400, 3, 5, 7, 100, 200, 1000, 2000]
|
||||
print(len(list1)) # 9
|
||||
# 先通过成员运算判断元素是否在列表中,如果存在就删除该元素
|
||||
if 3 in list1:
|
||||
list1.remove(3)
|
||||
if 1234 in list1:
|
||||
if 1234 in list1:
|
||||
list1.remove(1234)
|
||||
del list1[0]
|
||||
print(list1)
|
||||
# 清空列表元素
|
||||
list1.clear()
|
||||
print(list1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
print(list1) # [1, 400, 5, 7, 100, 200, 1000, 2000]
|
||||
# 从指定的位置删除元素
|
||||
list1.pop(0)
|
||||
list1.pop(len(list1) - 1)
|
||||
print(list1) # [400, 5, 7, 100, 200, 1000]
|
||||
# 清空列表元素
|
||||
list1.clear()
|
||||
print(list1) # []
|
||||
```
|
||||
|
||||
和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。
|
||||
|
||||
```Python
|
||||
def main():
|
||||
fruits = ['grape', 'apple', 'strawberry', 'waxberry']
|
||||
fruits += ['pitaya', 'pear', 'mango']
|
||||
# 循环遍历列表元素
|
||||
for fruit in fruits:
|
||||
print(fruit.title(), end=' ')
|
||||
print()
|
||||
# 列表切片
|
||||
fruits2 = fruits[1:4]
|
||||
print(fruits2)
|
||||
# fruit3 = fruits # 没有复制列表只创建了新的引用
|
||||
# 可以通过完整切片操作来复制列表
|
||||
fruits3 = fruits[:]
|
||||
print(fruits3)
|
||||
fruits4 = fruits[-3:-1]
|
||||
print(fruits4)
|
||||
# 可以通过反向切片操作来获得倒转后的列表的拷贝
|
||||
fruits5 = fruits[::-1]
|
||||
print(fruits5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
fruits = ['grape', 'apple', 'strawberry', 'waxberry']
|
||||
fruits += ['pitaya', 'pear', 'mango']
|
||||
# 列表切片
|
||||
fruits2 = fruits[1:4]
|
||||
print(fruits2) # apple strawberry waxberry
|
||||
# 可以通过完整切片操作来复制列表
|
||||
fruits3 = fruits[:]
|
||||
print(fruits3) # ['grape', 'apple', 'strawberry', 'waxberry', 'pitaya', 'pear', 'mango']
|
||||
fruits4 = fruits[-3:-1]
|
||||
print(fruits4) # ['pitaya', 'pear']
|
||||
# 可以通过反向切片操作来获得倒转后的列表的拷贝
|
||||
fruits5 = fruits[::-1]
|
||||
print(fruits5) # ['mango', 'pear', 'pitaya', 'waxberry', 'strawberry', 'apple', 'grape']
|
||||
```
|
||||
|
||||
下面的代码实现了对列表的排序操作。
|
||||
|
||||
```Python
|
||||
def main():
|
||||
list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry']
|
||||
list2 = sorted(list1)
|
||||
# sorted函数返回列表排序后的拷贝不会修改传入的列表
|
||||
# 函数的设计就应该像sorted函数一样尽可能不产生副作用
|
||||
list3 = sorted(list1, reverse=True)
|
||||
# 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序
|
||||
list4 = sorted(list1, key=len)
|
||||
print(list1)
|
||||
print(list2)
|
||||
print(list3)
|
||||
print(list4)
|
||||
# 给列表对象发出排序消息直接在列表对象上进行排序
|
||||
list1.sort(reverse=True)
|
||||
print(list1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry']
|
||||
list2 = sorted(list1)
|
||||
# sorted函数返回列表排序后的拷贝不会修改传入的列表
|
||||
# 函数的设计就应该像sorted函数一样尽可能不产生副作用
|
||||
list3 = sorted(list1, reverse=True)
|
||||
# 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序
|
||||
list4 = sorted(list1, key=len)
|
||||
print(list1)
|
||||
print(list2)
|
||||
print(list3)
|
||||
print(list4)
|
||||
# 给列表对象发出排序消息直接在列表对象上进行排序
|
||||
list1.sort(reverse=True)
|
||||
print(list1)
|
||||
```
|
||||
|
||||
### 生成式和生成器
|
||||
|
||||
我们还可以使用列表的生成式语法来创建列表,代码如下所示。
|
||||
|
||||
```Python
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
f = [x for x in range(1, 10)]
|
||||
print(f)
|
||||
f = [x + y for x in 'ABCDE' for y in '1234567']
|
||||
print(f)
|
||||
# 用列表的生成表达式语法创建列表容器
|
||||
# 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
|
||||
f = [x ** 2 for x in range(1, 1000)]
|
||||
print(sys.getsizeof(f)) # 查看对象占用内存的字节数
|
||||
print(f)
|
||||
# 请注意下面的代码创建的不是一个列表而是一个生成器对象
|
||||
# 通过生成器可以获取到数据但它不占用额外的空间存储数据
|
||||
# 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间)
|
||||
f = (x ** 2 for x in range(1, 1000))
|
||||
print(sys.getsizeof(f)) # 相比生成式生成器不占用存储数据的空间
|
||||
print(f)
|
||||
for val in f:
|
||||
f = [x for x in range(1, 10)]
|
||||
print(f)
|
||||
f = [x + y for x in 'ABCDE' for y in '1234567']
|
||||
print(f)
|
||||
# 用列表的生成表达式语法创建列表容器
|
||||
# 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
|
||||
f = [x ** 2 for x in range(1, 1000)]
|
||||
print(sys.getsizeof(f)) # 查看对象占用内存的字节数
|
||||
print(f)
|
||||
# 请注意下面的代码创建的不是一个列表而是一个生成器对象
|
||||
# 通过生成器可以获取到数据但它不占用额外的空间存储数据
|
||||
# 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间)
|
||||
f = (x ** 2 for x in range(1, 1000))
|
||||
print(sys.getsizeof(f)) # 相比生成式生成器不占用存储数据的空间
|
||||
print(f)
|
||||
for val in f:
|
||||
print(val)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
除了上面提到的生成器语法,Python中还有另外一种定义生成器的方式,就是通过`yield`关键字将一个普通函数改造成生成器函数。下面的代码演示了如何实现一个生成[斐波拉切数列](https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97)的生成器。所谓斐波拉切数列可以通过下面[递归](https://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92)的方法来进行定义:
|
||||
|
@ -212,39 +276,34 @@ if __name__ == '__main__':
|
|||
|
||||
### 使用元组
|
||||
|
||||
Python 的元组与列表类似,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
|
||||
Python中的元组与列表类似也是一种容器数据类型,可以用一个变量(对象)来存储多个数据,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
|
||||
|
||||
```Python
|
||||
def main():
|
||||
# 定义元组
|
||||
t = ('骆昊', 38, True, '四川成都')
|
||||
print(t)
|
||||
# 获取元组中的元素
|
||||
print(t[0])
|
||||
print(t[3])
|
||||
# 遍历元组中的值
|
||||
for member in t:
|
||||
# 定义元组
|
||||
t = ('骆昊', 38, True, '四川成都')
|
||||
print(t)
|
||||
# 获取元组中的元素
|
||||
print(t[0])
|
||||
print(t[3])
|
||||
# 遍历元组中的值
|
||||
for member in t:
|
||||
print(member)
|
||||
# 重新给元组赋值
|
||||
# t[0] = '王大锤' # TypeError
|
||||
# 变量t重新引用了新的元组原来的元组将被垃圾回收
|
||||
t = ('王大锤', 20, True, '云南昆明')
|
||||
print(t)
|
||||
# 将元组转换成列表
|
||||
person = list(t)
|
||||
print(person)
|
||||
# 列表是可以修改它的元素的
|
||||
person[0] = '李小龙'
|
||||
person[1] = 25
|
||||
print(person)
|
||||
# 将列表转换成元组
|
||||
fruits_list = ['apple', 'banana', 'orange']
|
||||
fruits_tuple = tuple(fruits_list)
|
||||
print(fruits_tuple)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
# 重新给元组赋值
|
||||
# t[0] = '王大锤' # TypeError
|
||||
# 变量t重新引用了新的元组原来的元组将被垃圾回收
|
||||
t = ('王大锤', 20, True, '云南昆明')
|
||||
print(t)
|
||||
# 将元组转换成列表
|
||||
person = list(t)
|
||||
print(person)
|
||||
# 列表是可以修改它的元素的
|
||||
person[0] = '李小龙'
|
||||
person[1] = 25
|
||||
print(person)
|
||||
# 将列表转换成元组
|
||||
fruits_list = ['apple', 'banana', 'orange']
|
||||
fruits_tuple = tuple(fruits_list)
|
||||
print(fruits_tuple)
|
||||
```
|
||||
|
||||
这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢?
|
||||
|
@ -260,96 +319,106 @@ Python中的集合跟数学上的集合是一致的,不允许有重复元素
|
|||
|
||||
![](./res/python-set.png)
|
||||
|
||||
可以按照下面代码所示的方式来创建和使用集合。
|
||||
|
||||
```Python
|
||||
def main():
|
||||
set1 = {1, 2, 3, 3, 3, 2}
|
||||
print(set1)
|
||||
print('Length =', len(set1))
|
||||
set2 = set(range(1, 10))
|
||||
print(set2)
|
||||
set1.add(4)
|
||||
set1.add(5)
|
||||
set2.update([11, 12])
|
||||
print(set1)
|
||||
print(set2)
|
||||
set2.discard(5)
|
||||
# remove的元素如果不存在会引发KeyError
|
||||
if 4 in set2:
|
||||
# 创建集合的字面量语法
|
||||
set1 = {1, 2, 3, 3, 3, 2}
|
||||
print(set1)
|
||||
print('Length =', len(set1))
|
||||
# 创建集合的构造器语法(面向对象部分会进行详细讲解)
|
||||
set2 = set(range(1, 10))
|
||||
set3 = set((1, 2, 3, 3, 2, 1))
|
||||
print(set2, set3)
|
||||
# 创建集合的推导式语法(推导式也可以用于推导集合)
|
||||
set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0}
|
||||
print(set4)
|
||||
```
|
||||
|
||||
向集合添加元素和从集合删除元素。
|
||||
|
||||
```Python
|
||||
set1.add(4)
|
||||
set1.add(5)
|
||||
set2.update([11, 12])
|
||||
set2.discard(5)
|
||||
if 4 in set2:
|
||||
set2.remove(4)
|
||||
print(set2)
|
||||
# 遍历集合容器
|
||||
for elem in set2:
|
||||
print(elem ** 2, end=' ')
|
||||
print()
|
||||
# 将元组转换成集合
|
||||
set3 = set((1, 2, 3, 3, 2, 1))
|
||||
print(set3.pop())
|
||||
print(set3)
|
||||
# 集合的交集、并集、差集、对称差运算
|
||||
print(set1 & set2)
|
||||
# print(set1.intersection(set2))
|
||||
print(set1 | set2)
|
||||
# print(set1.union(set2))
|
||||
print(set1 - set2)
|
||||
# print(set1.difference(set2))
|
||||
print(set1 ^ set2)
|
||||
# print(set1.symmetric_difference(set2))
|
||||
# 判断子集和超集
|
||||
print(set2 <= set1)
|
||||
# print(set2.issubset(set1))
|
||||
print(set3 <= set1)
|
||||
# print(set3.issubset(set1))
|
||||
print(set1 >= set2)
|
||||
# print(set1.issuperset(set2))
|
||||
print(set1 >= set3)
|
||||
# print(set1.issuperset(set3))
|
||||
print(set1, set2)
|
||||
print(set3.pop())
|
||||
print(set3)
|
||||
```
|
||||
|
||||
集合的成员、交集、并集、差集等运算。
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```Python
|
||||
# 集合的交集、并集、差集、对称差运算
|
||||
print(set1 & set2)
|
||||
# print(set1.intersection(set2))
|
||||
print(set1 | set2)
|
||||
# print(set1.union(set2))
|
||||
print(set1 - set2)
|
||||
# print(set1.difference(set2))
|
||||
print(set1 ^ set2)
|
||||
# print(set1.symmetric_difference(set2))
|
||||
# 判断子集和超集
|
||||
print(set2 <= set1)
|
||||
# print(set2.issubset(set1))
|
||||
print(set3 <= set1)
|
||||
# print(set3.issubset(set1))
|
||||
print(set1 >= set2)
|
||||
# print(set1.issuperset(set2))
|
||||
print(set1 >= set3)
|
||||
# print(set1.issuperset(set3))
|
||||
```
|
||||
|
||||
> **说明:** Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符(后面的章节中会讲到),上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如`&`运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。
|
||||
|
||||
### 使用字典
|
||||
|
||||
字典是另一种可变容器模型,类似于我们生活中使用的字典,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。
|
||||
字典是另一种可变容器模型,Python中的字典跟我们生活中使用的字典是一样一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。
|
||||
|
||||
```Python
|
||||
def main():
|
||||
scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82}
|
||||
# 通过键可以获取字典中对应的值
|
||||
print(scores['骆昊'])
|
||||
print(scores['狄仁杰'])
|
||||
# 对字典进行遍历(遍历的其实是键再通过键取对应的值)
|
||||
for elem in scores:
|
||||
print('%s\t--->\t%d' % (elem, scores[elem]))
|
||||
# 更新字典中的元素
|
||||
scores['白元芳'] = 65
|
||||
scores['诸葛王朗'] = 71
|
||||
scores.update(冷面=67, 方启鹤=85)
|
||||
print(scores)
|
||||
if '武则天' in scores:
|
||||
# 创建字典的字面量语法
|
||||
scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82}
|
||||
print(scores)
|
||||
# 创建字典的构造器语法
|
||||
items1 = dict(one=1, two=2, three=3, four=4)
|
||||
# 通过zip函数将两个序列压成字典
|
||||
items2 = dict(zip(['a', 'b', 'c'], '123'))
|
||||
# 创建字典的推导式语法
|
||||
items3 = {num: num ** 2 for num in range(1, 10)}
|
||||
print(items1, items2, items3)
|
||||
# 通过键可以获取字典中对应的值
|
||||
print(scores['骆昊'])
|
||||
print(scores['狄仁杰'])
|
||||
# 对字典中所有键值对进行遍历
|
||||
for key in scores:
|
||||
print(f'{key}: {scores[key]}')
|
||||
# 更新字典中的元素
|
||||
scores['白元芳'] = 65
|
||||
scores['诸葛王朗'] = 71
|
||||
scores.update(冷面=67, 方启鹤=85)
|
||||
print(scores)
|
||||
if '武则天' in scores:
|
||||
print(scores['武则天'])
|
||||
print(scores.get('武则天'))
|
||||
# get方法也是通过键获取对应的值但是可以设置默认值
|
||||
print(scores.get('武则天', 60))
|
||||
# 删除字典中的元素
|
||||
print(scores.popitem())
|
||||
print(scores.popitem())
|
||||
print(scores.pop('骆昊', 100))
|
||||
# 清空字典
|
||||
scores.clear()
|
||||
print(scores)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
print(scores.get('武则天'))
|
||||
# get方法也是通过键获取对应的值但是可以设置默认值
|
||||
print(scores.get('武则天', 60))
|
||||
# 删除字典中的元素
|
||||
print(scores.popitem())
|
||||
print(scores.popitem())
|
||||
print(scores.pop('骆昊', 100))
|
||||
# 清空字典
|
||||
scores.clear()
|
||||
print(scores)
|
||||
```
|
||||
|
||||
### 练习
|
||||
|
||||
#### 练习1:在屏幕上显示跑马灯文字
|
||||
#### 练习1:在屏幕上显示跑马灯文字。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
import os
|
||||
|
@ -373,6 +442,8 @@ if __name__ == '__main__':
|
|||
|
||||
#### 练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
import random
|
||||
|
||||
|
@ -396,6 +467,8 @@ def generate_code(code_len=4):
|
|||
|
||||
#### 练习3:设计一个函数返回给定文件名的后缀名。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
def get_suffix(filename, has_dot=False):
|
||||
"""
|
||||
|
@ -415,6 +488,8 @@ def get_suffix(filename, has_dot=False):
|
|||
|
||||
#### 练习4:设计一个函数返回传入的列表中最大和第二大的元素的值。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
def max2(x):
|
||||
m1, m2 = (x[0], x[1]) if x[0] > x[1] else (x[1], x[0])
|
||||
|
@ -427,7 +502,9 @@ def max2(x):
|
|||
return m1, m2
|
||||
```
|
||||
|
||||
#### 练习5:计算指定的年月日是这一年的第几天
|
||||
#### 练习5:计算指定的年月日是这一年的第几天。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
def is_leap_year(year):
|
||||
|
@ -472,6 +549,8 @@ if __name__ == '__main__':
|
|||
|
||||
#### 练习6:打印[杨辉三角](https://zh.wikipedia.org/wiki/%E6%9D%A8%E8%BE%89%E4%B8%89%E8%A7%92%E5%BD%A2)。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
def main():
|
||||
num = int(input('Number of rows: '))
|
||||
|
@ -493,7 +572,7 @@ if __name__ == '__main__':
|
|||
|
||||
### 综合案例
|
||||
|
||||
#### 案例1:双色球选号
|
||||
#### 案例1:双色球选号。
|
||||
|
||||
```Python
|
||||
from random import randrange, randint, sample
|
||||
|
@ -534,7 +613,7 @@ if __name__ == '__main__':
|
|||
|
||||
> **说明:** 上面使用random模块的sample函数来实现从列表中选择不重复的n个元素。
|
||||
|
||||
#### 综合案例2:[约瑟夫环问题](https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98)
|
||||
#### 综合案例2:[约瑟夫环问题](https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98)。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -564,7 +643,7 @@ if __name__ == '__main__':
|
|||
|
||||
```
|
||||
|
||||
#### 综合案例3:[井字棋](https://zh.wikipedia.org/wiki/%E4%BA%95%E5%AD%97%E6%A3%8B)游戏
|
||||
#### 综合案例3:[井字棋](https://zh.wikipedia.org/wiki/%E4%BA%95%E5%AD%97%E6%A3%8B)游戏。
|
||||
|
||||
```Python
|
||||
import os
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
## 面向对象编程基础
|
||||
|
||||
活在当下的程序员应该都听过“面向对象编程”一词,也经常有人问能不能用一句话解释下什么是“面向对象编程”,我们先来看看比较正式的说法。
|
||||
活在当下的程序员应该都听过"面向对象编程"一词,也经常有人问能不能用一句话解释下什么是"面向对象编程",我们先来看看比较正式的说法。
|
||||
|
||||
“把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。”
|
||||
"把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。"
|
||||
|
||||
这样一说是不是更不明白了。所以我们还是看看更通俗易懂的说法,下面这段内容来自于[知乎](https://www.zhihu.com/)。
|
||||
|
||||
|
@ -10,9 +10,9 @@
|
|||
|
||||
> **说明:** 以上的内容来自于网络,不代表作者本人的观点和看法,与作者本人立场无关,相关责任不由作者承担。
|
||||
|
||||
之前我们说过“程序是指令的集合”,我们在程序中书写的语句在执行时会变成一条或多条指令然后由CPU去执行。当然为了简化程序的设计,我们引入了函数的概念,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些功能的时候只要调用函数即可;如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数继续切分为子函数来降低系统的复杂性。但是说了这么多,不知道大家是否发现,所谓编程就是程序员按照计算机的工作方式控制计算机完成各种任务。但是,计算机的工作方式与正常人类的思维模式是不同的,如果编程就必须得抛弃人类正常的思维方式去迎合计算机,编程的乐趣就少了很多,“每个人都应该学习编程”这样的豪言壮语就只能说说而已。当然,这些还不是最重要的,最重要的是当我们需要开发一个复杂的系统时,代码的复杂性会让开发和维护工作都变得举步维艰,所以在上世纪60年代末期,“[软件危机](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%8D%B1%E6%9C%BA)”、“[软件工程](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B)”等一系列的概念开始在行业中出现。
|
||||
之前我们说过"**程序是指令的集合**",我们在程序中书写的语句在执行时会变成一条或多条指令然后由CPU去执行。当然为了简化程序的设计,我们引入了函数的概念,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些功能的时候只要调用函数即可;如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数继续切分为子函数来降低系统的复杂性。但是说了这么多,不知道大家是否发现,所谓编程就是程序员按照计算机的工作方式控制计算机完成各种任务。但是,计算机的工作方式与正常人类的思维模式是不同的,如果编程就必须得抛弃人类正常的思维方式去迎合计算机,编程的乐趣就少了很多,"每个人都应该学习编程"这样的豪言壮语就只能说说而已。当然,这些还不是最重要的,最重要的是当我们需要开发一个复杂的系统时,代码的复杂性会让开发和维护工作都变得举步维艰,所以在上世纪60年代末期,"[软件危机](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%8D%B1%E6%9C%BA)"、"[软件工程](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B)"等一系列的概念开始在行业中出现。
|
||||
|
||||
当然,程序员圈子内的人都知道,现实中并没有解决上面所说的这些问题的“[银弹](https://zh.wikipedia.org/wiki/%E6%B2%A1%E6%9C%89%E9%93%B6%E5%BC%B9)”,真正让软件开发者看到希望的是上世纪70年代诞生的[Smalltalk](https://zh.wikipedia.org/wiki/Smalltalk)编程语言中引入的面向对象的编程思想(面向对象编程的雏形可以追溯到更早期的[Simula](https://zh.wikipedia.org/wiki/Simula)语言)。按照这种编程理念,程序中的数据和操作数据的函数是一个逻辑上的整体,我们称之为“对象”,而我们解决问题的方式就是创建出需要的对象并向对象发出各种各样的消息,多个对象的协同工作最终可以让我们构造出复杂的系统来解决现实中的问题。
|
||||
当然,程序员圈子内的人都知道,现实中并没有解决上面所说的这些问题的"[银弹](https://zh.wikipedia.org/wiki/%E6%B2%A1%E6%9C%89%E9%93%B6%E5%BC%B9)",真正让软件开发者看到希望的是上世纪70年代诞生的[Smalltalk](https://zh.wikipedia.org/wiki/Smalltalk)编程语言中引入的面向对象的编程思想(面向对象编程的雏形可以追溯到更早期的[Simula](https://zh.wikipedia.org/wiki/Simula)语言)。按照这种编程理念,程序中的数据和操作数据的函数是一个逻辑上的整体,我们称之为“对象”,而我们解决问题的方式就是创建出需要的对象并向对象发出各种各样的消息,多个对象的协同工作最终可以让我们构造出复杂的系统来解决现实中的问题。
|
||||
|
||||
> **说明:** 当然面向对象也不是解决软件开发中所有问题的最后的“银弹”,所以今天的高级程序设计语言几乎都提供了对多种编程范式的支持,Python也不例外。
|
||||
|
||||
|
@ -97,7 +97,7 @@ if __name__ == "__main__":
|
|||
main()
|
||||
```
|
||||
|
||||
但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。
|
||||
但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是"**We are all consenting adults here**"。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。
|
||||
|
||||
```Python
|
||||
class Test:
|
||||
|
@ -124,11 +124,13 @@ if __name__ == "__main__":
|
|||
|
||||
### 面向对象的支柱
|
||||
|
||||
面向对象有三大支柱:封装、继承和多态。后面两个概念在下一个章节中进行详细的说明,这里我们先说一下什么是封装。我自己对封装的理解是“隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口”。我们在类中定义的方法其实就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数(方法的外部视图),而不需要知道方法内部的实现细节(方法的内部视图)。
|
||||
面向对象有三大支柱:封装、继承和多态。后面两个概念在下一个章节中进行详细的说明,这里我们先说一下什么是封装。我自己对封装的理解是"隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口"。我们在类中定义的方法其实就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数(方法的外部视图),而不需要知道方法内部的实现细节(方法的内部视图)。
|
||||
|
||||
### 练习
|
||||
|
||||
#### 练习1:定义一个类描述数字时钟
|
||||
#### 练习1:定义一个类描述数字时钟。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
from time import sleep
|
||||
|
@ -180,6 +182,8 @@ if __name__ == '__main__':
|
|||
|
||||
#### 练习2:定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法。
|
||||
|
||||
参考答案:
|
||||
|
||||
```Python
|
||||
from math import sqrt
|
||||
|
||||
|
|
|
@ -331,7 +331,7 @@ if __name__ == '__main__':
|
|||
|
||||
### 综合案例
|
||||
|
||||
#### 案例1:奥特曼打小怪兽
|
||||
#### 案例1:奥特曼打小怪兽。
|
||||
|
||||
```Python
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
@ -521,7 +521,7 @@ if __name__ == '__main__':
|
|||
main()
|
||||
```
|
||||
|
||||
#### 案例2:扑克游戏
|
||||
#### 案例2:扑克游戏。
|
||||
|
||||
```Python
|
||||
import random
|
||||
|
@ -638,7 +638,7 @@ if __name__ == '__main__':
|
|||
|
||||
>**说明:** 大家可以自己尝试在上面代码的基础上写一个简单的扑克游戏,例如21点(Black Jack),游戏的规则可以自己在网上找一找。
|
||||
|
||||
#### 案例3:工资结算系统
|
||||
#### 案例3:工资结算系统。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
"""
|
||||
用Python的turtle模块绘制国旗
|
||||
"""
|
||||
import turtle
|
||||
|
||||
|
||||
def draw_rectangle(x, y, width, height):
|
||||
"""绘制矩形"""
|
||||
turtle.goto(x, y)
|
||||
turtle.pencolor('red')
|
||||
turtle.fillcolor('red')
|
||||
turtle.begin_fill()
|
||||
for i in range(2):
|
||||
turtle.forward(width)
|
||||
turtle.left(90)
|
||||
turtle.forward(height)
|
||||
turtle.left(90)
|
||||
turtle.end_fill()
|
||||
|
||||
|
||||
def draw_star(x, y, radius):
|
||||
"""绘制五角星"""
|
||||
turtle.setpos(x, y)
|
||||
pos1 = turtle.pos()
|
||||
turtle.circle(-radius, 72)
|
||||
pos2 = turtle.pos()
|
||||
turtle.circle(-radius, 72)
|
||||
pos3 = turtle.pos()
|
||||
turtle.circle(-radius, 72)
|
||||
pos4 = turtle.pos()
|
||||
turtle.circle(-radius, 72)
|
||||
pos5 = turtle.pos()
|
||||
turtle.color('yellow', 'yellow')
|
||||
turtle.begin_fill()
|
||||
turtle.goto(pos3)
|
||||
turtle.goto(pos1)
|
||||
turtle.goto(pos4)
|
||||
turtle.goto(pos2)
|
||||
turtle.goto(pos5)
|
||||
turtle.end_fill()
|
||||
|
||||
|
||||
def main():
|
||||
"""主程序"""
|
||||
turtle.speed(12)
|
||||
turtle.penup()
|
||||
x, y = -270, -180
|
||||
# 画国旗主体
|
||||
width, height = 540, 360
|
||||
draw_rectangle(x, y, width, height)
|
||||
# 画大星星
|
||||
pice = 22
|
||||
center_x, center_y = x + 5 * pice, y + height - pice * 5
|
||||
turtle.goto(center_x, center_y)
|
||||
turtle.left(90)
|
||||
turtle.forward(pice * 3)
|
||||
turtle.right(90)
|
||||
draw_star(turtle.xcor(), turtle.ycor(), pice * 3)
|
||||
x_poses, y_poses = [10, 12, 12, 10], [2, 4, 7, 9]
|
||||
# 画小星星
|
||||
for x_pos, y_pos in zip(x_poses, y_poses):
|
||||
turtle.goto(x + x_pos * pice, y + height - y_pos * pice)
|
||||
turtle.left(turtle.towards(center_x, center_y) - turtle.heading())
|
||||
turtle.forward(pice)
|
||||
turtle.right(90)
|
||||
draw_star(turtle.xcor(), turtle.ycor(), pice)
|
||||
# 隐藏海龟
|
||||
turtle.ht()
|
||||
# 显示绘图窗口
|
||||
turtle.mainloop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 487 B After Width: | Height: | Size: 419 B |
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 461 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1016 B |
Before Width: | Height: | Size: 816 B After Width: | Height: | Size: 683 B |
Before Width: | Height: | Size: 365 B After Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 262 B |
Before Width: | Height: | Size: 798 B After Width: | Height: | Size: 621 B |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 169 KiB |
Before Width: | Height: | Size: 609 KiB After Width: | Height: | Size: 476 KiB |
Before Width: | Height: | Size: 546 KiB After Width: | Height: | Size: 433 KiB |
Before Width: | Height: | Size: 383 KiB After Width: | Height: | Size: 301 KiB |
Before Width: | Height: | Size: 506 KiB After Width: | Height: | Size: 414 KiB |
Before Width: | Height: | Size: 486 KiB After Width: | Height: | Size: 397 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 1012 KiB After Width: | Height: | Size: 824 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 349 KiB After Width: | Height: | Size: 268 KiB |
Before Width: | Height: | Size: 398 KiB After Width: | Height: | Size: 180 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 12 KiB |
|
@ -1,31 +1,137 @@
|
|||
## Python语言进阶
|
||||
|
||||
1. 数据结构和算法
|
||||
### 重要知识点
|
||||
|
||||
- 算法:解决问题的方法和步骤
|
||||
- 生成式(推导式)的用法
|
||||
|
||||
- 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。
|
||||
```Python
|
||||
prices = {
|
||||
'AAPL': 191.88,
|
||||
'GOOG': 1186.96,
|
||||
'IBM': 149.24,
|
||||
'ORCL': 48.44,
|
||||
'ACN': 166.89,
|
||||
'FB': 208.09,
|
||||
'SYMC': 21.29
|
||||
}
|
||||
# 用股票价格大于100元的股票构造一个新的字典
|
||||
prices2 = {key: value for key, value in prices.items() if value > 100}
|
||||
print(prices2)
|
||||
```
|
||||
|
||||
- 渐近时间复杂度的大O标记:
|
||||
> 说明:生成式(推导式)可以用来生成列表、集合和字典。
|
||||
|
||||
- 嵌套的列表的坑
|
||||
|
||||
```Python
|
||||
names = ['关羽', '张飞', '赵云', '马超', '黄忠']
|
||||
courses = ['语文', '数学', '英语']
|
||||
# 录入五个学生三门课程的成绩
|
||||
# 错误 - 参考http://pythontutor.com/visualize.html#mode=edit
|
||||
# scores = [[None] * len(courses)] * len(names)
|
||||
scores = [[None] * len(courses) for _ in range(len(names))]
|
||||
for row, name in enumerate(names):
|
||||
for col, course in enumerate(courses):
|
||||
scores[row][col] = float(input(f'请输入{name}的{course}成绩: '))
|
||||
print(scores)
|
||||
```
|
||||
|
||||
[Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP
|
||||
|
||||
- `heapq`模块(堆排序)
|
||||
|
||||
```Python
|
||||
"""
|
||||
从列表中找出最大的或最小的N个元素
|
||||
堆结构(大根堆/小根堆)
|
||||
"""
|
||||
import heapq
|
||||
|
||||
list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
|
||||
list2 = [
|
||||
{'name': 'IBM', 'shares': 100, 'price': 91.1},
|
||||
{'name': 'AAPL', 'shares': 50, 'price': 543.22},
|
||||
{'name': 'FB', 'shares': 200, 'price': 21.09},
|
||||
{'name': 'HPQ', 'shares': 35, 'price': 31.75},
|
||||
{'name': 'YHOO', 'shares': 45, 'price': 16.35},
|
||||
{'name': 'ACME', 'shares': 75, 'price': 115.65}
|
||||
]
|
||||
print(heapq.nlargest(3, list1))
|
||||
print(heapq.nsmallest(3, list1))
|
||||
print(heapq.nlargest(2, list2, key=lambda x: x['price']))
|
||||
print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
|
||||
```
|
||||
|
||||
- `itertools`模块
|
||||
|
||||
```Python
|
||||
"""
|
||||
迭代工具模块
|
||||
"""
|
||||
import itertools
|
||||
|
||||
# 产生ABCD的全排列
|
||||
itertools.permutations('ABCD')
|
||||
# 产生ABCDE的五选三组合
|
||||
itertools.combinations('ABCDE', 3)
|
||||
# 产生ABCD和123的笛卡尔积
|
||||
itertools.product('ABCD', '123')
|
||||
# 产生ABC的无限循环序列
|
||||
itertools.cycle(('A', 'B', 'C'))
|
||||
```
|
||||
|
||||
- `collections`模块
|
||||
|
||||
常用的工具类:
|
||||
|
||||
- `namedtuple`:命令元组,它是一个类工厂,接受类型的名称和属性列表来创建一个类。
|
||||
- `deque`:双端队列,是列表的替代实现。Python中的列表底层是基于数组来实现的,而deque底层是双向链表,因此当你需要在头尾添加和删除元素是,deque会表现出更好的性能,渐近时间复杂度为$O(1)$。
|
||||
- `Counter`:`dict`的子类,键是元素,值是元素的计数,它的`most_common()`方法可以帮助我们获取出现频率最高的元素。`Counter`和`dict`的继承关系我认为是值得商榷的,按照CARP原则,`Counter`跟`dict`的关系应该设计为关联关系更为合理。
|
||||
- `OrderedDict`:`dict`的子类,它记录了键值对插入的顺序,看起来既有字典的行为,也有链表的行为。
|
||||
- `defaultdict`:类似于字典类型,但是可以通过默认的工厂函数来获得键对应的默认值,相比字典中的`setdefault()`方法,这种做法更加高效。
|
||||
|
||||
```Python
|
||||
"""
|
||||
找出序列中出现次数最多的元素
|
||||
"""
|
||||
from collections import Counter
|
||||
|
||||
words = [
|
||||
'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
|
||||
'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
|
||||
'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes',
|
||||
'look', 'into', 'my', 'eyes', "you're", 'under'
|
||||
]
|
||||
counter = Counter(words)
|
||||
print(counter.most_common(3))
|
||||
```
|
||||
|
||||
### 数据结构和算法
|
||||
|
||||
- 算法:解决问题的方法和步骤
|
||||
|
||||
- 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。
|
||||
|
||||
- 渐近时间复杂度的大O标记:
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(c)" /> - 常量时间复杂度 - 布隆过滤器 / 哈希存储
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(log_2n)" /> - 对数时间复杂度 - 折半查找(二分查找)
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n)" /> - 线性时间复杂度 - 顺序查找 / 桶排序
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n)" /> - 线性时间复杂度 - 顺序查找 / 计数排序
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n*log_2n)" /> - 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序)
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n^2)" /> - 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序)
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n^3)" /> - 立方时间复杂度 - Floyd算法 / 矩阵乘法运算
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(2^n)" /> - 几何级数时间复杂度 - 汉诺塔
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n!)" /> - 阶乘时间复杂度 - 旅行经销商问题 - NP
|
||||
- <img src="http://latex.codecogs.com/gif.latex?O(n!)" /> - 阶乘时间复杂度 - 旅行经销商问题 - NPC
|
||||
|
||||
![](./res/algorithm_complexity_1.png)
|
||||
|
||||
![](./res/algorithm_complexity_2.png)
|
||||
|
||||
- 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)
|
||||
- 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)
|
||||
|
||||
```Python
|
||||
def select_sort(origin_items, comp=lambda x, y: x < y):
|
||||
def select_sort(items, comp=lambda x, y: x < y):
|
||||
"""简单选择排序"""
|
||||
items = origin_items[:]
|
||||
items = items[:]
|
||||
for i in range(len(items) - 1):
|
||||
min_index = i
|
||||
for j in range(i + 1, len(items)):
|
||||
|
@ -36,9 +142,24 @@
|
|||
```
|
||||
|
||||
```Python
|
||||
def bubble_sort(origin_items, comp=lambda x, y: x > y):
|
||||
"""高质量冒泡排序(搅拌排序)"""
|
||||
items = origin_items[:]
|
||||
def bubble_sort(items, comp=lambda x, y: x > y):
|
||||
"""冒泡排序"""
|
||||
items = items[:]
|
||||
for i in range(len(items) - 1):
|
||||
swapped = False
|
||||
for j in range(i, len(items) - 1 - i):
|
||||
if comp(items[j], items[j + 1]):
|
||||
items[j], items[j + 1] = items[j + 1], items[j]
|
||||
swapped = True
|
||||
if not swapped:
|
||||
break
|
||||
return items
|
||||
```
|
||||
|
||||
```Python
|
||||
def bubble_sort(items, comp=lambda x, y: x > y):
|
||||
"""搅拌排序(冒泡排序升级版)"""
|
||||
items = items[:]
|
||||
for i in range(len(items) - 1):
|
||||
swapped = False
|
||||
for j in range(i, len(items) - 1 - i):
|
||||
|
@ -57,17 +178,7 @@
|
|||
```
|
||||
|
||||
```Python
|
||||
def merge_sort(items, comp=lambda x, y: x <= y):
|
||||
"""归并排序(分治法)"""
|
||||
if len(items) < 2:
|
||||
return items[:]
|
||||
mid = len(items) // 2
|
||||
left = merge_sort(items[:mid], comp)
|
||||
right = merge_sort(items[mid:], comp)
|
||||
return merge(left, right, comp)
|
||||
|
||||
|
||||
def merge(items1, items2, comp):
|
||||
def merge(items1, items2, comp=lambda x, y: x < y):
|
||||
"""合并(将两个有序的列表合并成一个有序的列表)"""
|
||||
items = []
|
||||
index1, index2 = 0, 0
|
||||
|
@ -81,6 +192,20 @@
|
|||
items += items1[index1:]
|
||||
items += items2[index2:]
|
||||
return items
|
||||
|
||||
|
||||
def merge_sort(items, comp=lambda x, y: x < y):
|
||||
return _merge_sort(list(items), comp)
|
||||
|
||||
|
||||
def _merge_sort(items, comp):
|
||||
"""归并排序"""
|
||||
if len(items) < 2:
|
||||
return items
|
||||
mid = len(items) // 2
|
||||
left = _merge_sort(items[:mid], comp)
|
||||
right = _merge_sort(items[mid:], comp)
|
||||
return merge(left, right, comp)
|
||||
```
|
||||
|
||||
```Python
|
||||
|
@ -107,95 +232,7 @@
|
|||
return -1
|
||||
```
|
||||
|
||||
- 使用生成式(推导式)语法
|
||||
|
||||
```Python
|
||||
prices = {
|
||||
'AAPL': 191.88,
|
||||
'GOOG': 1186.96,
|
||||
'IBM': 149.24,
|
||||
'ORCL': 48.44,
|
||||
'ACN': 166.89,
|
||||
'FB': 208.09,
|
||||
'SYMC': 21.29
|
||||
}
|
||||
# 用股票价格大于100元的股票构造一个新的字典
|
||||
prices2 = {key: value for key, value in prices.items() if value > 100}
|
||||
print(prices2)
|
||||
```
|
||||
|
||||
> 说明:生成式(推导式)可以用来生成列表、集合和字典。
|
||||
|
||||
- 嵌套的列表
|
||||
|
||||
```Python
|
||||
names = ['关羽', '张飞', '赵云', '马超', '黄忠']
|
||||
courses = ['语文', '数学', '英语']
|
||||
# 录入五个学生三门课程的成绩
|
||||
# 错误 - 参考http://pythontutor.com/visualize.html#mode=edit
|
||||
# scores = [[None] * len(courses)] * len(names)
|
||||
scores = [[None] * len(courses) for _ in range(len(names))]
|
||||
for row, name in enumerate(names):
|
||||
for col, course in enumerate(courses):
|
||||
scores[row][col] = float(input(f'请输入{name}的{course}成绩: '))
|
||||
print(scores)
|
||||
```
|
||||
|
||||
[Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP
|
||||
|
||||
- heapq、itertools等的用法
|
||||
```Python
|
||||
"""
|
||||
从列表中找出最大的或最小的N个元素
|
||||
堆结构(大根堆/小根堆)
|
||||
"""
|
||||
import heapq
|
||||
|
||||
list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
|
||||
list2 = [
|
||||
{'name': 'IBM', 'shares': 100, 'price': 91.1},
|
||||
{'name': 'AAPL', 'shares': 50, 'price': 543.22},
|
||||
{'name': 'FB', 'shares': 200, 'price': 21.09},
|
||||
{'name': 'HPQ', 'shares': 35, 'price': 31.75},
|
||||
{'name': 'YHOO', 'shares': 45, 'price': 16.35},
|
||||
{'name': 'ACME', 'shares': 75, 'price': 115.65}
|
||||
]
|
||||
print(heapq.nlargest(3, list1))
|
||||
print(heapq.nsmallest(3, list1))
|
||||
print(heapq.nlargest(2, list2, key=lambda x: x['price']))
|
||||
print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
|
||||
```
|
||||
|
||||
```Python
|
||||
"""
|
||||
迭代工具 - 排列 / 组合 / 笛卡尔积
|
||||
"""
|
||||
import itertools
|
||||
|
||||
itertools.permutations('ABCD')
|
||||
itertools.combinations('ABCDE', 3)
|
||||
itertools.product('ABCD', '123')
|
||||
```
|
||||
|
||||
- collections模块下的工具类
|
||||
|
||||
```Python
|
||||
"""
|
||||
找出序列中出现次数最多的元素
|
||||
"""
|
||||
from collections import Counter
|
||||
|
||||
words = [
|
||||
'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
|
||||
'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
|
||||
'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes',
|
||||
'look', 'into', 'my', 'eyes', "you're", 'under'
|
||||
]
|
||||
counter = Counter(words)
|
||||
print(counter.most_common(3))
|
||||
```
|
||||
|
||||
- 常用算法:
|
||||
- 常用算法:
|
||||
|
||||
- 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。
|
||||
- 贪婪法 - 在对问题求解时,总是做出在当前看来
|
||||
|
@ -305,8 +342,8 @@
|
|||
"""
|
||||
快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大
|
||||
"""
|
||||
def quick_sort(origin_items, comp=lambda x, y: x <= y):
|
||||
items = origin_items[:]
|
||||
def quick_sort(items, comp=lambda x, y: x <= y):
|
||||
items = list(items)[:]
|
||||
_quick_sort(items, 0, len(items) - 1, comp)
|
||||
return items
|
||||
|
||||
|
@ -379,25 +416,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
动态规划例子1:[斐波拉切数列]()。(不使用动态规划将会是几何级数复杂度)
|
||||
|
||||
```Python
|
||||
"""
|
||||
动态规划 - 适用于有重叠子问题和最优子结构性质的问题
|
||||
使用动态规划方法所耗时间往往远少于朴素解法(用空间换取时间)
|
||||
"""
|
||||
def fib(num, temp={}):
|
||||
"""用递归计算Fibonacci数"""
|
||||
if num in (1, 2):
|
||||
return 1
|
||||
try:
|
||||
return temp[num]
|
||||
except KeyError:
|
||||
temp[num] = fib(num - 1) + fib(num - 2)
|
||||
return temp[num]
|
||||
```
|
||||
|
||||
动态规划例子2:子列表元素之和的最大值。(使用动态规划可以避免二重循环)
|
||||
动态规划例子:子列表元素之和的最大值。
|
||||
|
||||
> 说明:子列表指的是列表中索引(下标)连续的元素构成的列表;列表中的元素是int类型,可能包含正整数、0、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如:
|
||||
>
|
||||
|
@ -416,43 +435,43 @@
|
|||
```Python
|
||||
def main():
|
||||
items = list(map(int, input().split()))
|
||||
size = len(items)
|
||||
overall, partial = {}, {}
|
||||
overall[size - 1] = partial[size - 1] = items[size - 1]
|
||||
for i in range(size - 2, -1, -1):
|
||||
partial[i] = max(items[i], partial[i + 1] + items[i])
|
||||
overall[i] = max(partial[i], overall[i + 1])
|
||||
print(overall[0])
|
||||
overall = partial = items[0]
|
||||
for i in range(1, len(items)):
|
||||
partial = max(items[i], partial + items[i])
|
||||
overall = max(partial, overall)
|
||||
print(overall)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
2. 函数的使用方式
|
||||
> **说明**:这个题目最容易想到的解法是使用二重循环,但是代码的时间性能将会变得非常的糟糕。使用动态规划的思想,仅仅是多用了两个变量,就将原来$O(N^2)$复杂度的问题变成了$O(N)$。
|
||||
|
||||
- 将函数视为“一等公民”
|
||||
### 函数的使用方式
|
||||
|
||||
- 将函数视为“一等公民”
|
||||
|
||||
- 函数可以赋值给变量
|
||||
- 函数可以作为函数的参数
|
||||
- 函数可以作为函数的返回值
|
||||
|
||||
- 高阶函数的用法(`filter`、`map`以及它们的替代品)
|
||||
- 高阶函数的用法(`filter`、`map`以及它们的替代品)
|
||||
|
||||
```Python
|
||||
items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, range(1, 10))))
|
||||
items2 = [x ** 2 for x in range(1, 10) if x % 2]
|
||||
```
|
||||
|
||||
- 位置参数、可变参数、关键字参数、命名关键字参数
|
||||
- 位置参数、可变参数、关键字参数、命名关键字参数
|
||||
|
||||
- 参数的元信息(代码可读性问题)
|
||||
- 参数的元信息(代码可读性问题)
|
||||
|
||||
- 匿名函数和内联函数的用法(`lambda`函数)
|
||||
- 匿名函数和内联函数的用法(`lambda`函数)
|
||||
|
||||
- 闭包和作用域问题
|
||||
- 闭包和作用域问题
|
||||
|
||||
- Python搜索变量的LEGB顺序(Local --> Embedded --> Global --> Built-in)
|
||||
- Python搜索变量的LEGB顺序(Local >>> Embedded >>> Global >>> Built-in)
|
||||
|
||||
- `global`和`nonlocal`关键字的作用
|
||||
|
||||
|
@ -460,7 +479,7 @@
|
|||
|
||||
`nonlocal`:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。
|
||||
|
||||
- 装饰器函数(使用装饰器和取消装饰器)
|
||||
- 装饰器函数(使用装饰器和取消装饰器)
|
||||
|
||||
例子:输出函数执行时间的装饰器。
|
||||
|
||||
|
@ -478,7 +497,7 @@
|
|||
return wrapper
|
||||
```
|
||||
|
||||
如果装饰器不希望跟`print`函数耦合,可以编写带参数的装饰器。
|
||||
如果装饰器不希望跟`print`函数耦合,可以编写可以参数化的装饰器。
|
||||
|
||||
```Python
|
||||
from functools import wraps
|
||||
|
@ -486,7 +505,7 @@
|
|||
|
||||
|
||||
def record(output):
|
||||
"""自定义带参数的装饰器"""
|
||||
"""可以参数化的装饰器"""
|
||||
|
||||
def decorate(func):
|
||||
|
||||
|
@ -508,7 +527,7 @@
|
|||
|
||||
|
||||
class Record():
|
||||
"""自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)"""
|
||||
"""通过定义类的方式定义装饰器"""
|
||||
|
||||
def __init__(self, output):
|
||||
self.output = output
|
||||
|
@ -525,7 +544,7 @@
|
|||
return wrapper
|
||||
```
|
||||
|
||||
> 说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。
|
||||
> **说明**:由于对带装饰功能的函数添加了@wraps装饰器,可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。
|
||||
|
||||
例子:用装饰器来实现单例模式。
|
||||
|
||||
|
@ -547,22 +566,24 @@
|
|||
|
||||
|
||||
@singleton
|
||||
class President():
|
||||
class President:
|
||||
"""总统(单例类)"""
|
||||
pass
|
||||
```
|
||||
|
||||
> 说明:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?
|
||||
> **提示**:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?
|
||||
|
||||
线程安全的单例装饰器。
|
||||
|
||||
```Python
|
||||
from functools import wraps
|
||||
from threading import Lock
|
||||
from threading import RLock
|
||||
|
||||
|
||||
def singleton(cls):
|
||||
"""线程安全的单例装饰器"""
|
||||
instances = {}
|
||||
locker = Lock()
|
||||
locker = RLock()
|
||||
|
||||
@wraps(cls)
|
||||
def wrapper(*args, **kwargs):
|
||||
|
@ -575,9 +596,11 @@
|
|||
return wrapper
|
||||
```
|
||||
|
||||
3. 面向对象相关知识
|
||||
> **提示**:上面的代码用到了`with`上下文语法来进行锁操作,因为锁对象本身就是上下文管理器对象(支持`__enter__`和`__exit__`魔术方法)。在`wrapper`函数中,我们先做了一次不带锁的检查,然后再做带锁的检查,这样做比直接加锁检查性能要更好,如果对象已经创建就没有必须再去加锁而是直接返回该对象就可以了。
|
||||
|
||||
- 三大支柱:封装、继承、多态
|
||||
### 面向对象相关知识
|
||||
|
||||
- 三大支柱:封装、继承、多态
|
||||
|
||||
例子:工资结算系统。
|
||||
|
||||
|
@ -629,21 +652,15 @@
|
|||
return 1800.0 + self.sales * 0.05
|
||||
|
||||
|
||||
class EmployeeFactory():
|
||||
class EmployeeFactory:
|
||||
"""创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)"""
|
||||
|
||||
@staticmethod
|
||||
def create(emp_type, *args, **kwargs):
|
||||
"""创建员工"""
|
||||
emp_type = emp_type.upper()
|
||||
emp = None
|
||||
if emp_type == 'M':
|
||||
emp = Manager(*args, **kwargs)
|
||||
elif emp_type == 'P':
|
||||
emp = Programmer(*args, **kwargs)
|
||||
elif emp_type == 'S':
|
||||
emp = Salesman(*args, **kwargs)
|
||||
return emp
|
||||
all_emp_types = {'M': Manager, 'P': Programmer, 'S': Salesman}
|
||||
cls = all_emp_types[emp_type.upper()]
|
||||
return cls(*args, **kwargs) if cls else None
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -655,14 +672,14 @@
|
|||
EmployeeFactory.create('S', '典韦', 123000),
|
||||
]
|
||||
for emp in emps:
|
||||
print('%s: %.2f元' % (emp.name, emp.get_salary()))
|
||||
print(f'{emp.name}: {emp.get_salary():.2f}元')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
- 类与类之间的关系
|
||||
- 类与类之间的关系
|
||||
|
||||
- is-a关系:继承
|
||||
- has-a关系:关联 / 聚合 / 合成
|
||||
|
@ -699,12 +716,9 @@
|
|||
|
||||
def show(self):
|
||||
"""显示牌面"""
|
||||
suites = ['♠️', '♥️', '♣️', '♦️']
|
||||
suites = ['♠︎', '♥︎', '♣︎', '♦︎']
|
||||
faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
|
||||
return f'{suites[self.suite.value]} {faces[self.face]}'
|
||||
|
||||
def __str__(self):
|
||||
return self.show()
|
||||
return f'{suites[self.suite.value]}{faces[self.face]}'
|
||||
|
||||
def __repr__(self):
|
||||
return self.show()
|
||||
|
@ -769,11 +783,11 @@
|
|||
main()
|
||||
```
|
||||
|
||||
> 说明:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。
|
||||
> **说明**:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。
|
||||
|
||||
- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)
|
||||
- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)
|
||||
|
||||
- 垃圾回收、循环引用和弱引用
|
||||
- 垃圾回收、循环引用和弱引用
|
||||
|
||||
Python使用了自动化内存管理,这种管理机制以**引用计数**为基础,同时也引入了**标记-清除**和**分代收集**两种机制为辅的策略。
|
||||
|
||||
|
@ -826,28 +840,28 @@
|
|||
以下情况会导致垃圾回收:
|
||||
|
||||
- 调用`gc.collect()`
|
||||
- gc模块的计数器达到阀值
|
||||
- `gc`模块的计数器达到阀值
|
||||
- 程序退出
|
||||
|
||||
如果循环引用中两个对象都定义了`__del__`方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。
|
||||
如果循环引用中两个对象都定义了`__del__`方法,`gc`模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。
|
||||
|
||||
也可以通过`weakref`模块构造弱引用的方式来解决循环引用的问题。
|
||||
|
||||
- 魔法属性和方法(请参考《Python魔法方法指南》)
|
||||
- 魔法属性和方法(请参考《Python魔法方法指南》)
|
||||
|
||||
有几个小问题请大家思考:
|
||||
|
||||
- 自定义的对象能不能使用运算符做运算?
|
||||
- 自定义的对象能不能放到set中?能去重吗?
|
||||
- 自定义的对象能不能作为dict的键?
|
||||
- 自定义的对象能不能放到`set`中?能去重吗?
|
||||
- 自定义的对象能不能作为`dict`的键?
|
||||
- 自定义的对象能不能使用上下文语法?
|
||||
|
||||
- 混入(Mixin)
|
||||
- 混入(Mixin)
|
||||
|
||||
例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。
|
||||
|
||||
```Python
|
||||
class SetOnceMappingMixin():
|
||||
class SetOnceMappingMixin:
|
||||
"""自定义混入类"""
|
||||
__slots__ = ()
|
||||
|
||||
|
@ -871,7 +885,9 @@
|
|||
print(my_dict)
|
||||
```
|
||||
|
||||
- 元编程和元类
|
||||
- 元编程和元类
|
||||
|
||||
对象是通过类创建的,类是通过元类创建的,元类提供了创建类的元信息。所有的类都直接或间接的继承自`object`,所有的元类都直接或间接的继承自`type`。
|
||||
|
||||
例子:用元类实现单例模式。
|
||||
|
||||
|
@ -884,7 +900,7 @@
|
|||
|
||||
def __init__(cls, *args, **kwargs):
|
||||
cls.__instance = None
|
||||
cls.__lock = threading.Lock()
|
||||
cls.__lock = threading.RLock()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
|
@ -897,10 +913,11 @@
|
|||
|
||||
class President(metaclass=SingletonMeta):
|
||||
"""总统(单例类)"""
|
||||
|
||||
pass
|
||||
```
|
||||
|
||||
- 面向对象设计原则
|
||||
- 面向对象设计原则
|
||||
|
||||
- 单一职责原则 (**S**RP)- 一个类只做该做的事情(类的设计要高内聚)
|
||||
- 开闭原则 (**O**CP)- 软件实体应该对扩展开发对修改关闭
|
||||
|
@ -910,19 +927,19 @@
|
|||
- 合成聚合复用原则(CARP) - 优先使用强关联关系而不是继承关系复用代码
|
||||
- 最少知识原则(迪米特法则,Lo**D**)- 不要给没有必然联系的对象发消息
|
||||
|
||||
> 说明:上面加粗的字母放在一起称为面向对象的**SOLID**原则。
|
||||
> **说明**:上面加粗的字母放在一起称为面向对象的**SOLID**原则。
|
||||
|
||||
- GoF设计模式
|
||||
- GoF设计模式
|
||||
|
||||
- 创建型模式:单例、工厂、建造者、原型
|
||||
- 结构型模式:适配器、门面(外观)、代理
|
||||
- 行为型模式:迭代器、观察者、状态、策略
|
||||
|
||||
例子:可插拔的哈希算法。
|
||||
例子:可插拔的哈希算法(策略模式)。
|
||||
|
||||
```Python
|
||||
class StreamHasher():
|
||||
"""哈希摘要生成器(策略模式)"""
|
||||
"""哈希摘要生成器"""
|
||||
|
||||
def __init__(self, alg='md5', size=4096):
|
||||
self.size = size
|
||||
|
@ -941,10 +958,10 @@
|
|||
def main():
|
||||
"""主函数"""
|
||||
hasher1 = StreamHasher()
|
||||
with open('Python-3.7.1.tgz', 'rb') as stream:
|
||||
with open('Python-3.7.6.tgz', 'rb') as stream:
|
||||
print(hasher1.to_digest(stream))
|
||||
hasher2 = StreamHasher('sha1')
|
||||
with open('Python-3.7.1.tgz', 'rb') as stream:
|
||||
with open('Python-3.7.6.tgz', 'rb') as stream:
|
||||
print(hasher2(stream))
|
||||
|
||||
|
||||
|
@ -952,21 +969,15 @@
|
|||
main()
|
||||
```
|
||||
|
||||
4. 迭代器和生成器
|
||||
### 迭代器和生成器
|
||||
|
||||
- 和迭代器相关的魔术方法(`__iter__`和`__next__`)
|
||||
- 迭代器是实现了迭代器协议的对象。
|
||||
|
||||
- 两种创建生成器的方式(生成器表达式和`yield`关键字)
|
||||
- Python中没有像`protocol`或`interface`这样的定义协议的关键字。
|
||||
- Python中用魔术方法表示协议。
|
||||
- `__iter__`和`__next__`魔术方法就是迭代器协议。
|
||||
|
||||
```Python
|
||||
def fib(num):
|
||||
"""生成器"""
|
||||
a, b = 0, 1
|
||||
for _ in range(num):
|
||||
a, b = b, a + b
|
||||
yield a
|
||||
|
||||
|
||||
class Fib(object):
|
||||
"""迭代器"""
|
||||
|
||||
|
@ -986,11 +997,44 @@
|
|||
raise StopIteration()
|
||||
```
|
||||
|
||||
5. 并发编程
|
||||
- 生成器是语法简化版的迭代器。
|
||||
|
||||
Python中实现并发编程的三种方案:多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验;坏处在于并发的程序不容易开发和调试,同时对其他程序来说它并不友好。
|
||||
```Python
|
||||
def fib(num):
|
||||
"""生成器"""
|
||||
a, b = 0, 1
|
||||
for _ in range(num):
|
||||
a, b = b, a + b
|
||||
yield a
|
||||
```
|
||||
|
||||
- 多线程:Python中提供了Thread类并辅以Lock、Condition、Event、Semaphore和Barrier。Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。
|
||||
- 生成器进化为协程。
|
||||
|
||||
生成器对象可以使用`send()`方法发送数据,发送的数据会成为生成器函数中通过`yield`表达式获得的值。这样,生成器就可以作为协程使用,协程简单的说就是可以相互协作的子程序。
|
||||
|
||||
```Python
|
||||
def calc_avg():
|
||||
"""流式计算平均值"""
|
||||
total, counter = 0, 0
|
||||
avg_value = None
|
||||
while True:
|
||||
value = yield avg_value
|
||||
total, counter = total + value, counter + 1
|
||||
avg_value = total / counter
|
||||
|
||||
|
||||
gen = calc_avg()
|
||||
next(gen)
|
||||
print(gen.send(10))
|
||||
print(gen.send(20))
|
||||
print(gen.send(30))
|
||||
```
|
||||
|
||||
### 并发编程
|
||||
|
||||
Python中实现并发编程的三种方案:多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验;坏处在于并发的程序不容易开发和调试,同时对其他程序来说它并不友好。
|
||||
|
||||
- 多线程:Python中提供了`Thread`类并辅以`Lock`、`Condition`、`Event`、`Semaphore`和`Barrier`。Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1037,7 +1081,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
多个线程竞争资源的情况
|
||||
多个线程竞争资源的情况。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1107,7 +1151,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用`threading`模块的Condition来实现线程调度,该对象也是基于锁来创建的,代码如下所示:
|
||||
修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用`threading`模块的`Condition`来实现线程调度,该对象也是基于锁来创建的,代码如下所示:
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1178,7 +1222,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
- 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是Process,其他辅助的类跟threading模块中的类似,进程间共享数据可以使用管道、套接字等,在multiprocessing模块中有一个Queue类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。
|
||||
- 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是`Process`,其他辅助的类跟`threading`模块中的类似,进程间共享数据可以使用管道、套接字等,在`multiprocessing`模块中有一个`Queue`类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1235,7 +1279,7 @@
|
|||
main()
|
||||
```
|
||||
|
||||
> 说明:**多线程和多进程的比较**。
|
||||
> **重点**:**多线程和多进程的比较**。
|
||||
>
|
||||
> 以下情况需要使用多线程:
|
||||
>
|
||||
|
@ -1248,7 +1292,7 @@
|
|||
> 2. 程序的输入可以并行的分成块,并且可以将运算结果合并。
|
||||
> 3. 程序在内存使用方面没有任何限制且不强依赖于I/O操作(如:读写文件、套接字等)。
|
||||
|
||||
- 异步处理:从调度程序的任务队列中挑选任务,该调度程序以交叉的形式执行这些任务,我们并不能保证任务将以某种顺序去执行,因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现,由于执行时间和顺序的不确定,因此需要通过回调式编程或者`future`对象来获取任务执行的结果。Python 3通过`asyncio`模块和`await`和`async`关键字(在Python 3.7中正式被列为关键字)来支持异步处理。
|
||||
- 异步处理:从调度程序的任务队列中挑选任务,该调度程序以交叉的形式执行这些任务,我们并不能保证任务将以某种顺序去执行,因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现,由于执行时间和顺序的不确定,因此需要通过回调式编程或者`future`对象来获取任务执行的结果。Python 3通过`asyncio`模块和`await`和`async`关键字(在Python 3.7中正式被列为关键字)来支持异步处理。
|
||||
|
||||
```Python
|
||||
"""
|
||||
|
@ -1303,9 +1347,9 @@
|
|||
main()
|
||||
```
|
||||
|
||||
> 说明:上面的代码使用`get_event_loop`函数获得系统默认的事件循环,通过`gather`函数可以获得一个`future`对象,`future`对象的`add_done_callback`可以添加执行完成时的回调函数,`loop`对象的`run_until_complete`方法可以等待通过`future`对象获得协程执行结果。
|
||||
> **说明**:上面的代码使用`get_event_loop`函数获得系统默认的事件循环,通过`gather`函数可以获得一个`future`对象,`future`对象的`add_done_callback`可以添加执行完成时的回调函数,`loop`对象的`run_until_complete`方法可以等待通过`future`对象获得协程执行结果。
|
||||
|
||||
Python中有一个名为`aiohttp`的三方库,它提供了异步的HTTP客户端和服务器,这个三方库可以跟`asyncio`模块一起工作,并提供了对`Future`对象的支持。Python 3.6中引入了async和await来定义异步执行的函数以及创建异步上下文,在Python 3.7中它们正式成为了关键字。下面的代码异步的从5个URL中获取页面并通过正则表达式的命名捕获组提取了网站的标题。
|
||||
Python中有一个名为`aiohttp`的三方库,它提供了异步的HTTP客户端和服务器,这个三方库可以跟`asyncio`模块一起工作,并提供了对`Future`对象的支持。Python 3.6中引入了`async`和`await`来定义异步执行的函数以及创建异步上下文,在Python 3.7中它们正式成为了关键字。下面的代码异步的从5个URL中获取页面并通过正则表达式的命名捕获组提取了网站的标题。
|
||||
|
||||
```Python
|
||||
import asyncio
|
||||
|
@ -1334,8 +1378,8 @@
|
|||
'https://www.taobao.com/',
|
||||
'https://www.douban.com/')
|
||||
loop = asyncio.get_event_loop()
|
||||
tasks = [show_title(url) for url in urls]
|
||||
loop.run_until_complete(asyncio.wait(tasks))
|
||||
cos = [show_title(url) for url in urls]
|
||||
loop.run_until_complete(asyncio.wait(cos))
|
||||
loop.close()
|
||||
|
||||
|
||||
|
@ -1343,10 +1387,10 @@
|
|||
main()
|
||||
```
|
||||
|
||||
> 说明:**异步I/O与多进程的比较**。
|
||||
> **重点**:**异步I/O与多进程的比较**。
|
||||
>
|
||||
> 当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,asyncio就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑asyncio,它很适合编写没有实时数据处理需求的Web应用服务器。
|
||||
> 当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,`asyncio`就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑`asyncio`,它很适合编写没有实时数据处理需求的Web应用服务器。
|
||||
|
||||
Python还有很多用于处理并行任务的三方库,例如:joblib、PyMP等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。
|
||||
Python还有很多用于处理并行任务的三方库,例如:`joblib`、`PyMP`等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。
|
||||
|
||||
要实现任务的异步化,可以使用名为Celery的三方库。Celery是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。
|
||||
要实现任务的异步化,可以使用名为`Celery`的三方库。`Celery`是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 74 KiB |
|
@ -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
|
||||
|
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 487 KiB After Width: | Height: | Size: 336 KiB |
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 246 KiB |
Before Width: | Height: | Size: 794 KiB |
Before Width: | Height: | Size: 632 KiB |
Before Width: | Height: | Size: 388 KiB |
Before Width: | Height: | Size: 244 KiB |
Before Width: | Height: | Size: 175 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 209 KiB After Width: | Height: | Size: 137 KiB |
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 13 KiB |
|
@ -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** - 查看待执行的任务队列。
|
||||
|
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 269 KiB |
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 147 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 318 KiB |
Before Width: | Height: | Size: 426 KiB After Width: | Height: | Size: 195 KiB |
Before Width: | Height: | Size: 353 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 747 KiB After Width: | Height: | Size: 368 KiB |
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 153 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 224 KiB After Width: | Height: | Size: 195 KiB |
Before Width: | Height: | Size: 411 KiB After Width: | Height: | Size: 352 KiB |