+
+
+
+#### Python编年史
+
+下面是 Python 语言发展过程中的一些重要时间点:
+
+1. 1989年12月:吉多·范罗苏姆决心开发一个新的脚本语言及其解释器来打发无聊的圣诞节,新语言将作为 ABC 语言的继承者,主要用来替代 Unix shell 和 C 语言实现系统管理。由于吉多本人是 BBC 电视剧《*Monty Python's Flying Circus*》的忠实粉丝,所以他选择了 Python 这个词作为新语言的名字。
+2. 1991年02月:吉多·范罗苏姆在 alt.sources 新闻组上发布了 Python 解释器的最初代码,标记为版本0.9.0。
+3. 1994年01月:Python 1.0发布,梦开始的地方。
+4. 2000年10月:Python 2.0发布,Python 的整个开发过程更加透明,生态圈开始慢慢形成。
+5. 2008年12月:Python 3.0发布,引入了诸多现代编程语言的新特性,但并不完全向下兼容。
+6. 2011年04月:pip 首次发布,Python 语言有了自己的包管理工具。
+7. 2018年07月:吉多·范罗苏姆宣布从“终身仁慈独裁者”(开源项目社区出现争议时拥有最终决定权的人)的职位上“永久休假”。
+8. 2020年01月:在 Python 2和 Python 3共存了11年之后,官方停止了对 Python 2的更新和维护,希望用户尽快切换到 Python 3。
+9. 目前:Python 在大模型(GPT-3、GPT-4、BERT等)、计算机视觉(图像识别、目标检测、图像生成等)、智能推荐(YouTube、Netflix、字节跳动等)、自动驾驶(Waymo、Apollo等)、语音识别、数据科学、量化交易、自动化测试、自动化运维等领域都得到了广泛的应用,Python 语言的生态圈也是相当繁荣。
+
+> **说明**:大多数软件的版本号一般分为三段,形如A.B.C,其中A表示大版本号,当软件整体重写升级或出现不向后兼容的改变时,才会增加A;B表示功能更新,出现新功能时增加B;C表示小的改动(例如:修复了某个Bug),只要有修改就增加C。
+
+#### Python优缺点
+
+Python 语言的优点很多,简单为大家列出几点。
+
+1. **简单优雅**,跟其他很多编程语言相比,Python **更容易上手**。
+2. 能用更少的代码做更多的事情,**提升开发效率**。
+3. 开放源代码,拥有**强大的社区和生态圈**。
+4. **能够做的事情非常多**,有极强的适应性。
+5. **胶水语言**,能够黏合其他语言开发的东西。
+6. 解释型语言,更容易**跨平台**,能够在多种操作系统上运行。
+
+Python 最主要的缺点是**执行效率低**(解释型语言的通病),如果更看重代码的执行效率,C、C++ 或 Go 可能是你更好的选择。
+
+### 安装Python环境
+
+工欲善其事,必先利其器。想要开始你的 Python 编程之旅,首先得在计算机上安装 Python 环境,简单的说就是安装运行 Python 程序需要的 Python 解释器。我们推荐大家安装官方的 Python 3 解释器,它是用 C 语言编写的,我们通常也称之为 CPython,它可能是你目前最好的选择。首先,我们需要从官方网站的[下载页面](https://www.python.org/downloads/)找到下载链接,点击“Download”按钮进入下载页面后,需要根据自己的操作系统选择合适的 Python 3安装程序,如下图所示。
+
+
+
+进入下载页面后,有些 Python 版本并没有提供 Windows 和 macOS 系统的安装程序,只提供了源代码的压缩文件,对于熟悉 Linux 系统的小伙伴,我们可以通过源代码构建安装;对于使用 Windows 或 macOS 系统的小伙伴,我们还是**强烈建议**使用安装程序。例如,你想安装 Python 3.10,选择 Python 3.10.10 或 Python 3.10.11 就能找到 Windows 或 macOS 系统的安装包,而其他版本可能只有源代码,如下图所示。
+
+
+
+#### Windows环境
+
+下面我们以 Windows 11为例,讲解如何在 Windows 操作系统上安装 Python 环境。双击运行从官网下载的安装程序,会打开一个安装向导,如下图所示。
+
+
+
+首先,一定要记得勾选“Add python.exe to PATH”选项,它会帮助我们将 Python 解释器添加到 Windows 系统的 PATH 环境变量中(不理解没关系,勾上就对了);其次,“Use admin privileges when installing py.exe”是为了在安装过程中获得管理员权限,建议勾选。然后,我们选择“Customize Installation”,使用自定义安装的模式,这是专业人士的选择,而你就(假装)是那个专业人士,不建议使用“Install Now”(默认安装)。
+
+接下来,安装向导会提示你勾选需要的“Optional Features”(可选特性),这里咱们可以直接全选。值得一提的是其中的第2项,它是 Python 的包管理工具 pip,可以帮助我们安装三方库和三方工具,所以一定要记得勾选它,然后点击“Next”进入下一环节。
+
+
+
+接下来是对“Advanced Options”(高级选项)的选择,这里我们建议大家只勾选“Add Python to environment variables”和“Precompile standard library”这两个选项,前者会帮助我们自动配置好环境变量,后者会预编译标准库(生成`.pyc`文件),这样在使用时就无需临时编译了。还是那句话,不理解没关系,勾上就对了。下面的“Customize install location”(自定义安装路径)**强烈建议**修改为自定义的路径,这个路径中不应该包含中文、空格或其他特殊字符,注意这一点会为你将来减少很多不必要的麻烦。设置完成后,点击“Install”开始安装。
+
+
+
+安装成功会出现如下图所示的画面,安装成功的关键词是“successful”,如果安装失败,这里的单词会变成“failed”。
+
+
+
+安装完成后可以打开 Windows 的“命令行提示符”或 PowerShell,然后输入`python --version`或`python -V`来检查安装是否成功,这个命令是查看 Python 解释器的版本号。如果看到如下所示的画面,那么恭喜你,Python 环境已经安装成功了。这里我们建议再检查一下 Python 的包管理工具 pip 是否可用,对应的命令是`pip --version`或`pip -V`。
+
+
+
+> **说明**:如果安装过程报错或提示安装失败,很有可能是你的 Windows 系统缺失了一些动态链接库文件或缺少必要的构建工具导致的。可以在[微软官网](https://visualstudio.microsoft.com/zh-hans/downloads/)下载“Visual Studio 2022 生成工具”进行修复,如下图所示。如果不方便在微软官网下载的,也可以使用下面的百度云盘链接来获取修复工具,链接: https://pan.baidu.com/s/1iNDnU5UVdDX5sKFqsiDg5Q 提取码: cjs3。
+>
+>
+>
+> 上面下载的“Visual Studio 2022 生成工具”需要联网才能运行,运行后会出现如下图所示的画面,大家可以参考下图勾选对应的选项进行修复。修复过程需要联网下载对应的软件包,这个过程可能会比较耗时间,修复成功后可能会要求重启你的操作系统。
+>
+>
+
+#### macOS环境
+
+macOS 安装 Python 环境相较于 Windows 系统更为简单,我们从官方下载的安装包是一个`pkg`文件,双击运行之后不断的点击“继续”就安装成功了,几乎不用做任何的设置和勾选,如下图所示。
+
+
+
+安装完成后,可以在 macOS 的“终端”工具中输入`python3 --version`命令来检查是否安装成功,注意这里的命令是`python3`不是`python`!!!然后我们再检查一下包管理工具,输入命令`pip3 --version`,如下图所示。
+
+
+
+#### 其他安装方式
+
+有人可能会推荐新手直接安装 [Anaconda](https://www.anaconda.com/download/success),因为 Anaconda 会帮助我们安装 Python 解释器以及一些常用的三方库,除此之外还提供了一些便捷的工具,特别适合萌新小白。我个人并不推荐这种方式,因为在安装 Anaconda 时你会莫名其妙安装了一大堆有用没用的三方库(占用比较多的硬盘空间),然后你的终端或命令提示符会被 Anaconda 篡改(每次启动自动激活虚拟环境),这些并不符合软件设计的**最小惊讶原则**。其他关于 Anaconda 的小毛病此处就不再赘述了,如果你非要使用 Anaconda,推荐安装 Miniconda,它跟 Anaconda 在同一个下载页面。
+
+还有萌新小白经常会听到或说出,“我要写 Python 程序,安装一个 PyCharm 不就可以了吗?”。这里简单科普一下,PyCharm 只是一个辅助写 Python 代码的工具,它本身并不具备运行 Python 代码的能力,运行 Python 代码靠的是我们上面安装的 Python 解释器。当然,有些 PyCharm 版本在创建 Python 项目时,如果检测不到你电脑上的 Python 环境,也会提示你联网下载 Python 解释器。PyCharm 的安装和使用我们放在了下一课。
+
+### 总结
+
+总结一下我们学到的东西:
+
+1. Python 语言很强大,可以做很多的事情,所以值得我们去学习。
+2. 要使用 Python语言,首先得安装 Python 环境,也就是运行 Python 程序所需的 Python 解释器。
+3. Windows 系统可以在命令提示符或 PowerShell 中输入`python --version`检查 Python 环境是否安装成功;macOS 系统可以在终端中输入`python3 --version`进行检查。
diff --git "a/Day01-20/02.\347\254\254\344\270\200\344\270\252Python\347\250\213\345\272\217.md" "b/Day01-20/02.\347\254\254\344\270\200\344\270\252Python\347\250\213\345\272\217.md"
new file mode 100755
index 000000000..c2fade7c9
--- /dev/null
+++ "b/Day01-20/02.\347\254\254\344\270\200\344\270\252Python\347\250\213\345\272\217.md"
@@ -0,0 +1,150 @@
+## 第一个Python程序
+
+在上一课中,我们对 Python 语言的过去现在有了一些了解,我们准备好了运行 Python 程序所需要的解释器环境。相信大家已经迫不及待的想开始自己的 Python 编程之旅了,但是新问题来了,我们应该在什么地方书写 Python 程序,然后又怎么运行它呢?
+
+### 编写代码的工具
+
+下面我们为大家讲解几种可以编写和运行 Python 代码的工具,大家可以根据自己的需求来选择合适的工具。当然,对于初学者,我个人比较推荐使用 PyCharm,因为它不需要太多的配置也非常的强大,对新手还是很友好的。如果你也听说过或者喜欢 PyCharm,可以直接跳过下面对其他工具的介绍,直接快进到讲解 PyCharm 的地方。
+
+#### 默认的交互式环境
+
+我们打开 Windows 的“命令提示符”或“PowerShell”工具,输入`python`然后按下`Enter`键,这个命令会把我们带到一个交互式环境中。所谓交互式环境,就是我们输入一行代码并按下`Enter`键,代码马上会被执行,如果代码有产出结果,那么结果会被显示在窗口中,如下所示。
+
+```Bash
+Python 3.10.10
+Type "help", "copyright", "credits" or "license" for more information.
+>>> 2 * 3
+6
+>>> 2 + 3
+5
+>>>
+```
+
+> **说明**:使用 macOS 系统的用户需要打开“终端”工具,输入`python3`进入交互式环境。
+
+如果希望退出交互式环境,可以在交互式环境中输入`quit()`,如下所示。
+
+```Bash
+>>> quit()
+```
+
+#### 更好的交互式环境 - IPython
+
+上面说的交互式环境用户体验并不怎么好,大家使用一下就能感受到。我们可以用 IPython 来替换掉它,因为 IPython 提供了更为强大的编辑和交互功能。我们可以在命令提示符或终端中使用 Python 的包管理工具`pip`来安装 IPython,如下所示。
+
+```bash
+pip install ipython
+```
+
+> **提示**:在使用上面的命令安装 IPython 之前,可以先通过`pip config set global.index-url https://pypi.doubanio.com/simple`命令或`pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/`将下载源修改为国内的豆瓣镜像或清华镜像,否则下载安装的过程可能会非常的缓慢。
+
+接下来可以使用下面的命令启动 IPython,进入交互式环境。
+
+```bash
+ipython
+```
+
+> **说明**:还有一个网页版的 IPython 名叫 Jupyter,我们在用得着它的地方再为大家介绍。
+
+#### 文本编辑神器 - Visual Studio Code
+
+Visual Studio Code 是由微软开发能够在 Windows、 Linux 和 macOS 等操作系统上运行的代码编辑神器。它支持语法高亮、自动补全、多点编辑、运行调试等一系列便捷功能,而且能够支持多种编程语言。如果大家要选择一款高级文本编辑工具,强烈推荐 Visual Studio Code,关于它的[下载](https://code.visualstudio.com/)、安装和使用,有兴趣的读者可以自行研究。
+
+
+
+#### 集成开发环境 - PyCharm
+
+如果用 Python 语言开发商业项目,我们推荐大家使用更为专业的工具 PyCharm。PyCharm 是由捷克一家名为 [JetBrains](https://www.jetbrains.com/) 的公司针对 Python 语言提供的集成开发环境(IDE)。所谓集成开发环境,通常是指提供了编写代码、运行代码、调试代码、分析代码、版本控制等一系列强大功能和便捷操作的开发工具,因此特别适合用于商业项目的开发。我们可以在 JetBrains 公司的官方网站上找到 PyCharm 的[下载链接](
+
+官方提供了两个 PyCharm 的版本,一个是免费的社区版(Community Edition),功能相对弱小,但对于初学者来说是完全够用的;另一个是付费的专业版(Professional Edition),功能非常强大,但需要按年或按月支付费用,新用户可以免费试用30天时间。PyCharm 的安装没有任何难度,运行下载的安装程序,几乎全部使用默认设置进行安装就可以了。对于使用 Windows 系统的小伙伴,其中有一个步骤可以按照下图所示勾选“创建桌面快捷方式”和“在右键菜单中添加"Open Folder as Project"”就可以了。
+
+
+
+第一次运行 PyCharm 时,在提示你导入 PyCharm 设置的界面上直接选择“Do not import settings”,然后我们就可以看到如下图所示的欢迎界面。此处,我们可以先点击“Customize”选项对 PyCharm 做一些个性化的设置。
+
+
+
+接下来,我们可以在“Projects”选项中点击“New Project”来创建一个新的项目,此处还可以“打开已有项目”或“从版本控制服务器(VCS)获取项目”,如下图所示。
+
+
+
+创建项目的时候需要指定项目的路径并创建”虚拟环境“,我们建议每个 Python 都在自己专属的虚拟环境中运行。如果你的系统上还没 Python 环境,那么 PyCharm 会提供官网的下载链接,当你点击“Create”按钮创建项目时,它会联网下载 Python 解释器,如下图所示。
+
+
+
+当然,我们并不推荐这么做,因为我们在上一课已经安装过 Python 环境了。在系统有 Python 环境的情况下,PyCharm 通常会自动发现 Python 解释器的位置并以此为基础创建虚拟环境,所以大家看到的画面应该如下图所示。
+
+
+
+> **说明**:上面的截图来自于 Windows 系统,如果使用 macOS 系统,你看到的项目路径和 Python 解释器路径会跟上面有所不同。
+
+创建好项目后会出现如下图所示的画面,我们可以通过在项目文件夹上点击鼠标右键,选择“New”菜单下的“Python File”来创建一个 Python 文件,在给文件命名时建议使用英文字母和下划线的组合,创建好的 Python 文件会自动打开,进入可编辑的状态。
+
+
+
+接下来,我们可以在代码窗口编写我们的 Python 代码。写好代码后,可以在窗口中点击鼠标右键,选择“Run”菜单项来运行代码,下面的“Run”窗口会显示代码的执行结果,如下图所示。
+
+
+
+到这里,我们的第一个 Python 程序已经运转起来了,很酷吧!对了,PyCharm 有一个叫“每日小贴士”的弹窗,会教给你一些使用 PyCharm 的小技巧,如下图所示。如果不需要,直接关闭就可以了;如果不希望它再次出现,在关闭前可以勾选“Don't show tips on startup”。
+
+
+
+### 你好世界
+
+按照行业惯例,我们学习任何一门编程语言写的第一个程序都是输出`hello, world`,因为这段代码是伟大的丹尼斯·里奇(C 语言之父,和肯·汤普森一起开发了 Unix 操作系统)和布莱恩·柯尼汉(awk 语言的发明者)在他们的不朽著作《*The C Programming Language*》中写的第一段代码,下面是对应的 Python 语言的版本。
+
+```python
+print('hello, world')
+```
+
+> **注意**:上面代码中的圆括号、单引号都是在英文输入法状态下输入的,如果不小心写成了中文的圆括号或单引号,运行代码时会出现`SyntaxError: invalid character '(' (U+FF08)`或`SyntaxError: invalid character '‘' (U+2018)`这样的错误提示。
+
+上面的代码只有一个语句,在这个语句中,我们用到了一个名为`print`的函数,它可以帮助我们输出指定的内容;`print`函数圆括号中的`'hello, world'`是一个字符串,它代表了一段文本内容;在 Python 语言中,我们可以用单引号或双引号来表示一个字符串。不同于 C、C++ 或 Java 这样的编程语言,Python 代码中的语句不需要用分号来表示结束,也就是说,如果我们想再写一条语句,只需要回车换行即可,代码如下所示。此外,Python 代码也不需要通过编写名为`main`的入口函数来使其运行,提供入口函数是编写可执行的 C、C++ 或 Java 代码必须要做的事情,这一点很多程序员都不陌生,但是在 Python 语言中它并不是必要的。
+
+```python
+print('hello, world')
+print('goodbye, world')
+```
+
+如果不使用 PyCharm 这样的集成开发环境,我们也可以直接调用 Python 解释器来运行 Python 程序。我们可以将上面的代码保存成一个名为`example01.py`的文件,对于Windows 系统,我们假设该文件在`C:\code`目录下,我们打开“命令提示符”或“PowerShell”并输入下面的命令就可以运行它。
+
+```powershell
+python C:\code\example01.py
+```
+
+对于 macOS 系统,假设我们的文件在`/Users/Hao`目录下,那么可以在终端中输入下面的命令来运行程序。
+
+```Bash
+python3 /Users/Hao/example01.py
+```
+
+> **提示**:如果路径比较长,不愿意手动输入,我们可以通过拖拽的方式将文件直接拖到“命令提示符”或“终端”中,这样会自动输入完整的文件路径。
+
+大家可以试着修改上面的代码,比如将单引号中的`hello, world`换成其他内容或者多写几个这样的语句,看看会运行出怎样的结果。需要提醒大家的是,写 Python 代码时,最好每一行只写一条语句。虽然,我们可以使用`;`作为分隔将多个语句写在一行中,但是这样做会让代码变得非常难看,不再具备良好的可读性。
+
+### 注释你的代码
+
+注释是编程语言的一个重要组成部分,用于在代码中解释代码的作用,从而达到增强代码可读性的目标。当然,我们也可以将代码中暂时不需要运行的代码段通过添加注释来去掉,这样当你需要重新使用这些代码的时候,去掉注释符号就可以了。简单的说,**注释会让代码更容易看懂但不会影响代码的执行结果**。
+
+Python 中有两种形式的注释:
+
+1. 单行注释:以`#`和空格开头,可以注释掉从`#`开始后面一整行的内容。
+2. 多行注释:三个引号(通常用双引号)开头,三个引号结尾,通常用于添加多行说明性内容。
+
+```python
+"""
+第一个Python程序 - hello, world
+
+Version: 1.0
+Author: 骆昊
+"""
+# print('hello, world')
+print("你好,世界!")
+```
+
+### 总结
+
+到此,我们已经把第一个 Python 程序运行起来了,是不是很有成就感?!只要你坚持学习下去,再过一段时间,我们就可以用 Python 语言做更多更酷的事情。今时今日,编程就跟英语一样,对很多人来说都是一项必须要掌握的技能。
diff --git "a/Day01-20/03.Python\350\257\255\350\250\200\344\270\255\347\232\204\345\217\230\351\207\217.md" "b/Day01-20/03.Python\350\257\255\350\250\200\344\270\255\347\232\204\345\217\230\351\207\217.md"
new file mode 100755
index 000000000..02a0267f6
--- /dev/null
+++ "b/Day01-20/03.Python\350\257\255\350\250\200\344\270\255\347\232\204\345\217\230\351\207\217.md"
@@ -0,0 +1,132 @@
+## Python语言中的变量
+
+对于想学习编程的新手来说,有两个问题可能是他们很想知道的,其一是“什么是(计算机)程序”,其二是“写(计算机)程序能做什么”。先说说我对这两个问题的理解:**程序是数据和指令的有序集合**,**写程序就是用数据和指令控制计算机做我们想让它做的事情**。今时今日,为什么有那么多人选择用 Python 语言来写程序,因为 Python 语言足够简单和强大。相较于 C、C++、Java 这样的编程语言,Python 对初学者和非专业人士更加友好,很多问题在 Python 语言中都能找到简单优雅的解决方案。接下来,我们就从最基础的语言元素开始,带大家认识和使用 Python 语言。
+
+### 一些常识
+
+在开始系统的学习 Python 编程之前,我们先来科普一些计算机的基础知识。计算机的硬件系统通常由五大部件构成,包括:**运算器**、**控制器**、**存储器**、**输入设备**和**输出设备**。其中,运算器和控制器放在一起就是我们常说的**中央处理器**(CPU),它的功能是执行各种运算和控制指令。刚才我们提到过,程序是指令的集合,写程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。存储器可以分为**内部存储器**和**外部存储器**,前者就是我们常说的内存,它是中央处理器可以直接寻址的存储空间,程序在执行的过程中,对应的数据和指令需要加载到内存中。输入设备和输出设备经常被统称为 I/O 设备,键盘、鼠标、麦克风、摄像头是典型的输入设备,而显示器、打印机、扬声器等则是典型的输出设备。目前,我们使用的计算机基本大多是遵循“冯·诺依曼体系结构”的计算机,这种计算机有两个关键点:一是**将存储器与中央处理器分开**;二是**将数据以二进制方式编码**。
+
+二进制是一种“逢二进一”的计数法,跟人类使用的“逢十进一”的计数法本质是一样的。人类因为有十根手指,所以使用了十进制计数法,在计数时十根手指用完之后,就只能用进位的方式来表示更大的数值。当然凡事都有例外,玛雅人可能是因为长年光着脚的原因,把脚趾头也都用上了,于是他们使用了二十进制的计数法。基于这样的计数方式,玛雅人使用的历法跟我们平常使用的历法就产生了差异。按照玛雅人的历法,2012 年是上一个所谓的“太阳纪”的最后一年,而 2013 年则是新的“太阳纪”的开始。后来这件事情还被以讹传讹的方式误传为“2012 年是玛雅人预言的世界末日”的荒诞说法。今天有很多人猜测,玛雅文明之所以发展缓慢跟使用了二十进制是有关系的。对于计算机来说,二进制在物理器件上最容易实现的,因为可以用高电压表示 1,用低电压表示 0。不是所有写程序的人都需要熟悉二进制,熟悉十进制与二进制、八进制、十六进制的转换,大多数时候我们即便不了解这些知识也能写程序。但是,我们必须知道,计算机是使用二进制计数的,不管什么样的数据,到了计算机内存中都是以二进制形态存在的。
+
+> **说明**:关于二进制计数法以及它与其他进制如何相互转换,大家可以翻翻名为《计算机导论》或《计算机文化》的书,都能找到相应的知识,此处就不再进行赘述了,不清楚的读者可以自行研究。
+
+### 变量和类型
+
+要想在计算机的内存中保存数据,首先得说一说变量这个概念。在编程语言中,**变量是数据的载体**,简单的说就是一块用来保存数据的内存空间,**变量的值可以被读取和修改**,这是所有运算和控制的基础。计算机能处理的数据有很多种类型,最常见的就是数值,除了数值之外还有文本、图像、音频、视频等各种各样的数据类型。虽然数据在计算机中都是以二进制形态存在的,但是我们可以用不同类型的变量来表示数据类型的差异。Python 语言中预设了多种数据类型,也允许我们自定义新的数据类型,这一点在后面会讲到。我们首先来了解几种 Python 中最为常用的数据类型。
+
+1. 整型(`int`):Python 中可以处理任意大小的整数,而且支持二进制(如`0b100`,换算成十进制是4)、八进制(如`0o100`,换算成十进制是64)、十进制(`100`)和十六进制(`0x100`,换算成十进制是256)的表示法。运行下面的代码,看看会输出什么。
+
+ ```python
+ print(0b100) # 二进制整数
+ print(0o100) # 八进制整数
+ print(100) # 十进制整数
+ print(0x100) # 十六进制整数
+ ```
+
+2. 浮点型(`float`):浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,浮点数除了数学写法(如`123.456`)之外还支持科学计数法(如`1.23456e2`,表示$\small{1.23456 \times 10^{2}}$)。运行下面的代码,看看会输出什么。
+
+ ```python
+ print(123.456) # 数学写法
+ print(1.23456e2) # 科学计数法
+ ```
+
+3. 字符串型(`str`):字符串是以单引号或双引号包裹起来的任意文本,比如`'hello'`和`"hello"`。
+
+4. 布尔型(`bool`):布尔型只有`True`、`False`两种值,要么是`True`,要么是`False`,可以用来表示现实世界中的“是”和“否”,命题的“真”和“假”,状况的“好”与“坏”,水平的“高”与“低”等等。如果一个变量的值只有两种状态,我们就可以使用布尔型。
+
+### 变量命名
+
+对于每个变量,我们都需要给它取一个名字,就如同我们每个人都有自己的名字一样。在 Python 中,变量命名需要遵循以下的规则和惯例。
+
+- 规则部分:
+ - 规则1:变量名由**字母**、**数字**和**下划线**构成,数字不能开头。需要说明的是,这里说的字母指的是 Unicode 字符,Unicode 称为万国码,囊括了世界上大部分的文字系统,这也就意味着中文、日文、希腊字母等都可以作为变量名中的字符,但是一些特殊字符(如:`!`、`@`、`#`等)是不能出现在变量名中的。我们强烈建议大家把这里说的字母理解为**尽可能只使用英文字母**。
+ - 规则2:Python 是**大小写敏感**的编程语言,简单的说就是大写的`A`和小写的`a`是两个不同的变量,这一条其实并不算规则,而是需要大家注意的地方。
+ - 规则3:变量名**不要跟 Python 的关键字重名**,**尽可能避开 Python 的保留字**。这里的关键字是指在 Python 程序中有特殊含义的单词(如:`is`、`if`、`else`、`for`、`while`、`True`、`False`等),保留字主要指 Python 语言内置函数、内置模块等的名字(如:`int`、`print`、`input`、`str`、`math`、`os`等)。
+- 惯例部分:
+ - 惯例1:变量名通常使用**小写英文字母**,**多个单词用下划线进行连接**。
+ - 惯例2:受保护的变量用单个下划线开头。
+ - 惯例3:私有的变量用两个下划线开头。
+
+惯例2和惯例3大家暂时不用管,讲到后面自然会明白的。当然,作为一个专业的程序员,给变量命名时做到**见名知意**也是非常重要,这彰显了一个程序员的专业气质,很多开发岗位的面试也非常看重这一点。
+
+### 变量的使用
+
+下面通过例子来说明变量的类型和变量的使用。
+
+```python
+"""
+使用变量保存数据并进行加减乘除运算
+
+Version: 1.0
+Author: 骆昊
+"""
+a = 45 # 定义变量a,赋值45
+b = 12 # 定义变量b,赋值12
+print(a, b) # 45 12
+print(a + b) # 57
+print(a - b) # 33
+print(a * b) # 540
+print(a / b) # 3.75
+```
+
+在 Python 中可以使用`type`函数对变量的类型进行检查。程序设计中函数的概念跟数学上函数的概念非常类似,数学上的函数相信大家并不陌生,它包括了函数名、自变量和因变量。如果暂时不理解函数这个概念也不要紧,我们会在后续的内容中专门讲解函数的定义和使用。
+
+```python
+"""
+使用type函数检查变量的类型
+
+Version: 1.0
+Author: 骆昊
+"""
+a = 100
+b = 123.45
+c = 'hello, world'
+d = True
+print(type(a)) #
+
+下面,我们用`for-in`循环实现从 1 到 100 的整数求和,即 $\small{\sum_{n=1}^{100}{n}}$ 。
+
+```python
+"""
+从1到100的整数求和
+
+Version: 1.0
+Author: 骆昊
+"""
+total = 0
+for i in range(1, 101):
+ total += i
+print(total)
+```
+
+上面的代码中,变量`total`的作用是保存累加的结果。在循环的过程中,循环变量`i`的值会从 1 一直取到 100。对于变量`i`的每个取值,我们都执行了`total += i`,它相当于`total = total + i`,这条语句实现了累加操作。所以,当循环结束,我们输出变量`total` 的值,它的值就是从 1 累加到 100 的结果 5050。注意,`print(total)`这条语句前是没有缩进的,它不受`for-in`循环的控制,不会重复执行。
+
+我们再来写一个从1到100偶数求和的代码,如下所示。
+
+```python
+"""
+从1到100的偶数求和
+
+Version: 1.0
+Author: 骆昊
+"""
+total = 0
+for i in range(1, 101):
+ if i % 2 == 0:
+ total += i
+print(total)
+```
+
+> **说明**:上面的`for-in`循环中我们使用了分支结构来判断循环变量`i`是不是偶数。
+
+我们也可以修改`range`函数的参数,将起始值和跨度修改为`2`,用更为简单的代码实现从 1 到 100 的偶数求和。
+
+```python
+"""
+从1到100的偶数求和
+
+Version: 1.1
+Author: 骆昊
+"""
+total = 0
+for i in range(2, 101, 2):
+ total += i
+print(total)
+```
+
+当然, 更为简单的办法是使用 Python 内置的`sum`函数求和,这样我们连循环结构都省掉了。
+
+```python
+"""
+从1到100的偶数求和
+
+Version: 1.2
+Author: 骆昊
+"""
+print(sum(range(2, 101, 2)))
+```
+
+### while循环
+
+如果要构造循环结构但是又不能确定循环重复的次数,我们推荐使用`while`循环。`while`循环通过布尔值或能产生布尔值的表达式来控制循环,当布尔值或表达式的值为`True`时,循环体(`while`语句下方保持相同缩进的代码块)中的语句就会被重复执行,当表达式的值为`False`时,结束循环。
+
+下面我们用`while`循环来实现从 1 到 100 的整数求和,代码如下所示。
+
+```python
+"""
+从1到100的整数求和
+
+Version: 1.1
+Author: 骆昊
+"""
+total = 0
+i = 1
+while i <= 100:
+ total += i
+ i += 1
+print(total)
+```
+
+相较于`for-in`循环,上面的代码我们在循环开始前增加了一个变量`i`,我们使用这个变量来控制循环,所以`while`后面给出了`i <= 100`的条件。在`while`的循环体中,我们除了做累加,还需要让变量`i`的值递增,所以我们添加了`i += 1`这条语句,这样`i`的值就会依次取到1、2、3、……,直到 101。当`i`变成 101 时,`while`循环的条件不再成立,代码会离开`while`循环,此时我们输出变量`total`的值,它就是从 1 到 100 求和的结果 5050。
+
+如果要实现从 1 到 100 的偶数求和,我们可以对上面的代码稍作修改。
+
+```python
+"""
+从1到100的偶数求和
+
+Version: 1.3
+Author: 骆昊
+"""
+total = 0
+i = 2
+while i <= 100:
+ total += i
+ i += 2
+print(total)
+```
+
+### break和continue
+
+如果把`while`循环的条件设置为`True`,即让条件恒成立会怎么样呢?我们看看下面的代码,还是使用`while`构造循环结构,计算 1 到 100 的偶数和。
+
+```python
+"""
+从1到100的偶数求和
+
+Version: 1.4
+Author: 骆昊
+"""
+total = 0
+i = 2
+while True:
+ total += i
+ i += 2
+ if i > 100:
+ break
+print(total)
+```
+
+上面的代码中使用`while True`构造了一个条件恒成立的循环,也就意味着如果不做特殊处理,循环是不会结束的,这就是我们常说的“死循环”。为了在`i`的值超过 100 后让循环停下来,我们使用了`break`关键字,它的作用是终止循环结构的执行。需要注意的是,`break`只能终止它所在的那个循环,这一点在使用嵌套循环结构时需要引起注意,后面我们会讲到什么是嵌套的循环结构。除了`break`之外,还有另一个在循环结构中可以使用的关键字`continue`,它可以用来放弃本次循环后续的代码直接让循环进入下一轮,代码如下所示。
+
+```python
+"""
+从1到100的偶数求和
+
+Version: 1.5
+Author: 骆昊
+"""
+total = 0
+for i in range(1, 101):
+ if i % 2 != 0:
+ continue
+ total += i
+print(total)
+```
+
+> **说明**:上面的代码使用`continue`关键字跳过了`i`是奇数的情况,只有在`i`是偶数的前提下,才会执行到`total += i`。
+
+### 嵌套的循环结构
+
+和分支结构一样,循环结构也是可以嵌套的,也就是说在循环结构中还可以构造循环结构。下面的例子演示了如何通过嵌套的循环来输出一个乘法口诀表(九九表)。
+
+```python
+"""
+打印乘法口诀表
+
+Version: 1.0
+Author: 骆昊
+"""
+for i in range(1, 10):
+ for j in range(1, i + 1):
+ print(f'{i}×{j}={i * j}', end='\t')
+ print()
+```
+
+上面的代码中,`for-in`循环的循环体中又用到了`for-in`循环,外面的循环用来控制产生`i`行的输出,而里面的循环则用来控制在一行中输出`j`列。显然,里面的`for-in`循环的输出就是乘法口诀表中的一整行。所以在里面的循环完成时,我们用了一个`print()`来实现换行的效果,让下面的输出重新另起一行,最后的输出如下所示。
+
+```
+1×1=1
+2×1=2 2×2=4
+3×1=3 3×2=6 3×3=9
+4×1=4 4×2=8 4×3=12 4×4=16
+5×1=5 5×2=10 5×3=15 5×4=20 5×5=25
+6×1=6 6×2=12 6×3=18 6×4=24 6×5=30 6×6=36
+7×1=7 7×2=14 7×3=21 7×4=28 7×5=35 7×6=42 7×7=49
+8×1=8 8×2=16 8×3=24 8×4=32 8×5=40 8×6=48 8×7=56 8×8=64
+9×1=9 9×2=18 9×3=27 9×4=36 9×5=45 9×6=54 9×7=63 9×8=72 9×9=81
+```
+
+### 循环结构的应用
+
+#### 例子1:判断素数
+
+要求:输入一个大于 1 的正整数,判断它是不是素数。
+
+> **提示**:素数指的是只能被 1 和自身整除的大于 1 的整数。例如对于正整数 $\small{n}$,我们可以通过在 2 到 $\small{n - 1}$ 之间寻找有没有 $\small{n}$ 的因子,来判断它到底是不是一个素数。当然,循环不用从 2 开始到 $\small{n - 1}$ 结束,因为对于大于 1 的正整数,因子应该都是成对出现的,所以循环到 $\small{\sqrt{n}}$ 就可以结束了。
+
+```python
+"""
+输入一个大于1的正整数判断它是不是素数
+
+Version: 1.0
+Author: 骆昊
+"""
+num = int(input('请输入一个正整数: '))
+end = int(num ** 0.5)
+is_prime = True
+for i in range(2, end + 1):
+ if num % i == 0:
+ is_prime = False
+ break
+if is_prime:
+ print(f'{num}是素数')
+else:
+ print(f'{num}不是素数')
+```
+
+> **说明**:上面的代码中我们用了布尔型的变量`is_prime`,我们先将它赋值为`True`,假设`num`是一个素数;接下来,我们在 2 到`num ** 0.5`的范围寻找`num`的因子,如果找到了`num`的因子,那么它一定不是素数,此时我们将`is_prime`赋值为`False`,同时使用`break`关键字终止循环结构;最后,我们根据`is_prime`的值是`True`还是`False`来给出不同的输出。
+
+#### 例子2:最大公约数
+
+要求:输入两个大于 0 的正整数,求两个数的最大公约数。
+
+> **提示**:两个数的最大公约数是两个数的公共因子中最大的那个数。
+
+```python
+"""
+输入两个正整数求它们的最大公约数
+
+Version: 1.0
+Author: 骆昊
+"""
+x = int(input('x = '))
+y = int(input('y = '))
+for i in range(x, 0, -1):
+ if x % i == 0 and y % i == 0:
+ print(f'最大公约数: {i}')
+ break
+```
+
+> **说明**:上面代码中`for-in`循环的循环变量值是从大到小的,这样我们找到的能够同时整除`x`和`y`的因子`i`,就是`x`和`y`的最大公约数,此时我们用`break`终止循环。如果`x`和`y`互质,那么循环会执行到`i`变成 1,因为 1 是所有正整数的因子,此时`x`和`y`的最大公约数就是 1。
+
+用上面代码的找最大公约数在执行效率是有问题的。假如`x`的值是`999999999998`,`y`的值是`999999999999`,很显然两个数是互质的,最大公约数为 1。但是我们使用上面的代码,循环会重复`999999999998`次,这通常是难以接受的。我们可以使用欧几里得算法来找最大公约数,它能帮我们更快的得到想要的结果,代码如下所示。
+
+```python
+"""
+输入两个正整数求它们的最大公约数
+
+Version: 1.1
+Author: 骆昊
+"""
+x = int(input('x = '))
+y = int(input('y = '))
+while y % x != 0:
+ x, y = y % x, x
+print(f'最大公约数: {x}')
+```
+
+> **说明**:解决问题的方法和步骤可以称之为算法,对于同一个问题,我们可以设计出不同的算法,不同的算法在存储空间的占用和执行效率上都会存在差别,而这些差别就代表了算法的优劣。大家可以对比上面的两段待会,体会一下为什么我们说欧几里得算法是更好的选择。上面的代码中`x, y = y % x, x`语句表示将`y % x`的值赋给`x`,将`x` 原来的值赋给`y`。
+
+#### 例子3:猜数字游戏
+
+要求:计算机出一个 1 到 100 之间的随机数,玩家输入自己猜的数字,计算机给出对应的提示信息“大一点”、“小一点”或“猜对了”,如果玩家猜中了数字,计算机提示用户一共猜了多少次,游戏结束,否则游戏继续。
+
+```python
+"""
+猜数字小游戏
+
+Version: 1.0
+Author: 骆昊
+"""
+import random
+
+answer = random.randrange(1, 101)
+counter = 0
+while True:
+ counter += 1
+ num = int(input('请输入: '))
+ if num < answer:
+ print('大一点.')
+ elif num > answer:
+ print('小一点.')
+ else:
+ print('猜对了.')
+ break
+print(f'你一共猜了{counter}次.')
+```
+
+> **说明**:上面的代码使用`import random`导入了 Python 标准库的`random`模块,该模块的`randrange`函数帮助我们生成了 1 到 100 范围的随机数(不包括 100)。变量`counter`用来记录循环执行的次数,也就是用户一共猜了几次,每循环一次`counter`的值都会加 1。
+
+### 总结
+
+学会了 Python 中的分支结构和循环结构,我们就可以解决很多实际的问题了。通过这节课的学习,大家应该已经知道了可以用`for`和`while`关键字来构造循环结构。**如果事先知道循环结构重复的次数,我们通常使用**`for`**循环**;**如果循环结构的重复次数不能确定,可以用**`while`**循环**。此外,我们可以在循环结构中**使用**`break`**终止循环**,**也可以在循环结构中使用**`continue`**关键字让循环结构直接进入下一轮次**。
diff --git "a/Day01-20/07.\345\210\206\346\224\257\345\222\214\345\276\252\347\216\257\347\273\223\346\236\204\345\256\236\346\210\230.md" "b/Day01-20/07.\345\210\206\346\224\257\345\222\214\345\276\252\347\216\257\347\273\223\346\236\204\345\256\236\346\210\230.md"
new file mode 100755
index 000000000..5c06f09b3
--- /dev/null
+++ "b/Day01-20/07.\345\210\206\346\224\257\345\222\214\345\276\252\347\216\257\347\273\223\346\236\204\345\256\236\346\210\230.md"
@@ -0,0 +1,173 @@
+## 分支和循环结构实战
+
+通过前面两节课的学习,大家对 Python 中的分支结构和循环结构已经有了初步的认知。**分支结构和循环结构是构造程序逻辑的基础**,它们的重要性不言而喻,但是对于初学者来说这也是比较困难的部分。很多人对分支结构和循环结构的语法是能够理解的,但是遇到实际问题的时候又无法下手;**看懂别人的代码很容易,但是要自己写出类似的代码却又很难**。如果你也有同样的问题和困惑,千万不要沮丧,这只是因为你的编程之旅才刚刚开始,**你的练习量还没有达到让你可以随心所欲写出代码的程度**,只要加强编程练习,通过量的积累来产生质的变化,这个问题迟早都会解决的。
+
+### 例子1:100以内的素数
+
+> **说明**:素数指的是只能被 1 和自身整除的正整数(不包括 1),之前我们写过判断素数的代码,这里相当于是一个升级版本。
+
+```python
+"""
+输出100以内的素数
+
+Version: 1.0
+Author: 骆昊
+"""
+for num in range(2, 100):
+ is_prime = True
+ for i in range(2, int(num ** 0.5) + 1):
+ if num % i == 0:
+ is_prime = False
+ break
+ if is_prime:
+ print(num)
+```
+
+### 例子2:斐波那契数列
+
+要求:输出斐波那契数列中的前 20 个数。
+
+> **说明**:斐波那契数列(Fibonacci sequence),通常也被称作黄金分割数列,是意大利数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)在《计算之书》中研究理想假设条件下兔子成长率问题而引入的数列,因此这个数列也常被戏称为“兔子数列”。斐波那契数列的特点是数列的前两个数都是 1,从第三个数开始,每个数都是它前面两个数的和。按照这个规律,斐波那契数列的前 10 个数是:`1, 1, 2, 3, 5, 8, 13, 21, 34, 55`。斐波那契数列在现代物理、准晶体结构、化学等领域都有直接的应用。
+
+```python
+"""
+输出斐波那契数列中的前20个数
+
+Version: 1.0
+Author: 骆昊
+"""
+
+a, b = 0, 1
+for _ in range(20):
+ a, b = b, a + b
+ print(a)
+```
+
+> **说明**:上面循环中的`a, b = b, a + b`表示将变量`b`的值赋给`a`,把`a + b`的值赋给`b`。通过这个递推公式,我们可以依次获得斐波那契数列中的数。
+
+### 例子3:寻找水仙花数
+
+要求:找出 100 到 999 范围内的所有水仙花数。
+
+> **提示**:在数论中,水仙花数(narcissistic number)也被称为超完全数字不变数、自恋数、自幂数、阿姆斯特朗数,它是一个 $\small{N}$ 位非负整数,其各位数字的 $\small{N}$ 次方和刚好等于该数本身,例如: $\small{153 = 1^{3} + 5^{3} + 3^{3}}$ ,所以 153 是一个水仙花数; $\small{1634 = 1^{4} + 6^{4} + 3^{4} + 4^{4}}$ ,所以 1634 也是一个水仙花数。对于三位数,解题的关键是将它拆分为个位、十位、百位,再判断是否满足水仙花数的要求,这一点利用 Python 中的`//`和`%`运算符其实很容易做到。
+
+```python
+"""
+找出100到999范围内的水仙花数
+
+Version: 1.0
+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)
+```
+
+上面利用`//`和`%`拆分一个数的小技巧在写代码的时候还是很常用的。我们要将一个不知道有多少位的正整数进行反转,例如将 12389 变成 98321,也可以利用这两个运算来实现,代码如下所示。
+
+```python
+"""
+正整数的反转
+
+Version: 1.0
+Author: 骆昊
+"""
+num = int(input('num = '))
+reversed_num = 0
+while num > 0:
+ reversed_num = reversed_num * 10 + num % 10
+ num //= 10
+print(reversed_num)
+```
+
+### 例子4:百钱百鸡问题
+
+> **说明**:百钱百鸡是我国古代数学家张丘建在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?翻译成现代文是:公鸡 5 元一只,母鸡 3 元一只,小鸡 1 元三只,用 100 块钱买一百只鸡,问公鸡、母鸡、小鸡各有多少只?
+
+```python
+"""
+百钱百鸡问题
+
+Version: 1.0
+Author: 骆昊
+"""
+for x in range(0, 21):
+ for y in range(0, 34):
+ for z in range(0, 100, 3):
+ if x + y + z == 100 and 5 * x + 3 * y + z // 3 == 100:
+ print(f'公鸡: {x}只, 母鸡: {y}只, 小鸡: {z}只')
+```
+
+上面使用的方法叫做**穷举法**,也称为**暴力搜索法**,这种方法通过一项一项的列举备选解决方案中所有可能的候选项,并检查每个候选项是否符合问题的描述,最终得到问题的解。上面的代码中,我们使用了嵌套的循环结构,假设公鸡有`x`只,显然`x`的取值范围是 0 到 20,假设母鸡有`y`只,它的取值范围是 0 到 33,假设小鸡有`z`只,它的取值范围是 0 到 99 且取值是 3 的倍数。这样,我们设置好 100 只鸡的条件`x + y + z == 100`,设置好 100 块钱的条件`5 * x + 3 * y + z // 3 == 100`,当两个条件同时满足时,就是问题的正确答案,我们用`print`函数输出它。这种方法看起来比较笨拙,但对于运算能力非常强大的计算机来说,通常都是一个可行的甚至是不错的选择,只要问题的解存在就能够找到它。
+
+事实上,上面的代码还有更好的写法,既然我们已经假设公鸡有`x`只,母鸡有`y`只,那么小鸡的数量就应该是`100 - x - y`,这样减少一个条件,我们就可以把上面三层嵌套的`for-in`循环改写为两层嵌套的`for-in`循环。循环次数减少了,代码的执行效率就有了显著的提升,如下所示。
+
+```python
+"""
+百钱百鸡问题
+
+Version: 1.1
+Author: 骆昊
+"""
+for x in range(0, 21):
+ for y in range(0, 34):
+ z = 100 - x - y
+ if z % 3 == 0 and 5 * x + 3 * y + z // 3 == 100:
+ print(f'公鸡: {x}只, 母鸡: {y}只, 小鸡: {z}只')
+```
+
+> **说明**:上面代码中的`z % 3 == 0`是为了确保小鸡的数量是 3 的倍数。
+
+### 例子5:CRAPS赌博游戏
+
+> **说明**:CRAPS又称花旗骰,是美国拉斯维加斯非常受欢迎的一种的桌上赌博游戏。该游戏使用两粒骰子,玩家通过摇两粒骰子获得点数进行游戏。简化后的规则是:玩家第一次摇骰子如果摇出了 7 点或 11 点,玩家胜;玩家第一次如果摇出 2 点、3 点或 12 点,庄家胜;玩家如果摇出其他点数则游戏继续,玩家重新摇骰子,如果玩家摇出了 7 点,庄家胜;如果玩家摇出了第一次摇的点数,玩家胜;其他点数玩家继续摇骰子,直到分出胜负。为了增加代码的趣味性,我们设定游戏开始时玩家有 1000 元的赌注,每局游戏开始之前,玩家先下注,如果玩家获胜就可以获得对应下注金额的奖励,如果庄家获胜,玩家就会输掉自己下注的金额。游戏结束的条件是玩家破产(输光所有的赌注)。
+
+```python
+"""
+Craps赌博游戏
+
+Version: 1.0
+Author: 骆昊
+"""
+import random
+
+money = 1000
+while money > 0:
+ print(f'你的总资产为: {money}元')
+ # 下注金额必须大于0且小于等于玩家的总资产
+ while True:
+ debt = int(input('请下注: '))
+ if 0 < debt <= money:
+ break
+ # 用两个1到6均匀分布的随机数相加模拟摇两颗色子得到的点数
+ first_point = random.randrange(1, 7) + random.randrange(1, 7)
+ print(f'\n玩家摇出了{first_point}点')
+ if first_point == 7 or first_point == 11:
+ print('玩家胜!\n')
+ money += debt
+ elif first_point == 2 or first_point == 3 or first_point == 12:
+ print('庄家胜!\n')
+ money -= debt
+ else:
+ # 如果第一次摇色子没有分出胜负,玩家需要重新摇色子
+ while True:
+ current_point = random.randrange(1, 7) + random.randrange(1, 7)
+ print(f'玩家摇出了{current_point}点')
+ if current_point == 7:
+ print('庄家胜!\n')
+ money -= debt
+ break
+ elif current_point == first_point:
+ print('玩家胜!\n')
+ money += debt
+ break
+print('你破产了, 游戏结束!')
+```
+
+### 总结
+
+分支结构和循环结构都非常重要,是构造程序逻辑的基础,**一定要通过大量的练习来达到融会贯通**。我们可以用上面讲的花旗骰游戏作为一个标准,如果你能够很顺利的完成这段代码,那么分支结构和循环结构的知识你就已经很好的掌握了。
+
diff --git "a/Day01-20/08.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\210\227\350\241\250-1.md" "b/Day01-20/08.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\210\227\350\241\250-1.md"
new file mode 100755
index 000000000..7d7d02551
--- /dev/null
+++ "b/Day01-20/08.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\210\227\350\241\250-1.md"
@@ -0,0 +1,226 @@
+## 常用数据结构之列表-1
+
+在开始本节课的内容之前,我们先给大家一个编程任务,将一颗色子掷 6000 次,统计每种点数出现的次数。这个任务对大家来说应该是非常简单的,我们可以用 1 到 6 均匀分布的随机数来模拟掷色子,然后用 6 个变量分别记录每个点数出现的次数,相信通过前面的学习,大家都能比较顺利的写出下面的代码。
+
+```python
+"""
+将一颗色子掷6000次,统计每种点数出现的次数
+
+Author: 骆昊
+Version: 1.0
+"""
+import random
+
+f1 = 0
+f2 = 0
+f3 = 0
+f4 = 0
+f5 = 0
+f6 = 0
+for _ in range(6000):
+ face = random.randrange(1, 7)
+ if face == 1:
+ f1 += 1
+ elif face == 2:
+ f2 += 1
+ elif face == 3:
+ f3 += 1
+ elif face == 4:
+ f4 += 1
+ elif face == 5:
+ f5 += 1
+ else:
+ f6 += 1
+print(f'1点出现了{f1}次')
+print(f'2点出现了{f2}次')
+print(f'3点出现了{f3}次')
+print(f'4点出现了{f4}次')
+print(f'5点出现了{f5}次')
+print(f'6点出现了{f6}次')
+```
+
+上面的代码非常有多么“丑陋”相信就不用我多说了。当然,更为可怕的是,如果我们要掷两颗或者掷更多的色子,然后统计每种点数出现的次数,那就需要定义更多的变量,写更多的分支结构,大家想想都会感到恶心。讲到这里,相信大家心中已经有一个疑问了:有没有办法用一个变量来保存多个数据,有没有办法用统一的代码对多个数据进行操作?答案是肯定的,在 Python 语言中我们可以通过容器型变量来保存和操作多个数据,我们首先为大家介绍列表(`list`)这种新的数据类型。
+
+### 创建列表
+
+在 Python 中,**列表是由一系元素按特定顺序构成的数据序列**,这就意味着如果我们定义一个列表类型的变量,**可以用它来保存多个数据**。在 Python 中,可以使用`[]`字面量语法来定义列表,列表中的多个元素用逗号进行分隔,代码如下所示。
+
+```python
+items1 = [35, 12, 99, 68, 55, 35, 87]
+items2 = ['Python', 'Java', 'Go', 'Kotlin']
+items3 = [100, 12.3, 'Python', True]
+print(items1) # [35, 12, 99, 68, 55, 35, 87]
+print(items2) # ['Python', 'Java', 'Go', 'Kotlin']
+print(items3) # [100, 12.3, 'Python', True]
+```
+
+> **说明**:列表中可以有重复元素,例如`items1`中的`35`;列表中可以有不同类型的元素,例如`items3`中有`int`类型、`float`类型、`str`类型和`bool`类型的元素,但是我们通常并不建议将不同类型的元素放在同一个列表中,主要是操作起来极为不便。
+
+我们可以使用`type`函数来查看变量的类型,有兴趣的小伙伴可以自行查看上面的变量`items1`到底是什么类型。因为列表可以保存多个元素,它是一种容器型的数据类型,所以我们在给列表类型的变量起名字时,变量名通常用复数形式的单词。
+
+除此以外,还可以通过 Python 内置的`list`函数将其他序列变成列表。准确的说,`list`并不是一个普通的函数,它是创建列表对象的构造器,后面的课程会为大家介绍对象和构造器这些概念。
+
+```python
+items4 = list(range(1, 10))
+items5 = list('hello')
+print(items4) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
+print(items5) # ['h', 'e', 'l', 'l', 'o']
+```
+
+> **说明**:`range(1, 10)`会产生`1`到`9`的整数序列,给到`list`构造器中,会创建出由`1`到`9`的整数构成的列表。字符串是字符构成的序列,上面的`list('hello')`用字符串`hello`的字符作为列表元素,创建了列表对象。
+
+### 列表的运算
+
+我们可以使用`+`运算符实现两个列表的拼接,拼接运算会将两个列表中的元素连接起来放到一个列表中,代码如下所示。
+
+```python
+items5 = [35, 12, 99, 45, 66]
+items6 = [45, 58, 29]
+items7 = ['Python', 'Java', 'JavaScript']
+print(items5 + items6) # [35, 12, 99, 45, 66, 45, 58, 29]
+print(items6 + items7) # [45, 58, 29, 'Python', 'Java', 'JavaScript']
+items5 += items6
+print(items5) # [35, 12, 99, 45, 66, 45, 58, 29]
+```
+
+我们可以使用`*`运算符实现列表的重复运算,`*`运算符会将列表元素重复指定的次数,我们在上面的代码中增加两行,如下所示。
+
+```python
+print(items6 * 3) # [45, 58, 29, 45, 58, 29, 45, 58, 29]
+print(items7 * 2) # ['Python', 'Java', 'JavaScript', 'Python', 'Java', 'JavaScript']
+```
+
+我们可以使用`in`或`not in`运算符判断一个元素在不在列表中,我们在上面的代码代码中再增加两行,如下所示。
+
+```python
+print(29 in items6) # True
+print(99 in items6) # False
+print('C++' not in items7) # True
+print('Python' not in items7) # False
+```
+
+由于列表中有多个元素,而且元素是按照特定顺序放在列表中的,所以当我们想操作列表中的某个元素时,可以使用`[]`运算符,通过在`[]`中指定元素的位置来访问该元素,这种运算称为索引运算。需要说明的是,`[]`的元素位置可以是`0`到`N - 1`的整数,也可以是`-1`到`-N`的整数,分别称为正向索引和反向索引,其中`N`代表列表元素的个数。对于正向索引,`[0]`可以访问列表中的第一个元素,`[N - 1]`可以访问最后一个元素;对于反向索引,`[-1]`可以访问列表中的最后一个元素,`[-N]`可以访问第一个元素,代码如下所示。
+
+```python
+items8 = ['apple', 'waxberry', 'pitaya', 'peach', 'watermelon']
+print(items8[0]) # apple
+print(items8[2]) # pitaya
+print(items8[4]) # watermelon
+items8[2] = 'durian'
+print(items8) # ['apple', 'waxberry', 'durian', 'peach', 'watermelon']
+print(items8[-5]) # 'apple'
+print(items8[-4]) # 'waxberry'
+print(items8[-1]) # watermelon
+items8[-4] = 'strawberry'
+print(items8) # ['apple', 'strawberry', 'durian', 'peach', 'watermelon']
+```
+
+在使用索引运算的时候要避免出现索引越界的情况,对于上面的`items8`,如果我们访问`items8[5]`或`items8[-6]`,就会引发`IndexError`错误,导致程序崩溃,对应的错误信息是:*list index out of range*,翻译成中文就是“数组索引超出范围”。因为对于只有五个元素的列表`items8`,有效的正向索引是`0`到`4`,有效的反向索引是`-1`到`-5`。
+
+如果希望一次性访问列表中的多个元素,我们可以使用切片运算。切片运算是形如`[start:end:stride]`的运算符,其中`start`代表访问列表元素的起始位置,`end`代表访问列表元素的终止位置(终止位置的元素无法访问),而`stride`则代表了跨度,简单的说就是位置的增量,比如我们访问的第一个元素在`start`位置,那么第二个元素就在`start + stride`位置,当然`start + stride`要小于`end`。我们给上面的代码增加下面的语句,来使用切片运算符访问列表元素。
+
+```python
+print(items8[1:3:1]) # ['strawberry', 'durian']
+print(items8[0:3:1]) # ['apple', 'strawberry', 'durian']
+print(items8[0:5:2]) # ['apple', 'durian', 'watermelon']
+print(items8[-4:-2:1]) # ['strawberry', 'durian']
+print(items8[-2:-6:-1]) # ['peach', 'durian', 'strawberry', 'apple']
+```
+
+> **提醒**:大家可以看看上面代码中的最后一行,想一想当跨度为负数时,切片运算是如何访问元素的。
+
+如果`start`值等于`0`,那么在使用切片运算符时可以将其省略;如果`end`值等于`N`,`N`代表列表元素的个数,那么在使用切片运算符时可以将其省略;如果`stride`值等于`1`,那么在使用切片运算符时也可以将其省略。所以,下面的代码跟上面的代码作用完全相同。
+
+```python
+print(items8[1:3]) # ['strawberry', 'durian']
+print(items8[:3:1]) # ['apple', 'strawberry', 'durian']
+print(items8[::2]) # ['apple', 'durian', 'watermelon']
+print(items8[-4:-2]) # ['strawberry', 'durian']
+print(items8[-2::-1]) # ['peach', 'durian', 'strawberry', 'apple']
+```
+
+事实上,我们还可以通过切片操作修改列表中的元素,例如我们给上面的代码再加上一行,大家可以看看这里的输出。
+
+```python
+items8[1:3] = ['x', 'o']
+print(items8) # ['apple', 'x', 'o', 'peach', 'watermelon']
+```
+
+两个列表还可以做关系运算,我们可以比较两个列表是否相等,也可以给两个列表比大小,代码如下所示。
+
+```python
+nums1 = [1, 2, 3, 4]
+nums2 = list(range(1, 5))
+nums3 = [3, 2, 1]
+print(nums1 == nums2) # True
+print(nums1 != nums2) # False
+print(nums1 <= nums3) # True
+print(nums2 >= nums3) # False
+```
+
+> **说明**:上面的`nums1`和`nums2`对应元素完全相同,所以`==`运算的结果是`True`。`nums2`和`nums3`的比较,由于`nums2`的第一个元素`1`小于`nums3`的第一个元素`3`,所以`nums2 >= nums3`比较的结果是`False`。两个列表的关系运算在实际工作并不那么常用,如果实在不理解就跳过吧,不用纠结。
+
+### 元素的遍历
+
+如果想逐个取出列表中的元素,可以使用`for-in`循环的,有以下两种做法。
+
+方法一:在循环结构中通过索引运算,遍历列表元素。
+
+```python
+languages = ['Python', 'Java', 'C++', 'Kotlin']
+for index in range(len(languages)):
+ print(languages[index])
+```
+
+输出:
+
+```
+Python
+Java
+C++
+Kotlin
+```
+
+> **说明**:上面的`len`函数可以获取列表元素的个数`N`,而`range(N)`则构成了从`0`到`N-1`的范围,刚好可以作为列表元素的索引。
+
+方法二:直接对列表做循环,循环变量就是列表元素的代表。
+
+```python
+languages = ['Python', 'Java', 'C++', 'Kotlin']
+for language in languages:
+ print(language)
+```
+
+输出:
+
+```
+Python
+Java
+C++
+Kotlin
+```
+
+### 总结
+
+讲到这里,我们可以用列表的知识来重构上面“掷色子统计每种点数出现次数”的代码。
+
+```python
+"""
+将一颗色子掷6000次,统计每种点数出现的次数
+
+Author: 骆昊
+Version: 1.1
+"""
+import random
+
+counters = [0] * 6
+# 模拟掷色子记录每种点数出现的次数
+for _ in range(6000):
+ face = random.randrange(1, 7)
+ counters[face - 1] += 1
+# 输出每种点数出现的次数
+for face in range(1, 7):
+ print(f'{face}点出现了{counters[face - 1]}次')
+```
+
+上面的代码中,我们用`counters`列表中的六个元素分别表示 1 到 6 点出现的次数,最开始的时候六个元素的值都是 0。接下来,我们用 1 到 6 均匀分布的随机数模拟掷色子,如果摇出 1 点,`counters[0]`的值加 1,如果摇出 2 点,`counters[1]`的值加 1,以此类推。大家感受一下,由于使用了列表类型加上循环结构,我们对数据的处理是批量性的,这就使得修改后的代码比之前的代码要简单优雅得多。
diff --git "a/Day01-20/09.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\210\227\350\241\250-2.md" "b/Day01-20/09.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\210\227\350\241\250-2.md"
new file mode 100755
index 000000000..96c2c96e0
--- /dev/null
+++ "b/Day01-20/09.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\210\227\350\241\250-2.md"
@@ -0,0 +1,328 @@
+## 常用数据结构之列表-2
+
+### 列表的方法
+
+列表类型的变量拥有很多方法可以帮助我们操作一个列表,假设我们有名为`foos`的列表,列表有名为`bar`的方法,那么使用列表方法的语法是:`foos.bar()`,这是一种通过对象引用调用对象方法的语法。后面我们讲面向对象编程的时候,还会对这种语法进行详细的讲解,这种语法也称为给对象发消息。
+
+#### 添加和删除元素
+
+列表是一种可变容器,可变容器指的是我们可以向容器中添加元素、可以从容器移除元素,也可以修改现有容器中的元素。我们可以使用列表的`append`方法向列表中追加元素,使用`insert`方法向列表中插入元素。追加指的是将元素添加到列表的末尾,而插入则是在指定的位置添加新元素,大家可以看看下面的代码。
+
+```python
+languages = ['Python', 'Java', 'C++']
+languages.append('JavaScript')
+print(languages) # ['Python', 'Java', 'C++', 'JavaScript']
+languages.insert(1, 'SQL')
+print(languages) # ['Python', 'SQL', 'Java', 'C++', 'JavaScript']
+```
+
+我们可以用列表的`remove`方法从列表中删除指定元素,需要注意的是,如果要删除的元素并不在列表中,会引发`ValueError`错误导致程序崩溃,所以建议大家在删除元素时,先用之前讲过的成员运算做一个判断。我们还可以使用`pop`方法从列表中删除元素,`pop`方法默认删除列表中的最后一个元素,当然也可以给一个位置,删除指定位置的元素。在使用`pop`方法删除元素时,如果索引的值超出了范围,会引发`IndexError`异常,导致程序崩溃。除此之外,列表还有一个`clear`方法,可以清空列表中的元素,代码如下所示。
+
+```python
+languages = ['Python', 'SQL', 'Java', 'C++', 'JavaScript']
+if 'Java' in languages:
+ languages.remove('Java')
+if 'Swift' in languages:
+ languages.remove('Swift')
+print(languages) # ['Python', 'SQL', C++', 'JavaScript']
+languages.pop()
+temp = languages.pop(1)
+print(temp) # SQL
+languages.append(temp)
+print(languages) # ['Python', C++', 'SQL']
+languages.clear()
+print(languages) # []
+```
+
+> **说明**:`pop`方法删除元素时会得到被删除的元素,上面的代码中,我们将`pop`方法删除的元素赋值给了名为`temp`的变量。当然如果你愿意,还可以把这个元素再次加入到列表中,正如上面的代码`languages.append(temp)`所做的那样。
+
+这里还有一个小问题,例如`languages`列表中有多个`'Python'`,那么我们用`languages.remove('Python')`是删除所有的`'Python'`,还是删除第一个`'Python'`,大家可以先猜一猜,然后再自己动手尝试一下。
+
+从列表中删除元素其实还有一种方式,就是使用 Python 中的`del`关键字后面跟要删除的元素,这种做法跟使用`pop`方法指定索引删除元素没有实质性的区别,但后者会返回删除的元素,前者在性能上略优,因为`del`对应的底层字节码指令是`DELETE_SUBSCR`,而`pop`对应的底层字节码指令是`CALL_METHOD`和`POP_TOP`,如果不理解就不用管它了。
+
+```python
+items = ['Python', 'Java', 'C++']
+del items[1]
+print(items) # ['Python', 'C++']
+```
+
+#### 元素位置和频次
+
+列表的`index`方法可以查找某个元素在列表中的索引位置,如果找不到指定的元素,`index`方法会引发`ValueError`错误;列表的`count`方法可以统计一个元素在列表中出现的次数,代码如下所示。
+
+```python
+items = ['Python', 'Java', 'Java', 'C++', 'Kotlin', 'Python']
+print(items.index('Python')) # 0
+# 从索引位置1开始查找'Python'
+print(items.index('Python', 1)) # 5
+print(items.count('Python')) # 2
+print(items.count('Kotlin')) # 1
+print(items.count('Swfit')) # 0
+# 从索引位置3开始查找'Java'
+print(items.index('Java', 3)) # ValueError: 'Java' is not in list
+```
+
+#### 元素排序和反转
+
+列表的`sort`操作可以实现列表元素的排序,而`reverse`操作可以实现元素的反转,代码如下所示。
+
+```python
+items = ['Python', 'Java', 'C++', 'Kotlin', 'Swift']
+items.sort()
+print(items) # ['C++', 'Java', 'Kotlin', 'Python', 'Swift']
+items.reverse()
+print(items) # ['Swift', 'Python', 'Kotlin', 'Java', 'C++']
+```
+
+### 列表生成式
+
+在 Python 中,列表还可以通过一种特殊的字面量语法来创建,这种语法叫做生成式。下面,我们通过例子来说明使用列表生成式创建列表到底有什么好处。
+
+场景一:创建一个取值范围在`1`到`99`且能被`3`或者`5`整除的数字构成的列表。
+
+```python
+items = []
+for i in range(1, 100):
+ if i % 3 == 0 or i % 5 == 0:
+ items.append(i)
+print(items)
+```
+
+使用列表生成式做同样的事情,代码如下所示。
+
+```python
+items = [i for i in range(1, 100) if i % 3 == 0 or i % 5 == 0]
+print(items)
+```
+
+场景二:有一个整数列表`nums1`,创建一个新的列表`nums2`,`nums2`中的元素是`nums1`中对应元素的平方。
+
+```python
+nums1 = [35, 12, 97, 64, 55]
+nums2 = []
+for num in nums1:
+ nums2.append(num ** 2)
+print(nums2)
+```
+
+使用列表生成式做同样的事情,代码如下所示。
+
+```python
+nums1 = [35, 12, 97, 64, 55]
+nums2 = [num ** 2 for num in nums1]
+print(nums2)
+```
+
+场景三: 有一个整数列表`nums1`,创建一个新的列表`nums2`,将`nums1`中大于`50`的元素放到`nums2`中。
+
+```python
+nums1 = [35, 12, 97, 64, 55]
+nums2 = []
+for num in nums1:
+ if num > 50:
+ nums2.append(num)
+print(nums2)
+```
+
+使用列表生成式做同样的事情,代码如下所示。
+
+```python
+nums1 = [35, 12, 97, 64, 55]
+nums2 = [num for num in nums1 if num > 50]
+print(nums2)
+```
+
+使用列表生成式创建列表不仅代码简单优雅,而且性能上也优于使用`for-in`循环和`append`方法向空列表中追加元素的方式。为什么说生成式有更好的性能呢,那是因为 Python 解释器的字节码指令中有专门针对生成式的指令(`LIST_APPEND`指令);而`for`循环是通过方法调用(`LOAD_METHOD`和`CALL_METHOD`指令)的方式为列表添加元素,方法调用本身就是一个相对比较耗时的操作。对这一点不理解也没有关系,记住“**强烈建议用生成式语法来创建列表**”这个结论就可以了。
+
+### 嵌套列表
+
+Python 语言没有限定列表中的元素必须是相同的数据类型,也就是说一个列表中的元素可以任意的数据类型,当然也包括列表本身。如果列表中的元素也是列表,那么我们可以称之为嵌套的列表。嵌套的列表可以用来表示表格或数学上的矩阵,例如:我们想保存5个学生3门课程的成绩,可以用如下所示的列表。
+
+```python
+scores = [[95, 83, 92], [80, 75, 82], [92, 97, 90], [80, 78, 69], [65, 66, 89]]
+print(scores[0])
+print(scores[0][1])
+```
+
+对于上面的嵌套列表,每个元素相当于就是一个学生3门课程的成绩,例如`[95, 83, 92]`,而这个列表中的`83`代表了这个学生某一门课的成绩,如果想访问这个值,可以使用两次索引运算`scores[0][1]`,其中`scores[0]`可以得到`[95, 83, 92]`这个列表,再次使用索引运算`[1]`就可以获得该列表中的第二个元素。
+
+如果想通过键盘输入的方式来录入5个学生3门课程的成绩并保存在列表中,可以使用如下所示的代码。
+
+```python
+scores = []
+for _ in range(5):
+ temp = []
+ for _ in range(3):
+ score = int(input('请输入: '))
+ temp.append(score)
+ scores.append(temp)
+print(scores)
+```
+
+如果想通过产生随机数的方式来生成5个学生3门课程的成绩并保存在列表中,我们可以使用列表生成式,代码如下所示。
+
+```python
+import random
+
+scores = [[random.randrange(60, 101) for _ in range(3)] for _ in range(5)]
+print(scores)
+```
+
+> **说明**:上面的代码`[random.randrange(60, 101) for _ in range(3)] `可以产生由3个随机整数构成的列表,我们把这段代码又放在了另一个列表生成式中作为列表的元素,这样的元素一共生成5个,最终得到了一个嵌套列表。
+
+### 列表的应用
+
+下面我们通过一个双色球随机选号的例子为大家讲解列表的应用。双色球是由中国福利彩票发行管理中心发售的乐透型彩票,每注投注号码由`6`个红色球和`1`个蓝色球组成。红色球号码从`1`到`33`中选择,蓝色球号码从`1`到`16`中选择。每注需要选择`6`个红色球号码和`1`个蓝色球号码,如下所示。
+
+
+
+> **提示**:知乎上有一段对国内各种形式的彩票本质的论述相当精彩,这里分享给大家:“**虚构一个不劳而获的人,去忽悠一群想不劳而获的人,最终养活一批真正不劳而获的人**”。很多对概率没有概念的人,甚至认为彩票中与不中的概率都是 50%;还有很多人认为如果中奖的概率是 1%,那么买 100 次就一定可以中奖,这些都是非常荒唐的想法。所以,**珍爱生命,远离赌博,尤其是你对概率一无所知的情况下**!
+
+下面,我们通过 Python 程序来生成一组随机号码。
+
+```python
+"""
+双色球随机选号程序
+
+Author: 骆昊
+Version: 1.0
+"""
+import random
+
+red_balls = list(range(1, 34))
+selected_balls = []
+# 添加6个红色球到选中列表
+for _ in range(6):
+ # 生成随机整数代表选中的红色球的索引位置
+ index = random.randrange(len(red_balls))
+ # 将选中的球从红色球列表中移除并添加到选中列表
+ selected_balls.append(red_balls.pop(index))
+# 对选中的红色球排序
+selected_balls.sort()
+# 输出选中的红色球
+for ball in selected_balls:
+ print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
+# 随机选择1个蓝色球
+blue_ball = random.randrange(1, 17)
+# 输出选中的蓝色球
+print(f'\033[034m{blue_ball:0>2d}\033[0m')
+```
+
+> **说明**:上面代码中`print(f'\033[0m...\033[0m')`是为了控制输出内容的颜色,红色球输出成红色,蓝色球输出成蓝色。其中省略号代表我们要输出的内容,`\033[0m`是一个控制码,表示关闭所有属性,也就是说之前的控制码将会失效,你也可以将其简单的理解为一个定界符,`m`前面的`0`表示控制台的显示方式为默认值,`0`可以省略,`1`表示高亮,`5`表示闪烁,`7`表示反显等。在`0`和`m`的中间,我们可以写上代表颜色的数字,比如`30`代表黑色,`31`代表红色,`32`代表绿色,`33`代表黄色,`34`代表蓝色等。
+
+我们还可以利用`random`模块提供的`sample`和`choice`函数来简化上面的代码,前者可以实现无放回随机抽样,后者可以实现随机抽取一个元素,修改后的代码如下所示。
+
+```python
+"""
+双色球随机选号程序
+
+Author: 骆昊
+Version: 1.1
+"""
+import random
+
+red_balls = [i for i in range(1, 34)]
+blue_balls = [i for i in range(1, 17)]
+# 从红色球列表中随机抽出6个红色球(无放回抽样)
+selected_balls = random.sample(red_balls, 6)
+# 对选中的红色球排序
+selected_balls.sort()
+# 输出选中的红色球
+for ball in selected_balls:
+ print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
+# 从蓝色球列表中随机抽出1个蓝色球
+blue_ball = random.choice(blue_balls)
+# 输出选中的蓝色球
+print(f'\033[034m{blue_ball:0>2d}\033[0m')
+```
+
+如果要实现随机生成`N`注号码,我们只需要将上面的代码放到一个`N`次的循环中,如下所示。
+
+```python
+"""
+双色球随机选号程序
+
+Author: 骆昊
+Version: 1.2
+"""
+import random
+
+n = int(input('生成几注号码: '))
+red_balls = [i for i in range(1, 34)]
+blue_balls = [i for i in range(1, 17)]
+for _ in range(n):
+ # 从红色球列表中随机抽出6个红色球(无放回抽样)
+ selected_balls = random.sample(red_balls, 6)
+ # 对选中的红色球排序
+ selected_balls.sort()
+ # 输出选中的红色球
+ for ball in selected_balls:
+ print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
+ # 从蓝色球列表中随机抽出1个蓝色球
+ blue_ball = random.choice(blue_balls)
+ # 输出选中的蓝色球
+ print(f'\033[034m{blue_ball:0>2d}\033[0m')
+```
+
+我们在 PyCharm 中运行上面的代码,输入`5`,运行效果如下图所示。
+
+
+
+这里顺便给大家介绍一个名为 rich 的 Python 三方库,它可以帮助我们用最简单的方式产生最漂亮的输出,你可以在终端中使用 Python 包管理工具 pip 来安装这个三方库,对于使用 PyCharm 的用户,当然要在 PyCharm 的终端窗口使用 pip 命令将 rich 安装到项目的虚拟环境中,命令如下所示。
+
+```bash
+pip install rich
+```
+
+
+
+如上图所示,rich 安装成功后,我们可以用如下所示的代码来控制输出。
+
+```python
+"""
+双色球随机选号程序
+
+Author: 骆昊
+Version: 1.3
+"""
+import random
+
+from rich.console import Console
+from rich.table import Table
+
+# 创建控制台
+console = Console()
+
+n = int(input('生成几注号码: '))
+red_balls = [i for i in range(1, 34)]
+blue_balls = [i for i in range(1, 17)]
+
+# 创建表格并添加表头
+table = Table(show_header=True)
+for col_name in ('序号', '红球', '蓝球'):
+ table.add_column(col_name, justify='center')
+
+for i in range(n):
+ selected_balls = random.sample(red_balls, 6)
+ selected_balls.sort()
+ blue_ball = random.choice(blue_balls)
+ # 向表格中添加行(序号,红色球,蓝色球)
+ table.add_row(
+ str(i + 1),
+ f'[red]{" ".join([f"{ball:0>2d}" for ball in selected_balls])}[/red]',
+ f'[blue]{blue_ball:0>2d}[/blue]'
+ )
+
+# 通过控制台输出表格
+console.print(table)
+```
+
+> **说明**:上面代码第 31 行使用了列表生成式语法将红色球号码处理成字符串并保存在一个列表中,`" ".join([...])`是将列表中的多个字符串用空格拼接成一个完整的字符串,如果不理解可以先放放。字符串中的`[red]...[/red]`用来设置输出颜色为红色,第 32 行的`[blue]...[/blue]`用来设置输出颜色为蓝色。更多关于 rich 库的知识,可以参考[官方文档](https://github.com/textualize/rich/blob/master/README.cn.md)。
+
+最终的输出如下图所示,看着这样的输出,是不是心情更美好了一些。
+
+
+
+### 总结
+
+Python 中的列表底层是一个可以动态扩容的数组,列表元素在计算机内存中是连续存储的,所以可以实现随机访问(通过一个有效的索引获取对应的元素且操作时间与列表元素个数无关)。我们可以暂时不去触碰这些底层的存储细节,也不需要大家理解列表每个方法的渐近时间复杂度(执行方法耗费的时间跟列表元素个数之间的关系),大家先学会用列表解决工作中的问题,我想这一点更为重要。
\ No newline at end of file
diff --git "a/Day01-20/10.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\205\203\347\273\204.md" "b/Day01-20/10.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\205\203\347\273\204.md"
new file mode 100755
index 000000000..e1b96d377
--- /dev/null
+++ "b/Day01-20/10.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\205\203\347\273\204.md"
@@ -0,0 +1,173 @@
+## 常用数据结构之元组
+
+前面的两节课,我们为大家讲解了 Python 中的列表,它是一种容器型的数据类型,通过列表类型的变量,我们可以保存多个数据并通过循环实现对数据的批量操作。当然,Python 中还有其他容器型的数据类型,接下来我们就为大家讲解另一种容器型的数据类型,它的名字叫元组(tuple)。
+
+### 元组的定义和运算
+
+在 Python 语言中,元组也是多个元素按照一定顺序构成的序列。元组和列表的不同之处在于,**元组是不可变类型**,这就意味着元组类型的变量一旦定义,其中的元素不能再添加或删除,而且元素的值也不能修改。如果试图修改元组中的元素,将引发`TypeError`错误,导致程序崩溃。定义元组通常使用形如`(x, y, z)`的字面量语法,元组类型支持的运算符跟列表是一样的,我们可以看看下面的代码。
+
+```python
+# 定义一个三元组
+t1 = (35, 12, 98)
+# 定义一个四元组
+t2 = ('骆昊', 45, True, '四川成都')
+
+# 查看变量的类型
+print(type(t1)) #
+
+随着时间的推移,虽然数值运算仍然是计算机日常工作中最为重要的组成部分,但是今天的计算机还要处理大量的以文本形式存在的信息。如果我们希望通过 Python 程序来操作本这些文本信息,就必须要先了解字符串这种数据类型以及与它相关的运算和方法。
+
+### 字符串的定义
+
+所谓**字符串**,就是**由零个或多个字符组成的有限序列**,一般记为:
+
+$$
+s = a_1a_2 \cdots a_n \,\,\,\,\, (0 \le n \le \infty)
+$$
+
+在 Python 程序中,我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。字符串中的字符可以是特殊符号、英文字母、中文字符、日文的平假名或片假名、希腊字母、Emoji 字符(如:💩、🐷、🀄️)等。
+
+```python
+s1 = 'hello, world!'
+s2 = "你好,世界!❤️"
+s3 = '''hello,
+wonderful
+world!'''
+print(s1)
+print(s2)
+print(s3)
+```
+
+#### 转义字符
+
+我们可以在字符串中使用`\`(反斜杠)来表示转义,也就是说`\`后面的字符不再是它原来的意义,例如:`\n`不是代表字符`\`和字符`n`,而是表示换行;`\t`也不是代表字符`\`和字符`t`,而是表示制表符。所以如果字符串本身又包含了`'`、`"`、`\`这些特殊的字符,必须要通过`\`进行转义处理。例如要输出一个带单引号或反斜杠的字符串,需要用如下所示的方法。
+
+```python
+s1 = '\'hello, world!\''
+s2 = '\\hello, world!\\'
+print(s1)
+print(s2)
+```
+
+#### 原始字符串
+
+Python 中有一种以`r`或`R`开头的字符串,这种字符串被称为原始字符串,意思是字符串中的每个字符都是它本来的含义,没有所谓的转义字符。例如,在字符串`'hello\n'`中,`\n`表示换行;而在`r'hello\n'`中,`\n`不再表示换行,就是字符`\`和字符`n`。大家可以运行下面的代码,看看会输出什么。
+
+```python
+s1 = '\it \is \time \to \read \now'
+s2 = r'\it \is \time \to \read \now'
+print(s1)
+print(s2)
+```
+
+> **说明**:上面的变量`s1`中,`\t`、`\r`和`\n`都是转义字符。`\t`是制表符(table),`\n`是换行符(new line),`\r`是回车符(carriage return)相当于让输出回到了行首。对比一下两个`print`函数的输出,看看到底有什么区别!
+
+#### 字符的特殊表示
+
+Python 中还允许在`\`后面还可以跟一个八进制或者十六进制数来表示字符,例如`\141`和`\x61`都代表小写字母`a`,前者是八进制的表示法,后者是十六进制的表示法。另外一种表示字符的方式是在`\u`后面跟 Unicode 字符编码,例如`\u9a86\u660a`代表的是中文“骆昊”。运行下面的代码,看看输出了什么。
+
+```python
+s1 = '\141\142\143\x61\x62\x63'
+s2 = '\u9a86\u660a'
+print(s1)
+print(s2)
+```
+
+### 字符串的运算
+
+Python 语言为字符串类型提供了非常丰富的运算符,有很多运算符跟列表类型的运算符作用类似。例如,我们可以使用`+`运算符来实现字符串的拼接,可以使用`*`运算符来重复一个字符串的内容,可以使用`in`和`not in`来判断一个字符串是否包含另外一个字符串,我们也可以用`[]`和`[:]`运算符从字符串取出某个字符或某些字符。
+
+#### 拼接和重复
+
+下面的例子演示了使用`+`和`*`运算符来实现字符串的拼接和重复操作。
+
+```python
+s1 = 'hello' + ', ' + 'world'
+print(s1) # hello, world
+s2 = '!' * 3
+print(s2) # !!!
+s1 += s2
+print(s1) # hello, world!!!
+s1 *= 2
+print(s1) # hello, world!!!hello, world!!!
+```
+
+用`*`实现字符串的重复是非常有意思的一个运算符,在很多编程语言中,要表示一个有10个`a`的字符串,你只能写成`'aaaaaaaaaa'`,但是在 Python 中,你可以写成`'a' * 10`。你可能觉得`'aaaaaaaaaa'`这种写法也没有什么不方便的,但是请想一想,如果字符`a`要重复100次或者1000次又会如何呢?
+
+#### 比较运算
+
+对于两个字符串类型的变量,可以直接使用比较运算符来判断两个字符串的相等性或比较大小。需要说明的是,因为字符串在计算机内存中也是以二进制形式存在的,那么字符串的大小比较比的是每个字符对应的编码的大小。例如`A`的编码是`65`, 而`a`的编码是`97`,所以`'A' < 'a'`的结果相当于就是`65 < 97`的结果,这里很显然是`True`;而`'boy' < 'bad'`,因为第一个字符都是`'b'`比不出大小,所以实际比较的是第二个字符的大小,显然`'o' < 'a'`的结果是`False`,所以`'boy' < 'bad'`的结果是`False`。如果不清楚两个字符对应的编码到底是多少,可以使用`ord`函数来获得,之前我们有提到过这个函数。例如`ord('A')`的值是`65`,而`ord('昊')`的值是`26122`。下面的代码展示了字符串的比较运算,请大家仔细看看。
+
+```python
+s1 = 'a whole new world'
+s2 = 'hello world'
+print(s1 == s2) # False
+print(s1 < s2) # True
+print(s1 == 'hello world') # False
+print(s2 == 'hello world') # True
+print(s2 != 'Hello world') # True
+s3 = '骆昊'
+print(ord('骆')) # 39558
+print(ord('昊')) # 26122
+s4 = '王大锤'
+print(ord('王')) # 29579
+print(ord('大')) # 22823
+print(ord('锤')) # 38180
+print(s3 >= s4) # True
+print(s3 != s4) # True
+```
+
+#### 成员运算
+
+Python 中可以用`in`和`not in`判断一个字符串中是否包含另外一个字符或字符串,跟列表类型一样,`in`和`not in`称为成员运算符,会产生布尔值`True`或`False`,代码如下所示。
+
+```python
+s1 = 'hello, world'
+s2 = 'goodbye, world'
+print('wo' in s1) # True
+print('wo' not in s2) # False
+print(s2 in s1) # False
+```
+
+#### 获取字符串长度
+
+获取字符串长度跟获取列表元素个数一样,使用内置函数`len`,代码如下所示。
+
+```python
+s = 'hello, world'
+print(len(s)) # 12
+print(len('goodbye, world')) # 14
+```
+
+#### 索引和切片
+
+字符串的索引和切片操作跟列表、元组几乎没有区别,因为字符串也是一种有序序列,可以通过正向或反向的整数索引访问其中的元素。但是有一点需要注意,因为**字符串是不可变类型**,所以**不能通过索引运算修改字符串中的字符**。
+
+```python
+s = 'abc123456'
+n = len(s)
+print(s[0], s[-n]) # a a
+print(s[n-1], s[-1]) # 6 6
+print(s[2], s[-7]) # c c
+print(s[5], s[-4]) # 3 3
+print(s[2:5]) # c12
+print(s[-7:-4]) # c12
+print(s[2:]) # c123456
+print(s[:2]) # ab
+print(s[::2]) # ac246
+print(s[::-1]) # 654321cba
+```
+
+需要再次提醒大家注意的是,在进行索引运算时,如果索引越界,会引发`IndexError`异常,错误提示信息为:`string index out of range`(字符串索引超出范围)。
+
+### 字符的遍历
+
+如果希望遍历字符串中的每个字符,可以使用`for-in`循环,有如下所示的两种方式。
+
+方式一:
+
+```python
+s = 'hello'
+for i in range(len(s)):
+ print(s[i])
+```
+
+方式二:
+
+```python
+s = 'hello'
+for elem in s:
+ print(elem)
+```
+
+### 字符串的方法
+
+在 Python 中,我们可以通过字符串类型自带的方法对字符串进行操作和处理,假设我们有名为`foo`的字符串,字符串有名为`bar`的方法,那么使用字符串方法的语法是:`foo.bar()`,这是一种通过对象引用调用对象方法的语法,跟前面使用列表方法的语法是一样的。
+
+#### 大小写相关操作
+
+下面的代码演示了和字符串大小写变换相关的方法。
+
+```python
+s1 = 'hello, world!'
+# 字符串首字母大写
+print(s1.capitalize()) # Hello, world!
+# 字符串每个单词首字母大写
+print(s1.title()) # Hello, World!
+# 字符串变大写
+print(s1.upper()) # HELLO, WORLD!
+s2 = 'GOODBYE'
+# 字符串变小写
+print(s2.lower()) # goodbye
+# 检查s1和s2的值
+print(s1) # hello, world
+print(s2) # GOODBYE
+```
+
+> **说明**:由于字符串是不可变类型,使用字符串的方法对字符串进行操作会产生新的字符串,但是原来变量的值并没有发生变化。所以上面的代码中,当我们最后检查`s1`和`s2`两个变量的值时,`s1`和`s2` 的值并没有发生变化。
+
+#### 查找操作
+
+如果想在一个字符串中从前向后查找有没有另外一个字符串,可以使用字符串的`find`或`index`方法。在使用`find`和`index`方法时还可以通过方法的参数来指定查找的范围,也就是查找不必从索引为`0`的位置开始。
+
+```python
+s = 'hello, world!'
+print(s.find('or')) # 8
+print(s.find('or', 9)) # -1
+print(s.find('of')) # -1
+print(s.index('or')) # 8
+print(s.index('or', 9)) # ValueError: substring not found
+```
+
+>**说明**:`find`方法找不到指定的字符串会返回`-1`,`index`方法找不到指定的字符串会引发`ValueError`错误。
+
+`find`和`index`方法还有逆向查找(从后向前查找)的版本,分别是`rfind`和`rindex`,代码如下所示。
+
+```python
+s = 'hello world!'
+print(s.find('o')) # 4
+print(s.rfind('o')) # 7
+print(s.rindex('o')) # 7
+# print(s.rindex('o', 8)) # ValueError: substring not found
+```
+
+#### 性质判断
+
+可以通过字符串的`startswith`、`endswith`来判断字符串是否以某个字符串开头和结尾;还可以用`is`开头的方法判断字符串的特征,这些方法都返回布尔值,代码如下所示。
+
+```python
+s1 = 'hello, world!'
+print(s1.startswith('He')) # False
+print(s1.startswith('hel')) # True
+print(s1.endswith('!')) # True
+s2 = 'abc123456'
+print(s2.isdigit()) # False
+print(s2.isalpha()) # False
+print(s2.isalnum()) # True
+```
+
+> **说明**:上面的`isdigit`用来判断字符串是不是完全由数字构成的,`isalpha`用来判断字符串是不是完全由字母构成的,这里的字母指的是 Unicode 字符但不包含 Emoji 字符,`isalnum`用来判断字符串是不是由字母和数字构成的。
+
+#### 格式化
+
+在 Python 中,字符串类型可以通过`center`、`ljust`、`rjust`方法做居中、左对齐和右对齐的处理。如果要在字符串的左侧补零,也可以使用`zfill`方法。
+
+```python
+s = 'hello, world'
+print(s.center(20, '*')) # ****hello, world****
+print(s.rjust(20)) # hello, world
+print(s.ljust(20, '~')) # hello, world~~~~~~~~
+print('33'.zfill(5)) # 00033
+print('-33'.zfill(5)) # -0033
+```
+
+我们之前讲过,在用`print`函数输出字符串时,可以用下面的方式对字符串进行格式化。
+
+```python
+a = 321
+b = 123
+print('%d * %d = %d' % (a, b, a * b))
+```
+
+当然,我们也可以用字符串的`format`方法来完成字符串的格式,代码如下所示。
+
+```python
+a = 321
+b = 123
+print('{0} * {1} = {2}'.format(a, b, a * b))
+```
+
+从 Python 3.6 开始,格式化字符串还有更为简洁的书写方式,就是在字符串前加上`f`来格式化字符串,在这种以`f`打头的字符串中,`{变量名}`是一个占位符,会被变量对应的值将其替换掉,代码如下所示。
+
+```python
+a = 321
+b = 123
+print(f'{a} * {b} = {a * b}')
+```
+
+如果需要进一步控制格式化语法中变量值的形式,可以参照下面的表格来进行字符串格式化操作。
+
+| 变量值 | 占位符 | 格式化结果 | 说明 |
+| ----------- | ---------- | ------------- | ---- |
+| `3.1415926` | `{:.2f}` | `'3.14'` | 保留小数点后两位 |
+| `3.1415926` | `{:+.2f}` | `'+3.14'` | 带符号保留小数点后两位 |
+| `-1` | `{:+.2f}` | `'-1.00'` | 带符号保留小数点后两位 |
+| `3.1415926` | `{:.0f}` | `'3'` | 不带小数 |
+| `123` | `{:0>10d}` | `'0000000123'` | 左边补`0`,补够10位 |
+| `123` | `{:x<10d}` | `'123xxxxxxx'` | 右边补`x` ,补够10位 |
+| `123` | `{:>10d}` | `' 123'` | 左边补空格,补够10位 |
+| `123` | `{:<10d}` | `'123 '` | 右边补空格,补够10位 |
+| `123456789` | `{:,}` | `'123,456,789'` | 逗号分隔格式 |
+| `0.123` | `{:.2%}` | `'12.30%'` | 百分比格式 |
+| `123456789` | `{:.2e}` | `'1.23e+08'` | 科学计数法格式 |
+
+#### 修剪操作
+
+字符串的`strip`方法可以帮我们获得将原字符串修剪掉左右两端指定字符之后的字符串,默认是修剪空格字符。这个方法非常有实用价值,可以用来将用户输入时不小心键入的头尾空格等去掉,`strip`方法还有`lstrip`和`rstrip`两个版本,相信从名字大家已经猜出来这两个方法是做什么用的。
+
+```python
+s1 = ' jackfrued@126.com '
+print(s1.strip()) # jackfrued@126.com
+s2 = '~你好,世界~'
+print(s2.lstrip('~')) # 你好,世界~
+print(s2.rstrip('~')) # ~你好,世界
+```
+
+#### 替换操作
+
+如果希望用新的内容替换字符串中指定的内容,可以使用`replace`方法,代码如下所示。`replace`方法的第一个参数是被替换的内容,第二个参数是替换后的内容,还可以通过第三个参数指定替换的次数。
+
+```python
+s = 'hello, good world'
+print(s.replace('o', '@')) # hell@, g@@d w@rld
+print(s.replace('o', '@', 1)) # hell@, good world
+```
+
+#### 拆分与合并
+
+可以使用字符串的`split`方法将一个字符串拆分为多个字符串(放在一个列表中),也可以使用字符串的`join`方法将列表中的多个字符串连接成一个字符串,代码如下所示。
+
+```python
+s = 'I love you'
+words = s.split()
+print(words) # ['I', 'love', 'you']
+print('~'.join(words)) # I~love~you
+```
+
+需要说明的是,`split`方法默认使用空格进行拆分,我们也可以指定其他的字符来拆分字符串,而且还可以指定最大拆分次数来控制拆分的效果,代码如下所示。
+
+```python
+s = 'I#love#you#so#much'
+words = s.split('#')
+print(words) # ['I', 'love', 'you', 'so', 'much']
+words = s.split('#', 2)
+print(words) # ['I', 'love', 'you#so#much']
+```
+
+#### 编码和解码
+
+Python 中除了字符串`str`类型外,还有一种表示二进制数据的字节串类型(`bytes`)。所谓字节串,就是**由零个或多个字节组成的有限序列**。通过字符串的`encode`方法,我们可以按照某种编码方式将字符串编码为字节串,我们也可以使用字节串的`decode`方法,将字节串解码为字符串,代码如下所示。
+
+```python
+a = '骆昊'
+b = a.encode('utf-8')
+c = a.encode('gbk')
+print(b) # b'\xe9\xaa\x86\xe6\x98\x8a'
+print(c) # b'\xc2\xe6\xea\xbb'
+print(b.decode('utf-8')) # 骆昊
+print(c.decode('gbk')) # 骆昊
+```
+
+注意,如果编码和解码的方式不一致,会导致乱码问题(无法再现原始的内容)或引发`UnicodeDecodeError`错误,导致程序崩溃。
+
+#### 其他方法
+
+对于字符串类型来说,还有一个常用的操作是对字符串进行匹配检查,即检查字符串是否满足某种特定的模式。例如,一个网站对用户注册信息中用户名和邮箱的检查,就属于模式匹配检查。实现模式匹配检查的工具叫做正则表达式,Python 语言通过标准库中的`re`模块提供了对正则表达式的支持,我们会在后续的课程中为大家讲解这个知识点。
+
+### 总结
+
+知道如何表示和操作字符串对程序员来说是非常重要的,因为我们经常需要处理文本信息,Python 中操作字符串可以用拼接、索引、切片等运算符,也可以使用字符串类型提供的非常丰富的方法。
diff --git "a/Day01-20/12.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\351\233\206\345\220\210.md" "b/Day01-20/12.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\351\233\206\345\220\210.md"
new file mode 100755
index 000000000..a4b745806
--- /dev/null
+++ "b/Day01-20/12.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\351\233\206\345\220\210.md"
@@ -0,0 +1,185 @@
+## 常用数据结构之集合
+
+在学习了列表和元组之后,我们再来学习一种容器型的数据类型,它的名字叫集合(set)。说到集合这个词大家一定不会陌生,在数学课本上就有这个概念。如果我们**把一定范围的、确定的、可以区别的事物当作一个整体来看待**,那么这个整体就是集合,集合中的各个事物称为集合的**元素**。通常,集合需要满足以下要求:
+
+1. **无序性**:一个集合中,每个元素的地位都是相同的,元素之间是无序的。
+2. **互异性**:一个集合中,任何两个元素都是不相同的,即元素在集合中只能出现一次。
+3. **确定性**:给定一个集合和一个任意元素,该元素要么属这个集合,要么不属于这个集合,二者必居其一,不允许有模棱两可的情况出现。
+
+Python 程序中的集合跟数学上的集合没有什么本质区别,需要强调的是上面所说的无序性和互异性。无序性说明集合中的元素并不像列中的元素那样存在某种次序,可以通过索引运算就能访问任意元素,**集合并不支持索引运算**。另外,集合的互异性决定了**集合中不能有重复元素**,这一点也是集合区别于列表的地方,我们无法将重复的元素添加到一个集合中。集合类型必然是支持`in`和`not in`成员运算的,这样就可以确定一个元素是否属于集合,也就是上面所说的集合的确定性。**集合的成员运算在性能上要优于列表的成员运算**,这是集合的底层存储特性决定的,此处我们暂时不做讨论,大家记住这个结论即可。
+
+> **说明**:集合底层使用了哈希存储(散列存储),对哈希存储不了解的读者可以先看看“Hello 算法”网站对[哈希表](https://www.hello-algo.com/chapter_hashing/)的讲解,感谢作者的开源精神。
+
+### 创建集合
+
+在 Python 中,创建集合可以使用`{}`字面量语法,`{}`中需要至少有一个元素,因为没有元素的`{}`并不是空集合而是一个空字典,字典类型我们会在下一节课中为大家介绍。当然,也可以使用 Python 内置函数`set`来创建一个集合,准确的说`set`并不是一个函数,而是创建集合对象的构造器,这个知识点会在后面讲解面向对象编程的地方为大家介绍。我们可以使用`set`函数创建一个空集合,也可以用它将其他序列转换成集合,例如:`set('hello')`会得到一个包含了`4`个字符的集合(重复的字符`l`只会在集合中出现一次)。除了这两种方式,还可以使用生成式语法来创建集合,就像我们之前用生成式语法创建列表那样。
+
+```python
+set1 = {1, 2, 3, 3, 3, 2}
+print(set1)
+
+set2 = {'banana', 'pitaya', 'apple', 'apple', 'banana', 'grape'}
+print(set2)
+
+set3 = set('hello')
+print(set3)
+
+set4 = set([1, 2, 2, 3, 3, 3, 2, 1])
+print(set4)
+
+set5 = {num for num in range(1, 20) if num % 3 == 0 or num % 7 == 0}
+print(set5)
+```
+
+需要提醒大家,集合中的元素必须是`hashable`类型,所谓`hashable`类型指的是能够计算出哈希码的数据类型,通常不可变类型都是`hashable`类型,如整数(`int`)、浮点小数(`float`)、布尔值(`bool`)、字符串(`str`)、元组(`tuple`)等。可变类型都不是`hashable`类型,因为可变类型无法计算出确定的哈希码,所以它们不能放到集合中。例如:我们不能将列表作为集合中的元素;同理,由于集合本身也是可变类型,所以集合也不能作为集合中的元素。我们可以创建出嵌套列表(列表的元素也是列表),但是我们不能创建出嵌套的集合,这一点在使用集合的时候一定要引起注意。
+
+> **温馨提示**:如果不理解上面提到的哈希码、哈希存储这些概念,可以先放放,因为它并不影响你继续学习和使用 Python 语言。当然,如果是计算机专业的小伙伴,不理解哈希存储是很难被原谅的,要赶紧去补课了。
+
+### 元素的遍历
+
+我们可以通过`len`函数来获得集合中有多少个元素,但是我们不能通过索引运算来遍历集合中的元素,因为集合元素并没有特定的顺序。当然,要实现对集合元素的遍历,我们仍然可以使用`for-in`循环,代码如下所示。
+
+```python
+set1 = {'Python', 'C++', 'Java', 'Kotlin', 'Swift'}
+for elem in set1:
+ print(elem)
+```
+
+> **提示**:大家看看上面代码的运行结果,通过单词输出的顺序体会一下集合的无序性。
+
+### 集合的运算
+
+Python 为集合类型提供了非常丰富的运算,主要包括:成员运算、交集运算、并集运算、差集运算、比较运算(相等性、子集、超集)等。
+
+#### 成员运算
+
+可以通过成员运算`in`和`not in `检查元素是否在集合中,代码如下所示。
+
+```python
+set1 = {11, 12, 13, 14, 15}
+print(10 in set1) # False
+print(15 in set1) # True
+set2 = {'Python', 'Java', 'C++', 'Swift'}
+print('Ruby' in set2) # False
+print('Java' in set2) # True
+```
+
+#### 二元运算
+
+集合的二元运算主要指集合的交集、并集、差集、对称差等运算,这些运算可以通过运算符来实现,也可以通过集合类型的方法来实现,代码如下所示。
+
+
+
+```python
+set1 = {1, 2, 3, 4, 5, 6, 7}
+set2 = {2, 4, 6, 8, 10}
+
+# 交集
+print(set1 & set2) # {2, 4, 6}
+print(set1.intersection(set2)) # {2, 4, 6}
+
+# 并集
+print(set1 | set2) # {1, 2, 3, 4, 5, 6, 7, 8, 10}
+print(set1.union(set2)) # {1, 2, 3, 4, 5, 6, 7, 8, 10}
+
+# 差集
+print(set1 - set2) # {1, 3, 5, 7}
+print(set1.difference(set2)) # {1, 3, 5, 7}
+
+# 对称差
+print(set1 ^ set2) # {1, 3, 5, 7, 8, 10}
+print(set1.symmetric_difference(set2)) # {1, 3, 5, 7, 8, 10}
+```
+
+通过上面的代码可以看出,对两个集合求交集,`&`运算符和`intersection`方法的作用是完全相同的,使用运算符的方式显然更直观且代码也更简短。需要说明的是,集合的二元运算还可以跟赋值运算一起构成复合赋值运算,例如:`set1 |= set2`相当于`set1 = set1 | set2`,跟`|=`作用相同的方法是`update`;`set1 &= set2`相当于`set1 = set1 & set2`,跟`&=`作用相同的方法是`intersection_update`,代码如下所示。
+
+```python
+set1 = {1, 3, 5, 7}
+set2 = {2, 4, 6}
+set1 |= set2
+# set1.update(set2)
+print(set1) # {1, 2, 3, 4, 5, 6, 7}
+set3 = {3, 6, 9}
+set1 &= set3
+# set1.intersection_update(set3)
+print(set1) # {3, 6}
+set2 -= set1
+# set2.difference_update(set1)
+print(set2) # {2, 4}
+```
+
+#### 比较运算
+
+两个集合可以用`==`和`!=`进行相等性判断,如果两个集合中的元素完全相同,那么`==`比较的结果就是`True`,否则就是`False`。如果集合`A`的任意一个元素都是集合`B`的元素,那么集合`A`称为集合`B`的子集,即对于 $\small{\forall{a} \in {A}}$ ,均有 $\small{{a} \in {B}}$ ,则 $\small{{A} \subseteq {B}}$ ,`A`是`B`的子集,反过来也可以称`B`是`A`的超集。如果`A`是`B`的子集且`A`不等于`B`,那么`A`就是`B`的真子集。Python 为集合类型提供了判断子集和超集的运算符,其实就是我们非常熟悉的`<`、`<=`、`>`、`>=`这些运算符。当然,我们也可以通过集合类型的方法`issubset`和`issuperset`来判断集合之间的关系,代码如下所示。
+
+```python
+set1 = {1, 3, 5}
+set2 = {1, 2, 3, 4, 5}
+set3 = {5, 4, 3, 2, 1}
+
+print(set1 < set2) # True
+print(set1 <= set2) # True
+print(set2 < set3) # False
+print(set2 <= set3) # True
+print(set2 > set1) # True
+print(set2 == set3) # True
+
+print(set1.issubset(set2)) # True
+print(set2.issuperset(set1)) # True
+```
+
+> **说明**:上面的代码中,`set1 < set2`判断`set1`是不是`set2`的真子集,`set1 <= set2`判断`set1`是不是`set2`的子集,`set2 > set1`判断`set2`是不是`set1`的超集。当然,我们也可以通过`set1.issubset(set2)`判断`set1`是不是`set2`的子集;通过`set2.issuperset(set1)`判断`set2`是不是`set1`的超集。
+
+### 集合的方法
+
+刚才我们说过,Python 中的集合是可变类型,我们可以通过集合的方法向集合添加元素或从集合中删除元素。
+
+```python
+set1 = {1, 10, 100}
+
+# 添加元素
+set1.add(1000)
+set1.add(10000)
+print(set1) # {1, 100, 1000, 10, 10000}
+
+# 删除元素
+set1.discard(10)
+if 100 in set1:
+ set1.remove(100)
+print(set1) # {1, 1000, 10000}
+
+# 清空元素
+set1.clear()
+print(set1) # set()
+```
+
+> **说明**:删除元素的`remove`方法在元素不存在时会引发`KeyError`错误,所以上面的代码中我们先通过成员运算判断元素是否在集合中。集合类型还有一个`pop`方法可以从集合中随机删除一个元素,该方法在删除元素的同时会返回(获得)被删除的元素,而`remove`和`discard`方法仅仅是删除元素,不会返回(获得)被删除的元素。
+
+集合类型还有一个名为`isdisjoint`的方法可以判断两个集合有没有相同的元素,如果没有相同元素,该方法返回`True`,否则该方法返回`False`,代码如下所示。
+
+```python
+set1 = {'Java', 'Python', 'C++', 'Kotlin'}
+set2 = {'Kotlin', 'Swift', 'Java', 'Dart'}
+set3 = {'HTML', 'CSS', 'JavaScript'}
+print(set1.isdisjoint(set2)) # False
+print(set1.isdisjoint(set3)) # True
+```
+
+### 不可变集合
+
+Python 中还有一种不可变类型的集合,名字叫`frozenset`。`set`跟`frozenset`的区别就如同`list`跟`tuple`的区别,`frozenset`由于是不可变类型,能够计算出哈希码,因此它可以作为`set`中的元素。除了不能添加和删除元素,`frozenset`在其他方面跟`set`是一样的,下面的代码简单的展示了`frozenset`的用法。
+
+```python
+fset1 = frozenset({1, 3, 5, 7})
+fset2 = frozenset(range(1, 6))
+print(fset1) # frozenset({1, 3, 5, 7})
+print(fset2) # frozenset({1, 2, 3, 4, 5})
+print(fset1 & fset2) # frozenset({1, 3, 5})
+print(fset1 | fset2) # frozenset({1, 2, 3, 4, 5, 7})
+print(fset1 - fset2) # frozenset({7})
+print(fset1 < fset2) # False
+```
+
+### 总结
+
+Python 中的**集合类型是一种无序容器**,**不允许有重复运算**,由于底层使用了哈希存储,集合中的元素必须是`hashable`类型。集合与列表最大的区别在于**集合中的元素没有顺序**、所以**不能够通过索引运算访问元素**、但是集合可以执行交集、并集、差集等二元运算,也可以通过关系运算符检查两个集合是否存在超集、子集等关系。
diff --git "a/Day01-20/13.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\255\227\345\205\270.md" "b/Day01-20/13.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\255\227\345\205\270.md"
new file mode 100755
index 000000000..7d5849f67
--- /dev/null
+++ "b/Day01-20/13.\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\255\227\345\205\270.md"
@@ -0,0 +1,265 @@
+## 常用数据结构之字典
+
+迄今为止,我们已经为大家介绍了 Python 中的三种容器型数据类型(列表、元组、集合),但是这些数据类型仍然不足以帮助我们解决所有的问题。例如,我们需要一个变量来保存一个人的多项信息,包括:姓名、年龄、身高、体重、家庭住址、本人手机号、紧急联系人手机号,此时你会发现,我们之前学过的列表、元组和集合类型都不够好使。
+
+```python
+person1 = ['王大锤', 55, 168, 60, '成都市武侯区科华北路62号1栋101', '13122334455', '13800998877']
+person2 = ('王大锤', 55, 168, 60, '成都市武侯区科华北路62号1栋101', '13122334455', '13800998877')
+person3 = {'王大锤', 55, 168, 60, '成都市武侯区科华北路62号1栋101', '13122334455', '13800998877'}
+```
+
+集合肯定是最不合适的,因为集合中不能有重复元素,如果一个人的年龄和体重刚好相同,那么集合中就会少一项信息;同理,如果这个人的手机号和紧急联系人手机号是相同的,那么集合中又会少一项信息。另一方面,虽然列表和元组可以把一个人的所有信息都保存下来,但是当你想要获取这个人的手机号或家庭住址时,你得先知道他的手机号是列表或元组中的第几个元素。总之,在遇到上述的场景时,列表、元组、集合都不是最合适的选择,此时我们需要字典(dictionary)类型,这种数据类型最适合把相关联的信息组装到一起,可以帮助我们解决 Python 程序中为真实事物建模的问题。
+
+说到字典这个词,大家一定不陌生,读小学的时候,每个人手头基本上都有一本《新华字典》,如下图所示。
+
+
+
+Python 程序中的字典跟现实生活中的字典很像,它以键值对(键和值的组合)的方式把数据组织到一起,我们可以通过键找到与之对应的值并进行操作。就像《新华字典》中,每个字(键)都有与它对应的解释(值)一样,每个字和它的解释合在一起就是字典中的一个条目,而字典中通常包含了很多个这样的条目。
+
+### 创建和使用字典
+
+Python 中创建字典可以使用`{}`字面量语法,这一点跟上一节课讲的集合是一样的。但是字典的`{}`中的元素是以键值对的形式存在的,每个元素由`:`分隔的两个值构成,`:`前面是键,`:`后面是值,代码如下所示。
+
+```python
+xinhua = {
+ '麓': '山脚下',
+ '路': '道,往来通行的地方;方面,地区:南~货,外~货;种类:他俩是一~人',
+ '蕗': '甘草的别名',
+ '潞': '潞水,水名,即今山西省的浊漳河;潞江,水名,即云南省的怒江'
+}
+print(xinhua)
+person = {
+ 'name': '王大锤',
+ 'age': 55,
+ 'height': 168,
+ 'weight': 60,
+ 'addr': '成都市武侯区科华北路62号1栋101',
+ 'tel': '13122334455',
+ 'emergence contact': '13800998877'
+}
+print(person)
+```
+
+通过上面的代码,相信大家已经看出来了,用字典来保存一个人的信息远远优于使用列表或元组,因为我们可以用`:`前面的键来表示条目的含义,而`:`后面就是这个条目所对应的值。
+
+当然,如果愿意,我们也可以使用内置函数`dict`或者是字典的生成式语法来创建字典,代码如下所示。
+
+```python
+# dict函数(构造器)中的每一组参数就是字典中的一组键值对
+person = dict(name='王大锤', age=55, height=168, weight=60, addr='成都市武侯区科华北路62号1栋101')
+print(person) # {'name': '王大锤', 'age': 55, 'height': 168, 'weight': 60, 'addr': '成都市武侯区科华北路62号1栋101'}
+
+# 可以通过Python内置函数zip压缩两个序列并创建字典
+items1 = dict(zip('ABCDE', '12345'))
+print(items1) # {'A': '1', 'B': '2', 'C': '3', 'D': '4', 'E': '5'}
+items2 = dict(zip('ABCDE', range(1, 10)))
+print(items2) # {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5}
+
+# 用字典生成式语法创建字典
+items3 = {x: x ** 3 for x in range(1, 6)}
+print(items3) # {1: 1, 2: 8, 3: 27, 4: 64, 5: 125}
+```
+
+想知道字典中一共有多少组键值对,仍然是使用`len`函数;如果想对字典进行遍历,可以用`for`循环,但是需要注意,`for`循环只是对字典的键进行了遍历,不过没关系,在学习了字典的索引运算后,我们可以通过字典的键访问它对应的值。
+
+```python
+person = {
+ 'name': '王大锤',
+ 'age': 55,
+ 'height': 168,
+ 'weight': 60,
+ 'addr': '成都市武侯区科华北路62号1栋101'
+}
+print(len(person)) # 5
+for key in person:
+ print(key)
+```
+
+### 字典的运算
+
+对于字典类型来说,成员运算和索引运算肯定是很重要的,前者可以判定指定的键在不在字典中,后者可以通过键访问对应的值或者向字典中添加新的键值对。值得注意的是,字典的索引不同于列表的索引,列表中的元素因为有属于自己有序号,所以列表的索引是一个整数;字典中因为保存的是键值对,所以字典需要用键去索引对应的值。需要**特别提醒**大家注意的是,**字典中的键必须是不可变类型**,例如整数(`int`)、浮点数(`float`)、字符串(`str`)、元组(`tuple`)等类型,这一点跟集合类型对元素的要求是一样的;很显然,之前我们讲的列表(`list`)和集合(`set`)不能作为字典中的键,字典类型本身也不能再作为字典中的键,因为字典也是可变类型,但是列表、集合、字典都可以作为字典中的值,例如:
+
+```python
+person = {
+ 'name': '王大锤',
+ 'age': 55,
+ 'height': 168,
+ 'weight': 60,
+ 'addr': ['成都市武侯区科华北路62号1栋101', '北京市西城区百万庄大街1号'],
+ 'car': {
+ 'brand': 'BMW X7',
+ 'maxSpeed': '250',
+ 'length': 5170,
+ 'width': 2000,
+ 'height': 1835,
+ 'displacement': 3.0
+ }
+}
+print(person)
+```
+
+大家可以看看下面的代码,了解一下字典的成员运算和索引运算。
+
+```python
+person = {'name': '王大锤', 'age': 55, 'height': 168, 'weight': 60, 'addr': '成都市武侯区科华北路62号1栋101'}
+
+# 成员运算
+print('name' in person) # True
+print('tel' in person) # False
+
+# 索引运算
+print(person['name'])
+print(person['addr'])
+person['age'] = 25
+person['height'] = 178
+person['tel'] = '13122334455'
+person['signature'] = '你的男朋友是一个盖世垃圾,他会踏着五彩祥云去迎娶你的闺蜜'
+print(person)
+
+# 循环遍历
+for key in person:
+ print(f'{key}:\t{person[key]}')
+```
+
+需要注意,在通过索引运算获取字典中的值时,如指定的键没有在字典中,将会引发`KeyError`异常。
+
+### 字典的方法
+
+字典类型的方法基本上都跟字典的键值对操作相关,其中`get`方法可以通过键来获取对应的值。跟索引运算不同的是,`get`方法在字典中没有指定的键时不会产生异常,而是返回`None`或指定的默认值,代码如下所示。
+
+```python
+person = {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'}
+print(person.get('name')) # 王大锤
+print(person.get('sex')) # None
+print(person.get('sex', True)) # True
+```
+
+如果需要获取字典中所有的键,可以使用`keys`方法;如果需要获取字典中所有的值,可以使用`values`方法。字典还有一个名为`items`的方法,它会将键和值组装成二元组,通过该方法来遍历字典中的元素也是非常方便的。
+
+```python
+person = {'name': '王大锤', 'age': 25, 'height': 178}
+print(person.keys()) # dict_keys(['name', 'age', 'height'])
+print(person.values()) # dict_values(['王大锤', 25, 178])
+print(person.items()) # dict_items([('name', '王大锤'), ('age', 25), ('height', 178)])
+for key, value in person.items():
+ print(f'{key}:\t{value}')
+```
+
+字典的`update`方法实现两个字典的合并操作。例如,有两个字典`x`和`y`,当执行`x.update(y)`操作时,`x`跟`y`相同的键对应的值会被`y`中的值更新,而`y`中有但`x`中没有的键值对会直接添加到`x`中,代码如下所示。
+
+```python
+person1 = {'name': '王大锤', 'age': 55, 'height': 178}
+person2 = {'age': 25, 'addr': '成都市武侯区科华北路62号1栋101'}
+person1.update(person2)
+print(person1) # {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'}
+```
+
+如果使用 Python 3.9 及以上的版本,也可以使用`|`运算符来完成同样的操作,代码如下所示。
+
+```python
+person1 = {'name': '王大锤', 'age': 55, 'height': 178}
+person2 = {'age': 25, 'addr': '成都市武侯区科华北路62号1栋101'}
+person1 |= person2
+print(person1) # {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'}
+```
+
+可以通过`pop`或`popitem`方法从字典中删除元素,前者会返回(获得)键对应的值,但是如果字典中不存在指定的键,会引发`KeyError`错误;后者在删除元素时,会返回(获得)键和值组成的二元组。字典的`clear`方法会清空字典中所有的键值对,代码如下所示。
+
+```python
+person = {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'}
+print(person.pop('age')) # 25
+print(person) # {'name': '王大锤', 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'}
+print(person.popitem()) # ('addr', '成都市武侯区科华北路62号1栋101')
+print(person) # {'name': '王大锤', 'height': 178}
+person.clear()
+print(person) # {}
+```
+
+跟列表一样,从字典中删除元素也可以使用`del`关键字,在删除元素的时候如果指定的键索引不到对应的值,一样会引发`KeyError`错误,具体的做法如下所示。
+
+```python
+person = {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'}
+del person['age']
+del person['addr']
+print(person) # {'name': '王大锤', 'height': 178}
+```
+
+### 字典的应用
+
+我们通过几个简单的例子来看看如何使用字典类型解决一些实际的问题。
+
+**例子1**:输入一段话,统计每个英文字母出现的次数,按出现次数从高到低输出。
+
+```python
+sentence = input('请输入一段话: ')
+counter = {}
+for ch in sentence:
+ if 'A' <= ch <= 'Z' or 'a' <= ch <= 'z':
+ counter[ch] = counter.get(ch, 0) + 1
+sorted_keys = sorted(counter, key=counter.get, reverse=True)
+for key in sorted_keys:
+ print(f'{key} 出现了 {counter[key]} 次.')
+```
+
+输入:
+
+```
+Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.
+```
+
+输出:
+
+```
+e 出现了 27 次.
+n 出现了 21 次.
+a 出现了 18 次.
+i 出现了 18 次.
+s 出现了 16 次.
+t 出现了 16 次.
+o 出现了 14 次.
+h 出现了 13 次.
+r 出现了 10 次.
+d 出现了 9 次.
+l 出现了 9 次.
+g 出现了 6 次.
+u 出现了 6 次.
+f 出现了 6 次.
+c 出现了 6 次.
+y 出现了 5 次.
+b 出现了 5 次.
+m 出现了 4 次.
+p 出现了 3 次.
+w 出现了 2 次.
+v 出现了 2 次.
+M 出现了 1 次.
+k 出现了 1 次.
+x 出现了 1 次.
+```
+
+**例子2**:在一个字典中保存了股票的代码和价格,找出股价大于100元的股票并创建一个新的字典。
+
+> **说明**:可以用字典的生成式语法来创建这个新字典。
+
+```python
+stocks = {
+ 'AAPL': 191.88,
+ 'GOOG': 1186.96,
+ 'IBM': 149.24,
+ 'ORCL': 48.44,
+ 'ACN': 166.89,
+ 'FB': 208.09,
+ 'SYMC': 21.29
+}
+stocks2 = {key: value for key, value in stocks.items() if value > 100}
+print(stocks2)
+```
+
+输出:
+
+```
+{'AAPL': 191.88, 'GOOG': 1186.96, 'IBM': 149.24, 'ACN': 166.89, 'FB': 208.09}
+```
+
+### 总结
+
+Python 程序中的字典跟现实生活中字典非常像,允许我们**以键值对的形式保存数据**,再**通过键访问对应的值**。字典是一种非常**有利于数据检索**的数据类型,但是需要再次提醒大家,**字典中的键必须是不可变类型**,列表、集合、字典等类型的数据都不能作为字典的键。
diff --git "a/Day01-20/14.\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227.md" "b/Day01-20/14.\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227.md"
new file mode 100755
index 000000000..8ad8a4d55
--- /dev/null
+++ "b/Day01-20/14.\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227.md"
@@ -0,0 +1,392 @@
+## 函数和模块
+
+在讲解本节课的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解。
+
+$$
+x_{1} + x_{2} + x_{3} + x_{4} = 8
+$$
+
+你可能已经想到了,这个问题其实等同于将 8 个苹果分成四组且每组至少一个苹果有多少种方案,也等价于在分隔 8 个苹果的 7 个间隙之间放入三个隔断将苹果分成四组有多少种方案,所以答案是 $\small{C_{7}^{3} = 35}$ ,其中 $\small{C_{7}^{3}}$ 代表 7 选 3 的组合数,其计算公式如下所示。
+
+$$
+C_m^n = \frac {m!} {n!(m-n)!}
+$$
+
+根据之前学习的知识,我们可以用循环做累乘的方式分别计算出 $\small{m!}$ 、 $\small{n!}$ 和 $\small{(m-n)!}$ ,然后再通过除法运算得到组合数 $\small{C_{m}^{n}}$ ,代码如下所示。
+
+```python
+"""
+输入m和n,计算组合数C(m,n)的值
+
+Version: 1.0
+Author: 骆昊
+"""
+
+m = int(input('m = '))
+n = int(input('n = '))
+# 计算m的阶乘
+fm = 1
+for num in range(1, m + 1):
+ fm *= num
+# 计算n的阶乘
+fn = 1
+for num in range(1, n + 1):
+ fn *= num
+# 计算m-n的阶乘
+fk = 1
+for num in range(1, m - n + 1):
+ fk *= num
+# 计算C(M,N)的值
+print(fm // fn // fk)
+```
+
+输入:
+
+```
+m = 7
+n = 3
+```
+
+输出:
+
+```
+35
+```
+
+不知大家是否注意到,上面的代码中我们做了三次求阶乘的操作,虽然 $\small{m}$ 、 $\small{n}$ 、 $\small{m - n}$ 的值各不相同,但是三段代码并没有实质性的区别,属于重复代码。世界级的编程大师*Martin Fowler*曾经说过:“**代码有很多种坏味道,重复是最坏的一种!**”。要写出高质量的代码,首先就要解决重复代码的问题。对于上面的代码来说,我们可以将求阶乘的功能封装到一个称为“函数”的代码块中,在需要计算阶乘的地方,我们只需“调用函数”即可实现对求阶乘功能的复用。
+
+### 定义函数
+
+数学上的函数通常形如 $\small{y = f(x)}$ 或者 $\small{z = g(x, y)}$ 这样的形式,在 $\small{y = f(x)}$ 中, $\small{f}$ 是函数的名字, $\small{x}$ 是函数的自变量, $\small{y}$ 是函数的因变量;而在 $\small{z = g(x, y)}$ 中, $\small{g}$ 是函数名, $\small{x}$ 和 $\small{y}$ 是函数的自变量, $\small{z}$ 是函数的因变量。Python 中的函数跟这个结构是一致的,每个函数都有自己的名字、自变量和因变量。我们通常把 Python 函数的自变量称为函数的参数,而因变量称为函数的返回值。
+
+Python 中可以使用`def`关键字来定义函数,和变量一样每个函数也应该有一个漂亮的名字,命名规则跟变量的命名规则是一样的(大家赶紧想想我们之前讲过的变量的命名规则)。在函数名后面的圆括号中可以设置函数的参数,也就是我们刚才说的函数的自变量,而函数执行完成后,我们会通过`return`关键字来返回函数的执行结果,这就是我们刚才说的函数的因变量。如果函数中没有`return`语句,那么函数会返回代表空值的`None`。另外,函数也可以没有自变量(参数),但是函数名后面的圆括号是必须有的。一个函数要做的事情(要执行的代码),是通过代码缩进的方式放到函数定义行之后,跟之前分支和循环结构的代码块类似,如下图所示。
+
+
+
+下面,我们将之前代码中求阶乘的操作放到一个函数中,通过这种方式来重构上面的代码。**所谓重构,是在不影响代码执行结果的前提下对代码的结构进行调整**,重构之后的代码如下所示。
+
+```python
+"""
+输入m和n,计算组合数C(m,n)的值
+
+Version: 1.1
+Author: 骆昊
+"""
+
+
+# 通过关键字def定义求阶乘的函数
+# 自变量(参数)num是一个非负整数
+# 因变量(返回值)是num的阶乘
+def fac(num):
+ result = 1
+ for n in range(2, num + 1):
+ result *= n
+ return result
+
+
+m = int(input('m = '))
+n = int(input('n = '))
+# 计算阶乘的时候不需要写重复的代码而是直接调用函数
+# 调用函数的语法是在函数名后面跟上圆括号并传入参数
+print(fac(m) // fac(n) // fac(m - n))
+```
+
+大家可以感受下,上面的代码是不是比之前的版本更加简单优雅。更为重要的是,我们定义的求阶乘函数`fac`还可以在其他需要求阶乘的代码中重复使用。所以,**使用函数可以帮助我们将功能上相对独立且会被重复使用的代码封装起来**,当我们需要这些的代码,不是把重复的代码再编写一遍,而是**通过调用函数实现对既有代码的复用**。事实上,Python 标准库的`math`模块中,已经有一个名为`factorial`的函数实现了求阶乘的功能,我们可以直接用`import math`导入`math`模块,然后使用`math.factorial`来调用求阶乘的函数;我们也可以通过`from math import factorial`直接导入`factorial`函数来使用它,代码如下所示。
+
+```python
+"""
+输入m和n,计算组合数C(m,n)的值
+
+Version: 1.2
+Author: 骆昊
+"""
+from math import factorial
+
+m = int(input('m = '))
+n = int(input('n = '))
+print(factorial(m) // factorial(n) // factorial(m - n))
+```
+
+将来我们使用的函数,要么是自定义的函数,要么是 Python 标准库或者三方库中提供的函数,如果已经有现成的可用的函数,我们就没有必要自己去定义,“**重复发明轮子**”是一件非常糟糕的事情。对于上面的代码,如果你觉得`factorial`这个名字太长,书写代码的时候不是特别方便,我们在导入函数的时候还可以通过`as`关键字为其别名。在调用函数的时候,我们可以用函数的别名,而不再使用它之前的名字,代码如下所示。
+
+```python
+"""
+输入m和n,计算组合数C(m,n)的值
+
+Version: 1.3
+Author: 骆昊
+"""
+from math import factorial as f
+
+m = int(input('m = '))
+n = int(input('n = '))
+print(f(m) // f(n) // f(m - n))
+```
+
+### 函数的参数
+
+#### 位置参数和关键字参数
+
+我们再来写一个函数,根据给出的三条边的长度判断是否可以构成三角形,如果可以构成三角形则返回`True`,否则返回`False`,代码如下所示。
+
+```python
+def make_judgement(a, b, c):
+ """判断三条边的长度能否构成三角形"""
+ return a + b > c and b + c > a and a + c > b
+```
+
+上面`make_judgement`函数有三个参数,这种参数叫做位置参数,在调用函数时通常按照从左到右的顺序依次传入,而且传入参数的数量必须和定义函数时参数的数量相同,如下所示。
+
+```python
+print(make_judgement(1, 2, 3)) # False
+print(make_judgement(4, 5, 6)) # True
+```
+
+如果不想按照从左到右的顺序依次给出`a`、`b`、`c` 三个参数的值,也可以使用关键字参数,通过“参数名=参数值”的形式为函数传入参数,如下所示。
+
+```python
+print(make_judgement(b=2, c=3, a=1)) # False
+print(make_judgement(c=6, b=4, a=5)) # True
+```
+
+在定义函数时,我们可以在参数列表中用`/`设置**强制位置参数**(*positional-only arguments*),用`*`设置**命名关键字参数**。所谓强制位置参数,就是调用函数时只能按照参数位置来接收参数值的参数;而命名关键字参数只能通过“参数名=参数值”的方式来传递和接收参数,大家可以看看下面的例子。
+
+```python
+# /前面的参数是强制位置参数
+def make_judgement(a, b, c, /):
+ """判断三条边的长度能否构成三角形"""
+ return a + b > c and b + c > a and a + c > b
+
+
+# 下面的代码会产生TypeError错误,错误信息提示“强制位置参数是不允许给出参数名的”
+# TypeError: make_judgement() got some positional-only arguments passed as keyword arguments
+# print(make_judgement(b=2, c=3, a=1))
+```
+
+> **说明**:强制位置参数是 Python 3.8 引入的新特性,在使用低版本的 Python 解释器时需要注意。
+
+```python
+# *后面的参数是命名关键字参数
+def make_judgement(*, a, b, c):
+ """判断三条边的长度能否构成三角形"""
+ return a + b > c and b + c > a and a + c > b
+
+
+# 下面的代码会产生TypeError错误,错误信息提示“函数没有位置参数但却给了3个位置参数”
+# TypeError: make_judgement() takes 0 positional arguments but 3 were given
+# print(make_judgement(1, 2, 3))
+```
+
+#### 参数的默认值
+
+Python 中允许函数的参数拥有默认值,我们可以把之前讲过的一个例子“CRAPS赌博游戏”(《第07课:分支和循环结构的应用》)中摇色子获得点数的功能封装到函数中,代码如下所示。
+
+```python
+from random import randrange
+
+
+# 定义摇色子的函数
+# 函数的自变量(参数)n表示色子的个数,默认值为2
+# 函数的因变量(返回值)表示摇n颗色子得到的点数
+def roll_dice(n=2):
+ total = 0
+ for _ in range(n):
+ total += randrange(1, 7)
+ return total
+
+
+# 如果没有指定参数,那么n使用默认值2,表示摇两颗色子
+print(roll_dice())
+# 传入参数3,变量n被赋值为3,表示摇三颗色子获得点数
+print(roll_dice(3))
+```
+
+我们再来看一个更为简单的例子。
+
+```python
+def add(a=0, b=0, c=0):
+ """三个数相加求和"""
+ return a + b + c
+
+
+# 调用add函数,没有传入参数,那么a、b、c都使用默认值0
+print(add()) # 0
+# 调用add函数,传入一个参数,该参数赋值给变量a, 变量b和c使用默认值0
+print(add(1)) # 1
+# 调用add函数,传入两个参数,分别赋值给变量a和b,变量c使用默认值0
+print(add(1, 2)) # 3
+# 调用add函数,传入三个参数,分别赋值给a、b、c三个变量
+print(add(1, 2, 3)) # 6
+```
+
+需要注意的是,**带默认值的参数必须放在不带默认值的参数之后**,否则将产生`SyntaxError`错误,错误消息是:`non-default argument follows default argument`,翻译成中文的意思是“没有默认值的参数放在了带默认值的参数后面”。
+
+#### 可变参数
+
+Python 语言中可以通过星号表达式语法让函数支持可变参数。所谓可变参数指的是在调用函数时,可以向函数传入`0`个或任意多个参数。将来我们以团队协作的方式开发商业项目时,很有可能要设计函数给其他人使用,但有的时候我们并不知道函数的调用者会向该函数传入多少个参数,这个时候可变参数就能派上用场。
+
+下面的代码演示了如何使用可变位置参数实现对任意多个数求和的`add`函数,调用函数时传入的参数会保存到一个元组,通过对该元组的遍历,可以获取传入函数的参数。
+
+```python
+# 用星号表达式来表示args可以接收0个或任意多个参数
+# 调用函数时传入的n个参数会组装成一个n元组赋给args
+# 如果一个参数都没有传入,那么args会是一个空元组
+def add(*args):
+ total = 0
+ # 对保存可变参数的元组进行循环遍历
+ for val in args:
+ # 对参数进行了类型检查(数值型的才能求和)
+ if type(val) in (int, float):
+ total += val
+ return total
+
+
+# 在调用add函数时可以传入0个或任意多个参数
+print(add()) # 0
+print(add(1)) # 1
+print(add(1, 2, 3)) # 6
+print(add(1, 2, 'hello', 3.45, 6)) # 12.45
+```
+
+如果我们希望通过“参数名=参数值”的形式传入若干个参数,具体有多少个参数也是不确定的,我们还可以给函数添加可变关键字参数,把传入的关键字参数组装到一个字典中,代码如下所示。
+
+```python
+# 参数列表中的**kwargs可以接收0个或任意多个关键字参数
+# 调用函数时传入的关键字参数会组装成一个字典(参数名是字典中的键,参数值是字典中的值)
+# 如果一个关键字参数都没有传入,那么kwargs会是一个空字典
+def foo(*args, **kwargs):
+ print(args)
+ print(kwargs)
+
+
+foo(3, 2.1, True, name='骆昊', age=43, gpa=4.95)
+```
+
+输出:
+
+```
+(3, 2.1, True)
+{'name': '骆昊', 'age': 43, 'gpa': 4.95}
+```
+
+### 用模块管理函数
+
+不管用什么样的编程语言来写代码,给变量、函数起名字都是一个让人头疼的问题,因为我们会遇到**命名冲突**这种尴尬的情况。最简单的场景就是在同一个`.py`文件中定义了两个同名的函数,如下所示。
+
+```python
+def foo():
+ print('hello, world!')
+
+
+def foo():
+ print('goodbye, world!')
+
+
+foo() # 大家猜猜调用foo函数会输出什么
+```
+
+当然上面的这种情况我们很容易就能避免,但是如果项目是团队协作多人开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,这种情况下怎么解决命名冲突呢?答案其实很简单,Python 中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候,我们通过`import`关键字导入指定的模块再使用**完全限定名**(`模块名.函数名`)的调用方式,就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。
+
+`module1.py`
+
+```python
+def foo():
+ print('hello, world!')
+```
+
+`module2.py`
+
+```python
+def foo():
+ print('goodbye, world!')
+```
+
+`test.py`
+
+```python
+import module1
+import module2
+
+# 用“模块名.函数名”的方式(完全限定名)调用函数,
+module1.foo() # hello, world!
+module2.foo() # goodbye, world!
+```
+
+在导入模块时,还可以使用`as`关键字对模块进行别名,这样我们可以使用更为简短的完全限定名。
+
+`test.py`
+
+```python
+import module1 as m1
+import module2 as m2
+
+m1.foo() # hello, world!
+m2.foo() # goodbye, world!
+```
+
+上面两段代码,我们导入的是定义函数的模块,我们也可以使用`from...import...`语法从模块中直接导入需要使用的函数,代码如下所示。
+
+`test.py`
+
+```python
+from module1 import foo
+
+foo() # hello, world!
+
+from module2 import foo
+
+foo() # goodbye, world!
+```
+
+但是,如果我们如果从两个不同的模块中导入了同名的函数,后面导入的函数会替换掉之前的导入,就像下面的代码,调用`foo`会输出`goodbye, world!`,因为我们先导入了`module1`的`foo`,后导入了`module2`的`foo` 。如果两个`from...import...`反过来写,那就是另外一番光景了。
+
+`test.py`
+
+```python
+from module1 import foo
+from module2 import foo
+
+foo() # goodbye, world!
+```
+
+如果想在上面的代码中同时使用来自两个模块的`foo`函数还是有办法的,大家可能已经猜到了,还是用`as`关键字对导入的函数进行别名,代码如下所示。
+
+`test.py`
+
+```python
+from module1 import foo as f1
+from module2 import foo as f2
+
+f1() # hello, world!
+f2() # goodbye, world!
+```
+
+### 标准库中的模块和函数
+
+Python 标准库中提供了大量的模块和函数来简化我们的开发工作,我们之前用过的`random`模块就为我们提供了生成随机数和进行随机抽样的函数;而`time`模块则提供了和时间操作相关的函数;我们之前用到过的`math`模块中还包括了计算正弦、余弦、指数、对数等一系列的数学函数。随着我们深入学习 Python 语言,我们还会用到更多的模块和函数。
+
+Python 标准库中还有一类函数是不需要`import`就能够直接使用的,我们将其称之为**内置函数**,这些内置函数不仅有用而且还很常用,下面的表格列出了一部分的内置函数。
+
+| 函数 | 说明 |
+| ------- | ------------------------------------------------------------ |
+| `abs` | 返回一个数的绝对值,例如:`abs(-1.3)`会返回`1.3`。 |
+| `bin` | 把一个整数转换成以`'0b'`开头的二进制字符串,例如:`bin(123)`会返回`'0b1111011'`。 |
+| `chr` | 将Unicode编码转换成对应的字符,例如:`chr(8364)`会返回`'€'`。 |
+| `hex` | 将一个整数转换成以`'0x'`开头的十六进制字符串,例如:`hex(123)`会返回`'0x7b'`。 |
+| `input` | 从输入中读取一行,返回读到的字符串。 |
+| `len` | 获取字符串、列表等的长度。 |
+| `max` | 返回多个参数或一个可迭代对象中的最大值,例如:`max(12, 95, 37)`会返回`95`。 |
+| `min` | 返回多个参数或一个可迭代对象中的最小值,例如:`min(12, 95, 37)`会返回`12`。 |
+| `oct` | 把一个整数转换成以`'0o'`开头的八进制字符串,例如:`oct(123)`会返回`'0o173'`。 |
+| `open` | 打开一个文件并返回文件对象。 |
+| `ord` | 将字符转换成对应的Unicode编码,例如:`ord('€')`会返回`8364`。 |
+| `pow` | 求幂运算,例如:`pow(2, 3)`会返回`8`;`pow(2, 0.5)`会返回`1.4142135623730951`。 |
+| `print` | 打印输出。 |
+| `range` | 构造一个范围序列,例如:`range(100)`会产生`0`到`99`的整数序列。 |
+| `round` | 按照指定的精度对数值进行四舍五入,例如:`round(1.23456, 4)`会返回`1.2346`。 |
+| `sum` | 对一个序列中的项从左到右进行求和运算,例如:`sum(range(1, 101))`会返回`5050`。 |
+| `type` | 返回对象的类型,例如:`type(10)`会返回`int`;而` type('hello')`会返回`str`。 |
+
+### 总结
+
+**函数是对功能相对独立且会重复使用的代码的封装**。学会使用定义和使用函数,就能够写出更为优质的代码。当然,Python 语言的标准库中已经为我们提供了大量的模块和常用的函数,用好这些模块和函数就能够用更少的代码做更多的事情;如果这些模块和函数不能满足我们的要求,可能就需要自定义函数,然后再通过模块的概念来管理这些自定义函数。
\ No newline at end of file
diff --git "a/Day01-20/15.\345\207\275\346\225\260\345\272\224\347\224\250\345\256\236\346\210\230.md" "b/Day01-20/15.\345\207\275\346\225\260\345\272\224\347\224\250\345\256\236\346\210\230.md"
new file mode 100755
index 000000000..a553c80ba
--- /dev/null
+++ "b/Day01-20/15.\345\207\275\346\225\260\345\272\224\347\224\250\345\256\236\346\210\230.md"
@@ -0,0 +1,231 @@
+## 函数应用实战
+
+### 例子1:随机验证码
+
+设计一个生成随机验证码的函数,验证码由数字和英文大小写字母构成,长度可以通过参数设置。
+
+```python
+import random
+import string
+
+ALL_CHARS = string.digits + string.ascii_letters
+
+
+def generate_code(*, code_len=4):
+ """
+ 生成指定长度的验证码
+ :param code_len: 验证码的长度(默认4个字符)
+ :return: 由大小写英文字母和数字构成的随机验证码字符串
+ """
+ return ''.join(random.choices(ALL_CHARS, k=code_len))
+```
+
+> **说明1**:`string`模块的`digits`代表0到9的数字构成的字符串`'0123456789'`,`string`模块的`ascii_letters`代表大小写英文字母构成的字符串`'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'`。
+>
+> **说明2**:`random`模块的`sample`和`choices`函数都可以实现随机抽样,`sample`实现无放回抽样,这意味着抽样取出的元素是不重复的;`choices`实现有放回抽样,这意味着可能会重复选中某些元素。这两个函数的第一个参数代表抽样的总体,而参数`k`代表样本容量,需要说明的是`choices`函数的参数`k`是一个命名关键字参数,在传参时必须指定参数名。
+
+可以用下面的代码生成5组随机验证码来测试上面的函数。
+
+```python
+for _ in range(5):
+ print(generate_code())
+```
+
+输出:
+
+```
+59tZ
+QKU5
+izq8
+IBBb
+jIfX
+```
+
+或者
+
+```python
+for _ in range(5):
+ print(generate_code(code_len=6))
+```
+
+输出:
+
+```
+FxJucw
+HS4H9G
+0yyXfz
+x7fohf
+ReO22w
+```
+
+> **说明**:我们设计的`generate_code`函数的参数是命名关键字参数,由于它有默认值,可以不给它传值,使用默认值4。如果需要给函数传入参数,必须指定参数名`code_len`。
+
+### 例子2:判断素数
+
+设计一个判断给定的大于1的正整数是不是质数的函数。质数是只能被1和自身整除的正整数(大于1),如果一个大于 1 的正整数 $\small{N}$ 是质数,那就意味着在 2 到 $\small{N-1}$ 之间都没有它的因子。
+
+```python
+def is_prime(num: int) -> bool:
+ """
+ 判断一个正整数是不是质数
+ :param num: 大于1的正整数
+ :return: 如果num是质数返回True,否则返回False
+ """
+ for i in range(2, int(num ** 0.5) + 1):
+ if num % i == 0:
+ return False
+ return True
+```
+
+> **说明1**:上面`is_prime`函数的参数`num`后面的`: int`用来标注参数的类型,虽然它对代码的执行结果不产生任何影响,但是很好的增强了代码的可读性。同理,参数列表后面的`-> bool`用来标注函数返回值的类型,它也不会对代码的执行结果产生影响,但是却让我们清楚的知道,调用函数会得到一个布尔值,要么是`True`,要么是`False`。
+>
+> **说明2**:上面的循环并不需要从 2 循环到 $\small{N-1}$ ,因为如果循环进行到 $\small{\sqrt{N}}$ 时,还没有找到$\small{N}$的因子,那么 $\small{\sqrt{N}}$ 之后也不会出现 $\small{N}$ 的因子,大家可以自己想一想这是为什么。
+
+### 例子3:最大公约数和最小公倍数
+
+设计计算两个正整数最大公约数和最小公倍数的函数。 $\small{x}$ 和 $\small{y}$ 的最大公约数是能够同时整除 $\small{x}$ 和 $\small{y}$ 的最大整数,如果 $\small{x}$ 和 $\small{y}$ 互质,那么它们的最大公约数为 1; $\small{x}$ 和 $\small{y}$ 的最小公倍数是能够同时被 $\small{x}$ 和 $\small{y}$ 整除的最小正整数,如果 $\small{x}$ 和 $\small{y}$ 互质,那么它们的最小公倍数为 $\small{x \times y}$ 。需要提醒大家注意的是,计算最大公约数和最小公倍数是两个不同的功能,应该设计成两个函数,而不是把两个功能放到同一个函数中。
+
+```python
+def lcm(x: int, y: int) -> int:
+ """求最小公倍数"""
+ return x * y // gcd(x, y)
+
+
+def gcd(x: int, y: int) -> int:
+ """求最大公约数"""
+ while y % x != 0:
+ x, y = y % x, x
+ return x
+```
+
+> **说明**:函数之间可以相互调用,上面求最小公倍数的`lcm`函数调用了求最大公约数的`gcd`函数,通过 $\frac{x \times y}{ gcd(x, y)}$ 来计算最小公倍数。
+
+### 例子4:数据统计
+
+假设样本数据保存一个列表中,设计计算样本数据描述性统计信息的函数。描述性统计信息通常包括:算术平均值、中位数、极差(最大值和最小值的差)、方差、标准差、变异系数等,计算公式如下所示。
+
+样本均值(sample mean):
+
+$$
+\bar{x} = \frac{\sum_{i=1}^{n}x_{i}}{n} = \frac{x_{1}+x_{2}+\cdots +x_{n}}{n}
+$$
+
+样本方差(sample variance):
+
+$$
+s^2 = \frac {\sum_{i=1}^{n}(x_i - \bar{x})^2} {n-1}
+$$
+
+样本标准差(sample standard deviation):
+
+$$
+s = \sqrt{\frac{\sum_{i=1}^{n}(x_i - \bar{x})^2}{n-1}}
+$$
+
+变异系数(coefficient of sample variation):
+
+$$
+CV = \frac{s}{\bar{x}}
+$$
+
+```python
+def ptp(data):
+ """极差(全距)"""
+ return max(data) - min(data)
+
+
+def mean(data):
+ """算术平均"""
+ return sum(data) / len(data)
+
+
+def median(data):
+ """中位数"""
+ temp, size = sorted(data), len(data)
+ if size % 2 != 0:
+ return temp[size // 2]
+ else:
+ return mean(temp[size // 2 - 1:size // 2 + 1])
+
+
+def var(data, ddof=1):
+ """方差"""
+ x_bar = mean(data)
+ temp = [(num - x_bar) ** 2 for num in data]
+ return sum(temp) / (len(temp) - ddof)
+
+
+def std(data, ddof=1):
+ """标准差"""
+ return var(data, ddof) ** 0.5
+
+
+def cv(data, ddof=1):
+ """变异系数"""
+ return std(data, ddof) / mean(data)
+
+
+def describe(data):
+ """输出描述性统计信息"""
+ print(f'均值: {mean(data)}')
+ print(f'中位数: {median(data)}')
+ print(f'极差: {ptp(data)}')
+ print(f'方差: {var(data)}')
+ print(f'标准差: {std(data)}')
+ print(f'变异系数: {cv(data)}')
+```
+
+> **说明1**:中位数是将数据按照升序或降序排列后位于中间的数,它描述了数据的中等水平。中位数的计算分两种情况:当数据体量$n$为奇数时,中位数是位于 $\frac{n + 1}{2}$ 位置的元素;当数据体量 $\small{n}$ 为偶数时,中位数是位于 $\frac{n}{2}$ 和 $\frac{n}{2} + 1$ 两个位置元素的均值。
+>
+> **说明2**:计算方差和标准差的函数中有一个名为`ddof`的参数,它代表了可以调整的自由度,默认值为 1。在计算样本方差和样本标准差时,需要进行自由度校正;如果要计算总体方差和总体标准差,可以将`ddof`参数赋值为 0,即不需要进行自由度校正。
+>
+> **说明3**:`describe`函数将上面封装好的统计函数组装到一起,用于输出数据的描述性统计信息。事实上,Python 标准库中有一个名为`statistics`的模块,它已经把获取描述性统计信息的函数封装好了,有兴趣的读者可以自行了解。
+
+### 例子5:双色球随机选号
+
+我们用函数重构之前讲过的双色球随机选号的例子(《第09课:常用数据结构之列表-2》),将生成随机号码和输出一组号码的功能分别封装到两个函数中,然后通过调用函数实现机选`N`注号码的功能。
+
+```python
+"""
+双色球随机选号程序
+
+Author: 骆昊
+Version: 1.3
+"""
+import random
+
+RED_BALLS = [i for i in range(1, 34)]
+BLUE_BALLS = [i for i in range(1, 17)]
+
+
+def choose():
+ """
+ 生成一组随机号码
+ :return: 保存随机号码的列表
+ """
+ selected_balls = random.sample(RED_BALLS, 6)
+ selected_balls.sort()
+ selected_balls.append(random.choice(BLUE_BALLS))
+ return selected_balls
+
+
+def display(balls):
+ """
+ 格式输出一组号码
+ :param balls: 保存随机号码的列表
+ """
+ for ball in balls[:-1]:
+ print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
+ print(f'\033[034m{balls[-1]:0>2d}\033[0m')
+
+
+n = int(input('生成几注号码: '))
+for _ in range(n):
+ display(choose())
+```
+
+> **说明**:大家看看`display(choose())`这行代码,这里我们先通过`choose`函数获得一组随机号码,然后把`choose`函数的返回值作为`display`函数的参数,通过`display`函数将选中的随机号码显示出来。重构之后的代码逻辑非常清晰,代码的可读性更强了。如果有人为你封装了这两个函数,你仅仅是函数的调用者,其实你根本不用关心`choose`函数和`display`函数的内部实现,你只需要知道调用`choose`函数可以生成一组随机号码,而调用`display`函数传入一个列表,就可以输出这组号码。将来我们使用各种各样的 Python 三方库时,我们也根本不关注它们的底层实现,我们需要知道的仅仅是调用哪个函数可以解决问题。
+
+### 总结
+
+在写代码尤其是开发商业项目的时候,一定要有意识的**将相对独立且重复使用的功能封装成函数**,这样不管是自己还是团队的其他成员都可以通过调用函数的方式来使用这些功能,减少工作中那些重复且乏味的劳动。
\ No newline at end of file
diff --git "a/Day01-20/16.\345\207\275\346\225\260\344\275\277\347\224\250\350\277\233\351\230\266.md" "b/Day01-20/16.\345\207\275\346\225\260\344\275\277\347\224\250\350\277\233\351\230\266.md"
new file mode 100755
index 000000000..692144d66
--- /dev/null
+++ "b/Day01-20/16.\345\207\275\346\225\260\344\275\277\347\224\250\350\277\233\351\230\266.md"
@@ -0,0 +1,165 @@
+## 函数使用进阶
+
+我们继续探索定义和使用函数的相关知识。通过前面的学习,我们知道了函数有自变量(参数)和因变量(返回值),自变量可以是任意的数据类型,因变量也可以是任意的数据类型,那么这里就有一个小问题,我们能不能用函数作为函数的参数,用函数作为函数的返回值?这里我们先说结论:**Python 中的函数是“一等函数”**,所谓“一等函数”指的就是函数可以赋值给变量,函数可以作为函数的参数,函数也可以作为函数的返回值。把一个函数作为其他函数的参数或返回值的用法,我们通常称之为“高阶函数”。
+
+### 高阶函数
+
+我们回到之前讲过的一个例子,设计一个函数,传入任意多个参数,对其中`int`类型或`float`类型的元素实现求和操作。我们对之前的代码稍作调整,让整个代码更加紧凑一些,如下所示。
+
+```python
+def calc(*args, **kwargs):
+ items = list(args) + list(kwargs.values())
+ result = 0
+ for item in items:
+ if type(item) in (int, float):
+ result += item
+ return result
+```
+
+如果我们希望上面的`calc`函数不仅仅可以做多个参数的求和,还可以实现更多的甚至是自定义的二元运算,我们该怎么做呢?上面的代码只能求和是因为函数中使用了`+=`运算符,这使得函数跟加法运算形成了耦合关系,如果能解除这种耦合关系,函数的通用性和灵活性就会更好。解除耦合的办法就是将`+`运算符变成函数调用,并将其设计为函数的参数,代码如下所示。
+
+```python
+def calc(init_value, op_func, *args, **kwargs):
+ items = list(args) + list(kwargs.values())
+ result = init_value
+ for item in items:
+ if type(item) in (int, float):
+ result = op_func(result, item)
+ return result
+```
+
+注意,上面的函数增加了两个参数,其中`init_value`代表运算的初始值,`op_func`代表二元运算函数,为了调用修改后的函数,我们先定义做加法和乘法运算的函数,代码如下所示。
+
+```python
+def add(x, y):
+ return x + y
+
+
+def mul(x, y):
+ return x * y
+```
+
+如果要做求和的运算,我们可以按照下面的方式调用`calc`函数。
+
+```python
+print(calc(0, add, 1, 2, 3, 4, 5)) # 15
+```
+
+如果要做求乘积运算,我们可以按照下面的方式调用`calc`函数。
+
+```python
+print(calc(1, mul, 1, 2, 3, 4, 5)) # 120
+```
+
+上面的`calc`函数通过将运算符变成函数的参数,实现了跟加法运算耦合,这是一种非常高明和实用的编程技巧,但对于最初学者来说可能会觉得难以理解,建议大家细品一下。需要注意上面的代码中,将函数作为参数传入其他函数和直接调用函数是有显著的区别的,**调用函数需要在函数名后面跟上圆括号,而把函数作为参数时只需要函数名即可**。
+
+如果我们没有提前定义好`add`和`mul`函数,也可以使用 Python 标准库中的`operator`模块提供的`add`和`mul`函数,它们分别代表了做加法和做乘法的二元运算,我们拿过来直接使用即可,代码如下所示。
+
+```python
+import operator
+
+print(calc(0, operator.add, 1, 2, 3, 4, 5)) # 15
+print(calc(1, operator.mul, 1, 2, 3, 4, 5)) # 120
+```
+
+Python 内置函数中有不少高阶函数,我们前面提到过的`filter`和`map`函数就是高阶函数,前者可以实现对序列中元素的过滤,后者可以实现对序列中元素的映射,例如我们要去掉一个整数列表中的奇数,并对所有的偶数求平方得到一个新的列表,就可以直接使用这两个函数来做到,具体的做法是如下所示。
+
+```python
+def is_even(num):
+ """判断num是不是偶数"""
+ return num % 2 == 0
+
+
+def square(num):
+ """求平方"""
+ return num ** 2
+
+
+old_nums = [35, 12, 8, 99, 60, 52]
+new_nums = list(map(square, filter(is_even, old_nums)))
+print(new_nums) # [144, 64, 3600, 2704]
+```
+
+当然,要完成上面代码的功能,也可以使用列表生成式,列表生成式的做法更为简单优雅。
+
+```python
+old_nums = [35, 12, 8, 99, 60, 52]
+new_nums = [num ** 2 for num in old_nums if num % 2 == 0]
+print(new_nums) # [144, 64, 3600, 2704]
+```
+
+我们再来讨论一个内置函数`sorted`,它可以实现对容器型数据类型(如:列表、字典等)元素的排序。我们之前讲过`list`类型的`sort`方法,它实现了对列表元素的排序,`sorted`函数从功能上来讲跟列表的`sort`方法没有区别,但它会返回排序后的列表对象,而不是直接修改原来的列表,这一点我们称为**函数的无副作用设计**,也就是说调用函数除了产生返回值以外,不会对程序的状态或外部环境产生任何其他的影响。使用`sorted`函数排序时,可以通过高阶函数的形式自定义排序的规则,我们通过下面的例子加以说明。
+
+```python
+old_strings = ['in', 'apple', 'zoo', 'waxberry', 'pear']
+new_strings = sorted(old_strings)
+print(new_strings) # ['apple', 'in', 'pear', waxberry', 'zoo']
+```
+
+上面的代码对大家来说并不陌生,但是如果希望根据字符串的长度而不是字母表顺序对列表元素排序,我们可以向`sorted`函数传入一个名为`key`的参数,将`key`参数赋值为获取字符串长度的函数`len`,这个函数我们在之前的课程中讲到过,代码如下所示。
+
+```python
+old_strings = ['in', 'apple', 'zoo', 'waxberry', 'pear']
+new_strings = sorted(old_strings, key=len)
+print(new_strings) # ['in', 'zoo', 'pear', 'apple', 'waxberry']
+```
+
+> **说明**:列表类型的`sort`方法也有同样的`key`参数,有兴趣的读者可以自行尝试。
+
+### Lambda函数
+
+在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,也不需要考虑对函数的复用,那么我们可以使用 lambda 函数。Python 中的 lambda 函数是没有的名字函数,所以很多人也把它叫做**匿名函数**,lambda 函数只能有一行代码,代码中的表达式产生的运算结果就是这个匿名函数的返回值。之前的代码中,我们写的`is_even`和`square`函数都只有一行代码,我们可以考虑用 lambda 函数来替换掉它们,代码如下所示。
+
+```python
+old_nums = [35, 12, 8, 99, 60, 52]
+new_nums = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, old_nums)))
+print(new_nums) # [144, 64, 3600, 2704]
+```
+
+通过上面的代码可以看出,定义 lambda 函数的关键字是`lambda`,后面跟函数的参数,如果有多个参数用逗号进行分隔;冒号后面的部分就是函数的执行体,通常是一个表达式,表达式的运算结果就是 lambda 函数的返回值,不需要写`return` 关键字。
+
+前面我们说过,Python 中的函数是“一等函数”,函数是可以直接赋值给变量的。在学习了 lambda 函数之后,前面我们写过的一些函数就可以用一行代码来实现它们了,大家可以看看能否理解下面的求阶乘和判断素数的函数。
+
+```python
+import functools
+import operator
+
+# 用一行代码实现计算阶乘的函数
+fac = lambda n: functools.reduce(operator.mul, range(2, n + 1), 1)
+
+# 用一行代码实现判断素数的函数
+is_prime = lambda x: all(map(lambda f: x % f, range(2, int(x ** 0.5) + 1)))
+
+# 调用Lambda函数
+print(fac(6)) # 720
+print(is_prime(37)) # True
+```
+
+> **提示1**:上面使用的`reduce`函数是 Python 标准库`functools`模块中的函数,它可以实现对一组数据的归约操作,类似于我们之前定义的`calc`函数,第一个参数是代表运算的函数,第二个参数是运算的数据,第三个参数是运算的初始值。很显然,`reduce`函数也是高阶函数,它和`filter`函数、`map`函数一起构成了处理数据中非常关键的三个动作:**过滤**、**映射**和**归约**。
+>
+> **提示2**:上面判断素数的 lambda 函数通过`range`函数构造了从 2 到 $\small{\sqrt{x}}$ 的范围,检查这个范围有没有`x`的因子。`all`函数也是 Python 内置函数,如果传入的序列中所有的布尔值都是`True`,`all`函数返回`True`,否则`all`函数返回`False`。
+
+### 偏函数
+
+偏函数是指固定函数的某些参数,生成一个新的函数,这样就无需在每次调用函数时都传递相同的参数。在 Python 语言中,我们可以使用`functools`模块的`partial`函数来创建偏函数。例如,`int`函数在默认情况下可以将字符串视为十进制整数进行类型转换,如果我们修修改它的`base`参数,就可以定义出三个新函数,分别用于将二进制、八进制、十六进制字符串转换为整数,代码如下所示。
+
+```python
+import functools
+
+int2 = functools.partial(int, base=2)
+int8 = functools.partial(int, base=8)
+int16 = functools.partial(int, base=16)
+
+print(int('1001')) # 1001
+
+print(int2('1001')) # 9
+print(int8('1001')) # 513
+print(int16('1001')) # 4097
+```
+
+不知大家是否注意到,`partial`函数的第一个参数和返回值都是函数,它将传入的函数处理成一个新的函数返回。通过构造偏函数,我们可以结合实际的使用场景将原函数变成使用起来更为便捷的新函数,不知道大家有没有觉得这波操作很有意思。
+
+### 总结
+
+Python 中的函数是一等函数,可以赋值给变量,也可以作为函数的参数和返回值,这也就意味着我们可以在 Python 中使用高阶函数。高阶函数的概念对新手并不友好,但它却带来了函数设计上的灵活性。如果我们要定义的函数非常简单,只有一行代码,而且不需要函数名来复用它,我们可以使用 lambda 函数。
+
diff --git "a/Day01-20/17.\345\207\275\346\225\260\351\253\230\347\272\247\345\272\224\347\224\250.md" "b/Day01-20/17.\345\207\275\346\225\260\351\253\230\347\272\247\345\272\224\347\224\250.md"
new file mode 100755
index 000000000..0a5fbcbb7
--- /dev/null
+++ "b/Day01-20/17.\345\207\275\346\225\260\351\253\230\347\272\247\345\272\224\347\224\250.md"
@@ -0,0 +1,256 @@
+## 函数高级应用
+
+在上一个章节中,我们探索了 Python 中的高阶函数,相信大家对函数的定义和应用有了更深刻的认知。本章我们继续为大家讲解函数相关的知识,一个是 Python 中的特色语法装饰器,一个是函数的递归调用。
+
+### 装饰器
+
+Python 语言中,装饰器是“**用一个函数装饰另外一个函数并为其提供额外的能力**”的语法现象。装饰器本身是一个函数,它的参数是被装饰的函数,它的返回值是一个带有装饰功能的函数。通过前面的描述,相信大家已经听出来了,装饰器是一个高阶函数,它的参数和返回值都是函数。但是,装饰器的概念对编程语言的初学者来说,还是让人头疼的,下面我们先通过一个简单的例子来说明装饰器的作用。假设有名为`downlaod`和`upload`的两个函数,分别用于文件的上传和下载,如下所示。
+
+```python
+import random
+import time
+
+
+def download(filename):
+ """下载文件"""
+ print(f'开始下载{filename}.')
+ time.sleep(random.random() * 6)
+ print(f'{filename}下载完成.')
+
+
+def upload(filename):
+ """上传文件"""
+ print(f'开始上传{filename}.')
+ time.sleep(random.random() * 8)
+ print(f'{filename}上传完成.')
+
+
+download('MySQL从删库到跑路.avi')
+upload('Python从入门到住院.pdf')
+```
+
+> **说明**:上面的代码用休眠一段随机时间的方式模拟了下载和上传文件需要花费一定的时间,并没有真正的联网上传下载文件。用 Python 语言实现联网上传下载文件也非常简单,后面我们会讲到相关的知识。
+
+现在有一个新的需求,我们希望知道调用`download`和`upload`函数上传下载文件到底用了多少时间,这应该如何实现呢?相信很多小伙伴已经想到了,我们可以在函数开始执行的时候记录一个时间,在函数调用结束后记录一个时间,两个时间相减就可以计算出下载或上传的时间,代码如下所示。
+
+```python
+start = time.time()
+download('MySQL从删库到跑路.avi')
+end = time.time()
+print(f'花费时间: {end - start:.2f}秒')
+start = time.time()
+upload('Python从入门到住院.pdf')
+end = time.time()
+print(f'花费时间: {end - start:.2f}秒')
+```
+
+通过上面的代码,我们可以在下载和上传文件时记录下耗费的时间,但不知道大家是否注意到,上面记录时间、计算和显示执行时间的代码都是重复代码。有编程经验的人都知道,**重复的代码是万恶之源**,那么有没有办法在不写重复代码的前提下,用一种简单优雅的方式记录下函数的执行时间呢?在 Python 语言中,装饰器就是解决这类问题的最佳选择。通过装饰器语法,我们可以把跟原来的业务(上传和下载)没有关系计时功能的代码封装到一个函数中,如果`upload`和`download`函数需要记录时间,我们直接把装饰器作用到这两个函数上即可。既然上面提到了,装饰器是一个高阶函数,它的参数和返回值都是函数,我们将记录时间的装饰器姑且命名为`record_time`,那么它的整体结构应该如下面的代码所示。
+
+```python
+def record_time(func):
+
+ def wrapper(*args, **kwargs):
+
+ result = func(*args, **kwargs)
+
+ return result
+
+ return wrapper
+```
+
+相信大家注意到了,`record_time`函数的参数`func`代表了一个被装饰的函数,函数里面定义的`wrapper`函数是带有装饰功能的函数,它会执行被装饰的函数`func`,它还需要返回在最后产生函数执行的返回值。不知大家是否留意到,上面的代码我在第4行和第6行留下了两个空行,这意味着我们可以这些地方添加代码来实现额外的功能。`record_time`函数最终会返回这个带有装饰功能的函数`wrapper`并通过它替代原函数`func`,当原函数`func`被`record_time`函数装饰后,我们调用它时其实调用的是`wrapper`函数,所以才获得了额外的能力。`wrapper`函数的参数比较特殊,由于我们要用`wrapper`替代原函数`func`,但是我们又不清楚原函数`func`会接受哪些参数,所以我们就通过可变参数和关键字参数照单全收,然后在调用`func`的时候,原封不动的全部给它。这里还要强调一下,Python 语言支持函数的嵌套定义,就像上面,我们可以在`record_time`函数中定义`wrapper`函数,这个操作在很多编程语言中并不被支持。
+
+看懂这个结构后,我们就可以把记录时间的功能写到这个装饰器中,代码如下所示。
+
+```python
+import time
+
+
+def record_time(func):
+
+ def wrapper(*args, **kwargs):
+ # 在执行被装饰的函数之前记录开始时间
+ start = time.time()
+ # 执行被装饰的函数并获取返回值
+ result = func(*args, **kwargs)
+ # 在执行被装饰的函数之后记录结束时间
+ end = time.time()
+ # 计算和显示被装饰函数的执行时间
+ print(f'{func.__name__}执行时间: {end - start:.2f}秒')
+ # 返回被装饰函数的返回值
+ return result
+
+ return wrapper
+```
+
+写装饰器虽然颇费周折,但是这是个一劳永逸的骚操作,将来再有记录函数执行时间的需求时,我们只需要添加上面的装饰器即可。使用上面的装饰器函数有两种方式,第一种方式就是直接调用装饰器函数,传入被装饰的函数并获得返回值,我们可以用这个返回值直接替代原来的函数,那么在调用时就已经获得了装饰器提供的额外的能力(记录执行时间),大家试试下面的代码就明白了。
+
+```python
+download = record_time(download)
+upload = record_time(upload)
+download('MySQL从删库到跑路.avi')
+upload('Python从入门到住院.pdf')
+```
+
+在 Python 中,使用装饰器很有更为便捷的**语法糖**(编程语言中添加的某种语法,这种语法对语言的功能没有影响,但是使用更加方法,代码的可读性也更强,我们将其称之为“语法糖”或“糖衣语法”),可以用`@装饰器函数`将装饰器函数直接放在被装饰的函数上,效果跟上面的代码相同。我们把完整的代码为大家罗列出来,大家可以再看看我们是如何定义和使用装饰器的。
+
+```python
+import random
+import time
+
+
+def record_time(func):
+
+ def wrapper(*args, **kwargs):
+ start = time.time()
+ result = func(*args, **kwargs)
+ end = time.time()
+ print(f'{func.__name__}执行时间: {end - start:.2f}秒')
+ return result
+
+ return wrapper
+
+
+@record_time
+def download(filename):
+ print(f'开始下载{filename}.')
+ time.sleep(random.random() * 6)
+ print(f'{filename}下载完成.')
+
+
+@record_time
+def upload(filename):
+ print(f'开始上传{filename}.')
+ time.sleep(random.random() * 8)
+ print(f'{filename}上传完成.')
+
+
+download('MySQL从删库到跑路.avi')
+upload('Python从入门到住院.pdf')
+```
+
+上面的代码,我们通过装饰器语法糖为`download`和`upload`函数添加了装饰器,被装饰后的`download`和`upload`函数其实就是我们在装饰器中返回的`wrapper`函数,调用它们其实就是在调用`wrapper`函数,所以才有了记录函数执行时间的功能。
+
+如果在代码的某些地方,我们想去掉装饰器的作用执行原函数,那么在定义装饰器函数的时候,需要做一点点额外的工作。Python 标准库`functools`模块的`wraps`函数也是一个装饰器,我们将它放在`wrapper`函数上,这个装饰器可以帮我们保留被装饰之前的函数,这样在需要取消装饰器时,可以通过被装饰函数的`__wrapped__`属性获得被装饰之前的函数。
+
+```python
+import random
+import time
+
+from functools import wraps
+
+
+def record_time(func):
+
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ start = time.time()
+ result = func(*args, **kwargs)
+ end = time.time()
+ print(f'{func.__name__}执行时间: {end - start:.2f}秒')
+ return result
+
+ return wrapper
+
+
+@record_time
+def download(filename):
+ print(f'开始下载{filename}.')
+ time.sleep(random.random() * 6)
+ print(f'{filename}下载完成.')
+
+
+@record_time
+def upload(filename):
+ print(f'开始上传{filename}.')
+ time.sleep(random.random() * 8)
+ print(f'{filename}上传完成.')
+
+
+# 调用装饰后的函数会记录执行时间
+download('MySQL从删库到跑路.avi')
+upload('Python从入门到住院.pdf')
+# 取消装饰器的作用不记录执行时间
+download.__wrapped__('MySQL必知必会.pdf')
+upload.__wrapped__('Python从新手到大师.pdf')
+```
+
+**装饰器函数本身也可以参数化**,简单的说就是装饰器也是可以通过调用者传入的参数来进行定制的,这个知识点我们在后面用到的时候再为大家讲解。
+
+### 递归调用
+
+Python 中允许函数嵌套定义,也允许函数之间相互调用,而且一个函数还可以直接或间接的调用自身。函数自己调用自己称为递归调用,那么递归调用有什么用处呢?现实中,有很多问题的定义本身就是一个递归定义,例如我们之前讲到的阶乘,非负整数`N`的阶乘是`N`乘以`N-1`的阶乘,即 $\small{N! = N \times (N-1)!}$ ,定义的左边和右边都出现了阶乘的概念,所以这是一个递归定义。既然如此,我们可以使用递归调用的方式来写一个求阶乘的函数,代码如下所示。
+
+```python
+def fac(num):
+ if num in (0, 1):
+ return 1
+ return num * fac(num - 1)
+```
+
+上面的代码中,`fac`函数中又调用了`fac`函数,这就是所谓的递归调用。代码第2行的`if`条件叫做递归的收敛条件,简单的说就是什么时候要结束函数的递归调用,在计算阶乘时,如果计算到`0`或`1`的阶乘,就停止递归调用,直接返回`1`;代码第4行的`num * fac(num - 1)`是递归公式,也就是阶乘的递归定义。下面,我们简单的分析下,如果用`fac(5)`计算`5`的阶乘,整个过程会是怎样的。
+
+```python
+# 递归调用函数入栈
+# 5 * fac(4)
+# 5 * (4 * fac(3))
+# 5 * (4 * (3 * fac(2)))
+# 5 * (4 * (3 * (2 * fac(1))))
+# 停止递归函数出栈
+# 5 * (4 * (3 * (2 * 1)))
+# 5 * (4 * (3 * 2))
+# 5 * (4 * 6)
+# 5 * 24
+# 120
+print(fac(5)) # 120
+```
+
+注意,函数调用会通过内存中称为“栈”(stack)的数据结构来保存当前代码的执行现场,函数调用结束后会通过这个栈结构恢复之前的执行现场。栈是一种先进后出的数据结构,这也就意味着最早入栈的函数最后才会返回,而最后入栈的函数会最先返回。例如调用一个名为`a`的函数,函数`a`的执行体中又调用了函数`b`,函数`b`的执行体中又调用了函数`c`,那么最先入栈的函数是`a`,最先出栈的函数是`c`。每进入一个函数调用,栈就会增加一层栈帧(stack frame),栈帧就是我们刚才提到的保存当前代码执行现场的结构;每当函数调用结束后,栈就会减少一层栈帧。通常,内存中的栈空间很小,因此递归调用的次数如果太多,会导致栈溢出(stack overflow),所以**递归调用一定要确保能够快速收敛**。我们可以尝试执行`fac(5000)`,看看是不是会提示`RecursionError`错误,错误消息为:`maximum recursion depth exceeded in comparison`(超出最大递归深度),其实就是发生了栈溢出。
+
+如果我们使用官方的 Python 解释器(CPython),默认将函数调用的栈结构最大深度设置为`1000`层。如果超出这个深度,就会发生上面说的`RecursionError`。当然,我们可以使用`sys`模块的`setrecursionlimit`函数来改变递归调用的最大深度,但是我们不建议这样做,因为让递归快速收敛才是我们应该做的事情,否则就应该考虑使用循环递推而不是递归。
+
+再举一个之前讲过的生成斐波那契数列的例子,因为斐波那契数列前两个数都是`1`,从第三个数开始,每个数是前两个数相加的和,可以记为`f(n) = f(n - 1) + f(n - 2)`,很显然这又是一个递归的定义,所以我们可以用下面的递归调用函数来计算第`n`个斐波那契数。
+
+```python
+def fib1(n):
+ if n in (1, 2):
+ return 1
+ return fib1(n - 1) + fib1(n - 2)
+
+
+for i in range(1, 21):
+ print(fib1(i))
+```
+
+需要提醒大家,上面计算斐波那契数的代码虽然看起来非常简单明了,但执行性能是比较糟糕的。大家可以试一试,把上面代码`for`循环中`range`函数的第二个参数修改为`51`,即输出前50个斐波那契数,看看需要多长时间,也欢迎大家在评论区留下你的代码执行时间。至于为什么这么慢,大家可以自己思考一下原因。很显然,直接使用循环递推的方式获得斐波那契数列是更好的选择,代码如下所示。
+
+```python
+def fib2(n):
+ a, b = 0, 1
+ for _ in range(n):
+ a, b = b, a + b
+ return a
+```
+
+除此以外,我们还可以使用 Python 标准库中`functools`模块的`lru_cache`函数来优化上面的递归代码。`lru_cache`函数是一个装饰器函数,我们将其置于上面的函数`fib1`之上,它可以缓存该函数的执行结果从而避免在递归调用的过程中产生大量的重复运算,这样代码的执行性能就有“飞一般”的提升。大家可以尝试输出前50个斐波那契数,看看加上装饰器以后代码需要执行多长时间,评论区见!
+
+```python
+from functools import lru_cache
+
+
+@lru_cache()
+def fib1(n):
+ if n in (1, 2):
+ return 1
+ return fib1(n - 1) + fib1(n - 2)
+
+
+for i in range(1, 51):
+ print(i, fib1(i))
+```
+
+> **提示**:`lru_cache`函数是一个带参数的装饰器,所以上面第4行代码使用装饰器语法糖时,`lru_cache`后面要跟上圆括号。`lru_cache`函数有一个非常重要的参数叫`maxsize`,它可以用来定义缓存空间的大小,默认值是128。
+
+### 总结
+
+装饰器是 Python 语言中的特色语法,**可以通过装饰器来增强现有的函数**,这是一种非常有用的编程技巧。另一方面,通过函数递归调用,可以在代码层面将一些复杂的问题简单化,但是**递归调用一定要注意收敛条件和递归公式**,找到递归公式才有机会使用递归调用,而收敛条件则确保了递归调用能停下来。函数调用通过内存中的栈空间来保存现场和恢复现场,栈空间通常都很小,所以**递归如果不能迅速收敛,很可能会引发栈溢出错误,从而导致程序的崩溃**。
diff --git "a/Day01-20/18.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\205\245\351\227\250.md" "b/Day01-20/18.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\205\245\351\227\250.md"
new file mode 100755
index 000000000..365fa94a8
--- /dev/null
+++ "b/Day01-20/18.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\205\245\351\227\250.md"
@@ -0,0 +1,207 @@
+## 面向对象编程入门
+
+面向对象编程是一种非常流行的**编程范式**(programming paradigm),所谓编程范式就是**程序设计的方法论**,简单的说就是程序员对程序的认知和理解以及他们编写代码的方式。
+
+在前面的课程中,我们说过“**程序是指令的集合**”,运行程序时,程序中的语句会变成一条或多条指令,然后由CPU(中央处理器)去执行。为了简化程序的设计,我们又讲到了函数,**把相对独立且经常重复使用的代码放置到函数中**,在需要使用这些代码的时候调用函数即可。如果一个函数的功能过于复杂和臃肿,我们又可以进一步**将函数进一步拆分为多个子函数**来降低系统的复杂性。
+
+不知大家是否发现,编程其实是写程序的人按照计算机的工作方式通过代码控制机器完成任务。但是,计算机的工作方式与人类正常的思维模式是不同的,如果编程就必须抛弃人类正常的思维方式去迎合计算机,编程的乐趣就少了很多。这里,我想说的并不是我们不能按照计算机的工作方式去编写代码,但是当我们需要开发一个复杂的系统时,这种方式会让代码过于复杂,从而导致开发和维护工作都变得举步维艰。
+
+随着软件复杂性的增加,编写正确可靠的代码会变成了一项极为艰巨的任务,这也是很多人都坚信“软件开发是人类改造世界所有活动中最为复杂的活动”的原因。如何用程序描述复杂系统和解决复杂问题,就成为了所有程序员必须要思考和直面的问题。诞生于上世纪70年代的 Smalltalk 语言让软件开发者看到了希望,因为它引入了一种新的编程范式叫面向对象编程。在面向对象编程的世界里,程序中的**数据和操作数据的函数是一个逻辑上的整体**,我们称之为**对象**,**对象可以接收消息**,解决问题的方法就是**创建对象并向对象发出各种各样的消息**;通过消息传递,程序中的多个对象可以协同工作,这样就能构造出复杂的系统并解决现实中的问题。当然,面向对象编程的雏形还可以向前追溯到更早期的Simula语言,但这不是我们要讨论的重点。
+
+> **说明:** 今天我们使用的很多高级程序设计语言都支持面向对象编程,但是面向对象编程也不是解决软件开发中所有问题的“银弹”,或者说在软件开发这个行业目前还没有所谓的“银弹”。关于这个问题,大家可以参考 IBM360 系统之父弗雷德里克·布鲁克斯所发表的论文《没有银弹:软件工程的本质性与附属性工作》或软件工程的经典著作《人月神话》一书。
+
+### 类和对象
+
+如果要用一句话来概括面向对象编程,我认为下面的说法是相当精辟和准确的。
+
+> **面向对象编程**:把一组数据和处理数据的方法组成**对象**,把行为相同的对象归纳为**类**,通过**封装**隐藏对象的内部细节,通过**继承**实现类的特化和泛化,通过**多态**实现基于对象类型的动态分派。
+
+这句话对初学者来说可能不那么容易理解,但是我可以先为大家圈出几个关键词:**对象**(object)、**类**(class)、**封装**(encapsulation)、**继承**(inheritance)、**多态**(polymorphism)。
+
+我们先说说类和对象这两个词。在面向对象编程中,**类是一个抽象的概念,对象是一个具体的概念**。我们把同一类对象的共同特征抽取出来就是一个类,比如我们经常说的人类,这是一个抽象概念,而我们每个人就是人类的这个抽象概念下的实实在在的存在,也就是一个对象。简而言之,**类是对象的蓝图和模板,对象是类的实例,是可以接受消息的实体**。
+
+在面向对象编程的世界中,**一切皆为对象**,**对象都有属性和行为**,**每个对象都是独一无二的**,而且**对象一定属于某个类**。对象的属性是对象的静态特征,对象的行为是对象的动态特征。按照上面的说法,如果我们把拥有共同特征的对象的属性和行为都抽取出来,就可以定义出一个类。
+
+
+
+### 定义类
+
+在 Python 语言中,我们可以使用`class`关键字加上类名来定义类,通过缩进我们可以确定类的代码块,就如同定义函数那样。在类的代码块中,我们需要写一些函数,我们说过类是一个抽象概念,那么这些函数就是我们对一类对象共同的动态特征的提取。写在类里面的函数我们通常称之为**方法**,方法就是对象的行为,也就是对象可以接收的消息。方法的第一个参数通常都是`self`,它代表了接收这个消息的对象本身。
+
+```python
+class Student:
+
+ def study(self, course_name):
+ print(f'学生正在学习{course_name}.')
+
+ def play(self):
+ print(f'学生正在玩游戏.')
+```
+
+### 创建和使用对象
+
+在我们定义好一个类之后,可以使用构造器语法来创建对象,代码如下所示。
+
+```python
+stu1 = Student()
+stu2 = Student()
+print(stu1) # <__main__.Student object at 0x10ad5ac50>
+print(stu2) # <__main__.Student object at 0x10ad5acd0>
+print(hex(id(stu1)), hex(id(stu2))) # 0x10ad5ac50 0x10ad5acd0
+```
+
+在类的名字后跟上圆括号就是所谓的构造器语法,上面的代码创建了两个学生对象,一个赋值给变量`stu1`,一个赋值给变量`stu2`。当我们用`print`函数打印`stu1`和`stu2`两个变量时,我们会看到输出了对象在内存中的地址(十六进制形式),跟我们用`id`函数查看对象标识获得的值是相同的。现在我们可以告诉大家,我们定义的变量其实保存的是一个对象在内存中的逻辑地址(位置),通过这个逻辑地址,我们就可以在内存中找到这个对象。所以`stu3 = stu2`这样的赋值语句并没有创建新的对象,只是用一个新的变量保存了已有对象的地址。
+
+接下来,我们尝试给对象发消息,即调用对象的方法。刚才的`Student`类中我们定义了`study`和`play`两个方法,两个方法的第一个参数`self`代表了接收消息的学生对象,`study`方法的第二个参数是学习的课程名称。Python中,给对象发消息有两种方式,请看下面的代码。
+
+```python
+# 通过“类.方法”调用方法
+# 第一个参数是接收消息的对象
+# 第二个参数是学习的课程名称
+Student.study(stu1, 'Python程序设计') # 学生正在学习Python程序设计.
+# 通过“对象.方法”调用方法
+# 点前面的对象就是接收消息的对象
+# 只需要传入第二个参数课程名称
+stu1.study('Python程序设计') # 学生正在学习Python程序设计.
+
+Student.play(stu2) # 学生正在玩游戏.
+stu2.play() # 学生正在玩游戏.
+```
+
+### 初始化方法
+
+大家可能已经注意到了,刚才我们创建的学生对象只有行为没有属性,如果要给学生对象定义属性,我们可以修改`Student`类,为其添加一个名为`__init__`的方法。在我们调用`Student`类的构造器创建对象时,首先会在内存中获得保存学生对象所需的内存空间,然后通过自动执行`__init__`方法,完成对内存的初始化操作,也就是把数据放到内存空间中。所以我们可以通过给`Student`类添加`__init__`方法的方式为学生对象指定属性,同时完成对属性赋初始值的操作,正因如此,`__init__`方法通常也被称为初始化方法。
+
+我们对上面的`Student`类稍作修改,给学生对象添加`name`(姓名)和`age`(年龄)两个属性。
+
+```python
+class Student:
+ """学生"""
+
+ def __init__(self, name, age):
+ """初始化方法"""
+ self.name = name
+ self.age = age
+
+ def study(self, course_name):
+ """学习"""
+ print(f'{self.name}正在学习{course_name}.')
+
+ def play(self):
+ """玩耍"""
+ print(f'{self.name}正在玩游戏.')
+```
+
+修改刚才创建对象和给对象发消息的代码,重新执行一次,看看程序的执行结果有什么变化。
+
+```python
+# 调用Student类的构造器创建对象并传入初始化参数
+stu1 = Student('骆昊', 44)
+stu2 = Student('王大锤', 25)
+stu1.study('Python程序设计') # 骆昊正在学习Python程序设计.
+stu2.play() # 王大锤正在玩游戏.
+```
+
+
+### 面向对象的支柱
+
+面向对象编程有三大支柱,就是我们之前给大家划重点的时候圈出的三个词:**封装**、**继承**和**多态**。后面两个概念在下一节课中会详细说明,这里我们先说一下什么是封装。我自己对封装的理解是:**隐藏一切可以隐藏的实现细节,只向外界暴露简单的调用接口**。我们在类中定义的对象方法其实就是一种封装,这种封装可以让我们在创建对象之后,只需要给对象发送一个消息就可以执行方法中的代码,也就是说我们在只知道方法的名字和参数(方法的外部视图),不知道方法内部实现细节(方法的内部视图)的情况下就完成了对方法的使用。
+
+举一个例子,假如要控制一个机器人帮我倒杯水,如果不使用面向对象编程,不做任何的封装,那么就需要向这个机器人发出一系列的指令,如站起来、向左转、向前走5步、拿起面前的水杯、向后转、向前走10步、弯腰、放下水杯、按下出水按钮、等待10秒、松开出水按钮、拿起水杯、向右转、向前走5步、放下水杯等,才能完成这个简单的操作,想想都觉得麻烦。按照面向对象编程的思想,我们可以将倒水的操作封装到机器人的一个方法中,当需要机器人帮我们倒水的时候,只需要向机器人对象发出倒水的消息就可以了,这样做不是更好吗?
+
+在很多场景下,面向对象编程其实就是一个三步走的问题。第一步定义类,第二步创建对象,第三步给对象发消息。当然,有的时候我们是不需要第一步的,因为我们想用的类可能已经存在了。之前我们说过,Python内置的`list`、`set`、`dict`其实都是类,如果要创建列表、集合、字典对象,我们就不用自定义类了。当然,有的类并不是 Python 标准库中直接提供的,它可能来自于第三方的代码,如何安装和使用三方代码在后续课程中会进行讨论。在某些特殊的场景中,我们会用到名为“内置对象”的对象,所谓“内置对象”就是说上面三步走的第一步和第二步都不需要了,因为类已经存在而且对象已然创建过了,直接向对象发消息就可以了,这也就是我们常说的“开箱即用”。
+
+### 面向对象案例
+
+#### 例子1:时钟
+
+> **要求**:定义一个类描述数字时钟,提供走字和显示时间的功能。
+
+```python
+import time
+
+
+# 定义时钟类
+class Clock:
+ """数字时钟"""
+
+ def __init__(self, hour=0, minute=0, second=0):
+ """初始化方法
+ :param hour: 时
+ :param minute: 分
+ :param second: 秒
+ """
+ self.hour = hour
+ self.min = minute
+ self.sec = second
+
+ def run(self):
+ """走字"""
+ self.sec += 1
+ if self.sec == 60:
+ self.sec = 0
+ self.min += 1
+ if self.min == 60:
+ self.min = 0
+ self.hour += 1
+ if self.hour == 24:
+ self.hour = 0
+
+ def show(self):
+ """显示时间"""
+ return f'{self.hour:0>2d}:{self.min:0>2d}:{self.sec:0>2d}'
+
+
+# 创建时钟对象
+clock = Clock(23, 59, 58)
+while True:
+ # 给时钟对象发消息读取时间
+ print(clock.show())
+ # 休眠1秒钟
+ time.sleep(1)
+ # 给时钟对象发消息使其走字
+ clock.run()
+```
+
+#### 例子2:平面上的点
+
+> **要求**:定义一个类描述平面上的点,提供计算到另一个点距离的方法。
+
+```python
+class Point:
+ """平面上的点"""
+
+ def __init__(self, x=0, y=0):
+ """初始化方法
+ :param x: 横坐标
+ :param y: 纵坐标
+ """
+ self.x, self.y = x, y
+
+ def distance_to(self, other):
+ """计算与另一个点的距离
+ :param other: 另一个点
+ """
+ dx = self.x - other.x
+ dy = self.y - other.y
+ return (dx * dx + dy * dy) ** 0.5
+
+ def __str__(self):
+ return f'({self.x}, {self.y})'
+
+
+p1 = Point(3, 5)
+p2 = Point(6, 9)
+print(p1) # 调用对象的__str__魔法方法
+print(p2)
+print(p1.distance_to(p2))
+```
+
+### 总结
+
+面向对象编程是一种非常流行的编程范式,除此之外还有**指令式编程**、**函数式编程**等编程范式。由于现实世界是由对象构成的,而对象是可以接收消息的实体,所以**面向对象编程更符合人类正常的思维习惯**。类是抽象的,对象是具体的,有了类就能创建对象,有了对象就可以接收消息,这就是面向对象编程的基础。定义类的过程是一个抽象的过程,找到对象公共的属性属于数据抽象,找到对象公共的方法属于行为抽象。抽象的过程是一个仁者见仁智者见智的过程,对同一类对象进行抽象可能会得到不同的结果,如下图所示。
+
+
+
+> **说明:** 本节课的插图来自于 Grady Booc 等撰写的《面向对象分析与设计》一书,该书是讲解面向对象编程的经典著作,有兴趣的读者可以购买和阅读这本书来了解更多的面向对象的相关知识。
+
diff --git "a/Day01-20/19.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\350\277\233\351\230\266.md" "b/Day01-20/19.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\350\277\233\351\230\266.md"
new file mode 100755
index 000000000..c6b540f03
--- /dev/null
+++ "b/Day01-20/19.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\350\277\233\351\230\266.md"
@@ -0,0 +1,191 @@
+## 面向对象编程进阶
+
+前面我们讲解了 Python 面向对象编程的一些基础知识,本节我们继续讨论面向对象编程相关的内容。
+
+### 可见性和属性装饰器
+
+在很多面向对象编程语言中,对象的属性通常会被设置为私有(private)或受保护(protected)的成员,简单的说就是不允许直接访问这些属性;对象的方法通常都是公开的(public),因为公开的方法是对象能够接受的消息,也是对象暴露给外界的调用接口,这就是所谓的访问可见性。在 Python 中,可以通过给对象属性名添加前缀下划线的方式来说明属性的访问可见性,例如,可以用`__name`表示一个私有属性,`_name`表示一个受保护属性,代码如下所示。
+
+```python
+class Student:
+
+ def __init__(self, name, age):
+ self.__name = name
+ self.__age = age
+
+ def study(self, course_name):
+ print(f'{self.__name}正在学习{course_name}.')
+
+
+stu = Student('王大锤', 20)
+stu.study('Python程序设计')
+print(stu.__name) # AttributeError: 'Student' object has no attribute '__name'
+```
+
+上面代码的最后一行会引发`AttributeError`(属性错误)异常,异常消息为:`'Student' object has no attribute '__name'`。由此可见,以`__`开头的属性`__name`相当于是私有的,在类的外面无法直接访问,但是类里面的`study`方法中可以通过`self.__name`访问该属性。需要说明的是,大多数使用 Python 语言的人在定义类时,通常不会选择让对象的属性私有或受保护,正如有一句名言说的:“**We are all consenting adults here**”(大家都是成年人),成年人可以为自己的行为负责,而不需要通过 Python 语言本身来限制访问可见性。事实上,大多数的程序员都认为**开放比封闭要好**,把对象的属性私有化并非必不可少的东西,所以 Python 语言并没有从语义上做出最严格的限定,也就是说上面的代码如果你愿意,用`stu._Student__name`的方式仍然可以访问到私有属性`__name`,有兴趣的读者可以自己试一试。
+
+### 动态属性
+
+Python 语言属于动态语言,维基百科对动态语言的解释是:“在运行时可以改变其结构的语言,例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化”。动态语言非常灵活,目前流行的 Python 和 JavaScript 都是动态语言,除此之外,诸如 PHP、Ruby 等也都属于动态语言,而 C、C++ 等语言则不属于动态语言。
+
+在 Python 中,我们可以动态为对象添加属性,这是 Python 作为动态类型语言的一项特权,代码如下所示。需要提醒大家的是,对象的方法其实本质上也是对象的属性,如果给对象发送一个无法接收的消息,引发的异常仍然是`AttributeError`。
+
+```python
+class Student:
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+
+stu = Student('王大锤', 20)
+stu.sex = '男' # 给学生对象动态添加sex属性
+```
+
+如果不希望在使用对象时动态的为对象添加属性,可以使用 Python 语言中的`__slots__`魔法。对于`Student`类来说,可以在类中指定`__slots__ = ('name', 'age')`,这样`Student`类的对象只能有`name`和`age`属性,如果想动态添加其他属性将会引发异常,代码如下所示。
+
+```python
+class Student:
+ __slots__ = ('name', 'age')
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+
+stu = Student('王大锤', 20)
+# AttributeError: 'Student' object has no attribute 'sex'
+stu.sex = '男'
+```
+
+### 静态方法和类方法
+
+之前我们在类中定义的方法都是对象方法,换句话说这些方法都是对象可以接收的消息。除了对象方法之外,类中还可以有静态方法和类方法,这两类方法是发给类的消息,二者并没有实质性的区别。在面向对象的世界里,一切皆为对象,我们定义的每一个类其实也是一个对象,而静态方法和类方法就是发送给类对象的消息。那么,什么样的消息会直接发送给类对象呢?
+
+举一个例子,定义一个三角形类,通过传入三条边的长度来构造三角形,并提供计算周长和面积的方法。计算周长和面积肯定是三角形对象的方法,这一点毫无疑问。但是在创建三角形对象时,传入的三条边长未必能构造出三角形,为此我们可以先写一个方法来验证给定的三条边长是否可以构成三角形,这种方法很显然就不是对象方法,因为在调用这个方法时三角形对象还没有创建出来。我们可以把这类方法设计为静态方法或类方法,也就是说这类方法不是发送给三角形对象的消息,而是发送给三角形类的消息,代码如下所示。
+
+```python
+class Triangle(object):
+ """三角形"""
+
+ def __init__(self, a, b, c):
+ """初始化方法"""
+ self.a = a
+ self.b = b
+ self.c = c
+
+ @staticmethod
+ def is_valid(a, b, c):
+ """判断三条边长能否构成三角形(静态方法)"""
+ return a + b > c and b + c > a and a + c > b
+
+ # @classmethod
+ # def is_valid(cls, a, b, c):
+ # """判断三条边长能否构成三角形(类方法)"""
+ # return a + b > c and b + c > a and a + c > b
+
+ def perimeter(self):
+ """计算周长"""
+ return self.a + self.b + self.c
+
+ def area(self):
+ """计算面积"""
+ p = self.perimeter() / 2
+ return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5
+```
+
+上面的代码使用`staticmethod`装饰器声明了`is_valid`方法是`Triangle`类的静态方法,如果要声明类方法,可以使用`classmethod`装饰器(如上面的代码15~18行所示)。可以直接使用`类名.方法名`的方式来调用静态方法和类方法,二者的区别在于,类方法的第一个参数是类对象本身,而静态方法则没有这个参数。简单的总结一下,**对象方法、类方法、静态方法都可以通过“类名.方法名”的方式来调用,区别在于方法的第一个参数到底是普通对象还是类对象,还是没有接受消息的对象**。静态方法通常也可以直接写成一个独立的函数,因为它并没有跟特定的对象绑定。
+
+这里做一个补充说明,我们可以给上面计算三角形周长和面积的方法添加一个`property`装饰器(Python 内置类型),这样三角形类的`perimeter`和`area`就变成了两个属性,不再通过调用方法的方式来访问,而是用对象访问属性的方式直接获得,修改后的代码如下所示。
+
+```python
+class Triangle(object):
+ """三角形"""
+
+ def __init__(self, a, b, c):
+ """初始化方法"""
+ self.a = a
+ self.b = b
+ self.c = c
+
+ @staticmethod
+ def is_valid(a, b, c):
+ """判断三条边长能否构成三角形(静态方法)"""
+ return a + b > c and b + c > a and a + c > b
+
+ @property
+ def perimeter(self):
+ """计算周长"""
+ return self.a + self.b + self.c
+
+ @property
+ def area(self):
+ """计算面积"""
+ p = self.perimeter / 2
+ return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5
+
+
+t = Triangle(3, 4, 5)
+print(f'周长: {t.perimeter}')
+print(f'面积: {t.area}')
+```
+
+### 继承和多态
+
+面向对象的编程语言支持在已有类的基础上创建新类,从而减少重复代码的编写。提供继承信息的类叫做父类(超类、基类),得到继承信息的类叫做子类(派生类、衍生类)。例如,我们定义一个学生类和一个老师类,我们会发现他们有大量的重复代码,而这些重复代码都是老师和学生作为人的公共属性和行为,所以在这种情况下,我们应该先定义人类,再通过继承,从人类派生出老师类和学生类,代码如下所示。
+
+```python
+class Person:
+ """人"""
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ def eat(self):
+ print(f'{self.name}正在吃饭.')
+
+ def sleep(self):
+ print(f'{self.name}正在睡觉.')
+
+
+class Student(Person):
+ """学生"""
+
+ def __init__(self, name, age):
+ super().__init__(name, age)
+
+ def study(self, course_name):
+ print(f'{self.name}正在学习{course_name}.')
+
+
+class Teacher(Person):
+ """老师"""
+
+ def __init__(self, name, age, title):
+ super().__init__(name, age)
+ self.title = title
+
+ def teach(self, course_name):
+ print(f'{self.name}{self.title}正在讲授{course_name}.')
+
+
+
+stu1 = Student('白元芳', 21)
+stu2 = Student('狄仁杰', 22)
+tea1 = Teacher('武则天', 35, '副教授')
+stu1.eat()
+stu2.sleep()
+tea1.eat()
+stu1.study('Python程序设计')
+tea1.teach('Python程序设计')
+stu2.study('数据科学导论')
+```
+
+继承的语法是在定义类的时候,在类名后的圆括号中指定当前类的父类。如果定义一个类的时候没有指定它的父类是谁,那么默认的父类是`object`类。`object`类是 Python 中的顶级类,这也就意味着所有的类都是它的子类,要么直接继承它,要么间接继承它。Python 语言允许多重继承,也就是说一个类可以有一个或多个父类,关于多重继承的问题我们在后面会有更为详细的讨论。在子类的初始化方法中,我们可以通过`super().__init__()`来调用父类初始化方法,`super`函数是 Python 内置函数中专门为获取当前对象的父类对象而设计的。从上面的代码可以看出,子类除了可以通过继承得到父类提供的属性和方法外,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力。在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,也叫做“里氏替换原则”(Liskov Substitution Principle)。
+
+子类继承父类的方法后,还可以对方法进行重写(重新实现该方法),不同的子类可以对父类的同一个方法给出不同的实现版本,这样的方法在程序运行时就会表现出多态行为(调用相同的方法,做了不同的事情)。多态是面向对象编程中最精髓的部分,当然也是对初学者来说最难以理解和灵活运用的部分,我们会在下一个章节用专门的例子来讲解这个知识点。
+
+### 总结
+
+Python 是动态类型语言,Python 中的对象可以动态的添加属性,对象的方法其实也是属性,只不过和该属性对应的是一个可以调用的函数。在面向对象的世界中,**一切皆为对象**,我们定义的类也是对象,所以**类也可以接收消息**,对应的方法是类方法或静态方法。通过继承,我们**可以从已有的类创建新类**,实现对已有类代码的复用。
diff --git "a/Day01-20/20.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\272\224\347\224\250.md" "b/Day01-20/20.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\272\224\347\224\250.md"
new file mode 100755
index 000000000..b1a0e504c
--- /dev/null
+++ "b/Day01-20/20.\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\272\224\347\224\250.md"
@@ -0,0 +1,228 @@
+## 面向对象编程应用
+
+面向对象编程对初学者来说不难理解但很难应用,虽然我们为大家总结过面向对象的三步走方法(定义类、创建对象、给对象发消息),但是说起来容易做起来难。**大量的编程练习**和**阅读优质的代码**可能是这个阶段最能够帮助到大家的两件事情。接下来我们还是通过经典的案例来剖析面向对象编程的知识,同时也通过这些案例把我们之前学过的 Python 知识都串联起来。
+
+### 例子1:扑克游戏。
+
+> **说明**:简单起见,我们的扑克只有52张牌(没有大小王),游戏需要将 52 张牌发到 4 个玩家的手上,每个玩家手上有 13 张牌,按照黑桃、红心、草花、方块的顺序和点数从小到大排列,暂时不实现其他的功能。
+
+使用面向对象编程方法,首先需要从问题的需求中找到对象并抽象出对应的类,此外还要找到对象的属性和行为。当然,这件事情并不是特别困难,我们可以从需求的描述中找出名词和动词,名词通常就是对象或者是对象的属性,而动词通常是对象的行为。扑克游戏中至少应该有三类对象,分别是牌、扑克和玩家,牌、扑克、玩家三个类也并不是孤立的。类和类之间的关系可以粗略的分为 **is-a关系(继承)**、**has-a关系(关联)**和 **use-a关系(依赖)**。很显然扑克和牌是 has-a 关系,因为一副扑克有(has-a)52 张牌;玩家和牌之间不仅有关联关系还有依赖关系,因为玩家手上有(has-a)牌而且玩家使用了(use-a)牌。
+
+牌的属性显而易见,有花色和点数。我们可以用 0 到 3 的四个数字来代表四种不同的花色,但是这样的代码可读性会非常糟糕,因为我们并不知道黑桃、红心、草花、方块跟 0 到 3 的数字的对应关系。如果一个变量的取值只有有限多个选项,我们可以使用枚举。与 C、Java 等语言不同的是,Python 中没有声明枚举类型的关键字,但是可以通过继承`enum`模块的`Enum`类来创建枚举类型,代码如下所示。
+
+```python
+from enum import Enum
+
+
+class Suite(Enum):
+ """花色(枚举)"""
+ SPADE, HEART, CLUB, DIAMOND = range(4)
+```
+
+通过上面的代码可以看出,定义枚举类型其实就是定义符号常量,如`SPADE`、`HEART`等。每个符号常量都有与之对应的值,这样表示黑桃就可以不用数字 0,而是用`Suite.SPADE`;同理,表示方块可以不用数字 3, 而是用`Suite.DIAMOND`。注意,使用符号常量肯定是优于使用字面常量的,因为能够读懂英文就能理解符号常量的含义,代码的可读性会提升很多。Python 中的枚举类型是可迭代类型,简单的说就是可以将枚举类型放到`for-in`循环中,依次取出每一个符号常量及其对应的值,如下所示。
+
+```python
+for suite in Suite:
+ print(f'{suite}: {suite.value}')
+```
+
+接下来我们可以定义牌类。
+
+```python
+class Card:
+ """牌"""
+
+ def __init__(self, suite, face):
+ self.suite = suite
+ self.face = face
+
+ def __repr__(self):
+ suites = '♠♥♣♦'
+ faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
+ return f'{suites[self.suite.value]}{faces[self.face]}' # 返回牌的花色和点数
+```
+
+可以通过下面的代码来测试下`Card`类。
+
+```python
+card1 = Card(Suite.SPADE, 5)
+card2 = Card(Suite.HEART, 13)
+print(card1) # ♠5
+print(card2) # ♥K
+```
+
+接下来我们定义扑克类。
+
+```python
+import random
+
+
+class Poker:
+ """扑克"""
+
+ def __init__(self):
+ self.cards = [Card(suite, face)
+ for suite in Suite
+ for face in range(1, 14)] # 52张牌构成的列表
+ self.current = 0 # 记录发牌位置的属性
+
+ def shuffle(self):
+ """洗牌"""
+ self.current = 0
+ random.shuffle(self.cards) # 通过random模块的shuffle函数实现随机乱序
+
+ def deal(self):
+ """发牌"""
+ card = self.cards[self.current]
+ self.current += 1
+ return card
+
+ @property
+ def has_next(self):
+ """还有没有牌可以发"""
+ return self.current < len(self.cards)
+```
+
+可以通过下面的代码来测试下`Poker`类。
+
+```python
+poker = Poker()
+print(poker.cards) # 洗牌前的牌
+poker.shuffle()
+print(poker.cards) # 洗牌后的牌
+```
+
+定义玩家类。
+
+```python
+class Player:
+ """玩家"""
+
+ def __init__(self, name):
+ self.name = name
+ self.cards = [] # 玩家手上的牌
+
+ def get_one(self, card):
+ """摸牌"""
+ self.cards.append(card)
+
+ def arrange(self):
+ """整理手上的牌"""
+ self.cards.sort()
+```
+
+创建四个玩家并将牌发到玩家的手上。
+
+```python
+poker = Poker()
+poker.shuffle()
+players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')]
+# 将牌轮流发到每个玩家手上每人13张牌
+for _ in range(13):
+ for player in players:
+ player.get_one(poker.deal())
+# 玩家整理手上的牌输出名字和手牌
+for player in players:
+ player.arrange()
+ print(f'{player.name}: ', end='')
+ print(player.cards)
+```
+
+执行上面的代码会在`player.arrange()`那里出现异常,因为`Player`的`arrange`方法使用了列表的`sort`对玩家手上的牌进行排序,排序需要比较两个`Card`对象的大小,而`<`运算符又不能直接作用于`Card`类型,所以就出现了`TypeError`异常,异常消息为:`'<' not supported between instances of 'Card' and 'Card'`。
+
+为了解决这个问题,我们可以对`Card`类的代码稍作修改,使得两个`Card`对象可以直接用`<`进行大小的比较。这里用到技术叫**运算符重载**,Python 中要实现对`<`运算符的重载,需要在类中添加一个名为`__lt__`的魔术方法。很显然,魔术方法`__lt__`中的`lt`是英文单词“less than”的缩写,以此类推,魔术方法`__gt__`对应`>`运算符,魔术方法`__le__`对应`<=`运算符,`__ge__`对应`>=`运算符,`__eq__`对应`==`运算符,`__ne__`对应`!=`运算符。
+
+修改后的`Card`类代码如下所示。
+
+```python
+class Card:
+ """牌"""
+
+ def __init__(self, suite, face):
+ self.suite = suite
+ self.face = face
+
+ def __repr__(self):
+ suites = '♠♥♣♦'
+ faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
+ return f'{suites[self.suite.value]}{faces[self.face]}'
+
+ def __lt__(self, other):
+ if self.suite == other.suite:
+ return self.face < other.face # 花色相同比较点数的大小
+ return self.suite.value < other.suite.value # 花色不同比较花色对应的值
+```
+
+>**说明:** 大家可以尝试在上面代码的基础上写一个简单的扑克游戏,如 21 点游戏(Black Jack),游戏的规则可以自己在网上找一找。
+
+### 例子2:工资结算系统。
+
+> **要求**:某公司有三种类型的员工,分别是部门经理、程序员和销售员。需要设计一个工资结算系统,根据提供的员工信息来计算员工的月薪。其中,部门经理的月薪是固定 15000 元;程序员按工作时间(以小时为单位)支付月薪,每小时 200 元;销售员的月薪由 1800 元底薪加上销售额 5% 的提成两部分构成。
+
+通过对上述需求的分析,可以看出部门经理、程序员、销售员都是员工,有相同的属性和行为,那么我们可以先设计一个名为`Employee`的父类,再通过继承的方式从这个父类派生出部门经理、程序员和销售员三个子类。很显然,后续的代码不会创建`Employee` 类的对象,因为我们需要的是具体的员工对象,所以这个类可以设计成专门用于继承的抽象类。Python 语言中没有定义抽象类的关键字,但是可以通过`abc`模块中名为`ABCMeta` 的元类来定义抽象类。关于元类的概念此处不展开讲解,当然大家不用纠结,照做即可。
+
+```python
+from abc import ABCMeta, abstractmethod
+
+
+class Employee(metaclass=ABCMeta):
+ """员工"""
+
+ def __init__(self, name):
+ self.name = name
+
+ @abstractmethod
+ def get_salary(self):
+ """结算月薪"""
+ pass
+```
+
+在上面的员工类中,有一个名为`get_salary`的方法用于结算月薪,但是由于还没有确定是哪一类员工,所以结算月薪虽然是员工的公共行为但这里却没有办法实现。对于暂时无法实现的方法,我们可以使用`abstractmethod`装饰器将其声明为抽象方法,所谓**抽象方法就是只有声明没有实现的方法**,**声明这个方法是为了让子类去重写这个方法**。接下来的代码展示了如何从员工类派生出部门经理、程序员、销售员这三个子类以及子类如何重写父类的抽象方法。
+
+```python
+class Manager(Employee):
+ """部门经理"""
+
+ def get_salary(self):
+ return 15000.0
+
+
+class Programmer(Employee):
+ """程序员"""
+
+ def __init__(self, name, working_hour=0):
+ super().__init__(name)
+ self.working_hour = working_hour
+
+ def get_salary(self):
+ return 200 * self.working_hour
+
+
+class Salesman(Employee):
+ """销售员"""
+
+ def __init__(self, name, sales=0):
+ super().__init__(name)
+ self.sales = sales
+
+ def get_salary(self):
+ return 1800 + self.sales * 0.05
+```
+
+上面的`Manager`、`Programmer`、`Salesman`三个类都继承自`Employee`,三个类都分别重写了`get_salary`方法。**重写就是子类对父类已有的方法重新做出实现**。相信大家已经注意到了,三个子类中的`get_salary`各不相同,所以这个方法在程序运行时会产生**多态行为**,多态简单的说就是**调用相同的方法**,**不同的子类对象做不同的事情**。
+
+我们通过下面的代码来完成这个工资结算系统,由于程序员和销售员需要分别录入本月的工作时间和销售额,所以在下面的代码中我们使用了 Python 内置的`isinstance`函数来判断员工对象的类型。我们之前讲过的`type`函数也能识别对象的类型,但是`isinstance`函数更加强大,因为它可以判断出一个对象是不是某个继承结构下的子类型,你可以简单的理解为`type`函数是对对象类型的精准匹配,而`isinstance`函数是对对象类型的模糊匹配。
+
+```python
+emps = [Manager('刘备'), Programmer('诸葛亮'), Manager('曹操'), Programmer('荀彧'), Salesman('张辽')]
+for emp in emps:
+ if isinstance(emp, Programmer):
+ emp.working_hour = int(input(f'请输入{emp.name}本月工作时间: '))
+ elif isinstance(emp, Salesman):
+ emp.sales = float(input(f'请输入{emp.name}本月销售额: '))
+ print(f'{emp.name}本月工资为: ¥{emp.get_salary():.2f}元')
+```
+
+### 总结
+
+面向对象编程思想非常的好,也符合人类的正常思维习惯,但是要想灵活运用面向对象编程中的抽象、封装、继承、多态需要长时间的积累和沉淀,这件事情无法一蹴而就,因为知识的积累本就是涓滴成河的过程。
diff --git a/Day01-20/res/day01/github_pypl_and_ieee_spectrum.png b/Day01-20/res/day01/github_pypl_and_ieee_spectrum.png
new file mode 100755
index 000000000..37b28343e
Binary files /dev/null and b/Day01-20/res/day01/github_pypl_and_ieee_spectrum.png differ
diff --git a/Day01-20/res/day01/install_python_1.png b/Day01-20/res/day01/install_python_1.png
new file mode 100755
index 000000000..c9048606a
Binary files /dev/null and b/Day01-20/res/day01/install_python_1.png differ
diff --git a/Day01-20/res/day01/install_python_2.png b/Day01-20/res/day01/install_python_2.png
new file mode 100755
index 000000000..df69f6d45
Binary files /dev/null and b/Day01-20/res/day01/install_python_2.png differ
diff --git a/Day01-20/res/day01/install_python_3.png b/Day01-20/res/day01/install_python_3.png
new file mode 100755
index 000000000..384ca3309
Binary files /dev/null and b/Day01-20/res/day01/install_python_3.png differ
diff --git a/Day01-20/res/day01/install_python_4.png b/Day01-20/res/day01/install_python_4.png
new file mode 100755
index 000000000..8904d1495
Binary files /dev/null and b/Day01-20/res/day01/install_python_4.png differ
diff --git a/Day01-20/res/day01/install_python_5.png b/Day01-20/res/day01/install_python_5.png
new file mode 100755
index 000000000..f09d4e7f3
Binary files /dev/null and b/Day01-20/res/day01/install_python_5.png differ
diff --git a/Day01-20/res/day01/install_python_6.png b/Day01-20/res/day01/install_python_6.png
new file mode 100755
index 000000000..31dda3ce8
Binary files /dev/null and b/Day01-20/res/day01/install_python_6.png differ
diff --git a/Day01-20/res/day01/install_python_7.png b/Day01-20/res/day01/install_python_7.png
new file mode 100755
index 000000000..2a9e0e0a0
Binary files /dev/null and b/Day01-20/res/day01/install_python_7.png differ
diff --git a/Day01-20/res/day01/python_download_page_1.png b/Day01-20/res/day01/python_download_page_1.png
new file mode 100755
index 000000000..cf4b99052
Binary files /dev/null and b/Day01-20/res/day01/python_download_page_1.png differ
diff --git a/Day01-20/res/day01/python_download_page_2.png b/Day01-20/res/day01/python_download_page_2.png
new file mode 100755
index 000000000..17c7a8a0e
Binary files /dev/null and b/Day01-20/res/day01/python_download_page_2.png differ
diff --git a/Day01-20/res/day01/tiobe_index.png b/Day01-20/res/day01/tiobe_index.png
new file mode 100755
index 000000000..52ebfef1d
Binary files /dev/null and b/Day01-20/res/day01/tiobe_index.png differ
diff --git a/Day01-20/res/day01/vs_build_tools_download.png b/Day01-20/res/day01/vs_build_tools_download.png
new file mode 100755
index 000000000..ff194f6f0
Binary files /dev/null and b/Day01-20/res/day01/vs_build_tools_download.png differ
diff --git a/Day01-20/res/day01/vs_build_tools_install.png b/Day01-20/res/day01/vs_build_tools_install.png
new file mode 100755
index 000000000..7b58a2b87
Binary files /dev/null and b/Day01-20/res/day01/vs_build_tools_install.png differ
diff --git a/Day01-20/res/day02/pycharm_download_page.png b/Day01-20/res/day02/pycharm_download_page.png
new file mode 100755
index 000000000..d23020b54
Binary files /dev/null and b/Day01-20/res/day02/pycharm_download_page.png differ
diff --git a/Day01-20/res/day02/using_pycharm_1.png b/Day01-20/res/day02/using_pycharm_1.png
new file mode 100755
index 000000000..5866afad3
Binary files /dev/null and b/Day01-20/res/day02/using_pycharm_1.png differ
diff --git a/Day01-20/res/day02/using_pycharm_2.png b/Day01-20/res/day02/using_pycharm_2.png
new file mode 100755
index 000000000..e95a84c62
Binary files /dev/null and b/Day01-20/res/day02/using_pycharm_2.png differ
diff --git a/Day01-20/res/day02/using_pycharm_3.png b/Day01-20/res/day02/using_pycharm_3.png
new file mode 100755
index 000000000..65b097604
Binary files /dev/null and b/Day01-20/res/day02/using_pycharm_3.png differ
diff --git a/Day01-20/res/day02/using_pycharm_4.png b/Day01-20/res/day02/using_pycharm_4.png
new file mode 100755
index 000000000..5d8af92c6
Binary files /dev/null and b/Day01-20/res/day02/using_pycharm_4.png differ
diff --git a/Day01-20/res/day02/using_pycharm_5.png b/Day01-20/res/day02/using_pycharm_5.png
new file mode 100755
index 000000000..4ac4165c2
Binary files /dev/null and b/Day01-20/res/day02/using_pycharm_5.png differ
diff --git a/Day01-20/res/day02/using_pycharm_6.png b/Day01-20/res/day02/using_pycharm_6.png
new file mode 100755
index 000000000..62f19a6b5
Binary files /dev/null and b/Day01-20/res/day02/using_pycharm_6.png differ
diff --git a/Day01-20/res/day02/using_pycharm_7.png b/Day01-20/res/day02/using_pycharm_7.png
new file mode 100755
index 000000000..e204b3aca
Binary files /dev/null and b/Day01-20/res/day02/using_pycharm_7.png differ
diff --git a/Day01-20/res/day02/using_pycharm_8.png b/Day01-20/res/day02/using_pycharm_8.png
new file mode 100755
index 000000000..7e7ffcc6d
Binary files /dev/null and b/Day01-20/res/day02/using_pycharm_8.png differ
diff --git a/Day01-20/res/day02/visual_studio_code.png b/Day01-20/res/day02/visual_studio_code.png
new file mode 100755
index 000000000..c8b4df311
Binary files /dev/null and b/Day01-20/res/day02/visual_studio_code.png differ
diff --git a/Day01-20/res/day06/terminate_program.png b/Day01-20/res/day06/terminate_program.png
new file mode 100755
index 000000000..0a98dd339
Binary files /dev/null and b/Day01-20/res/day06/terminate_program.png differ
diff --git a/Day01-20/res/day09/lottery.png b/Day01-20/res/day09/lottery.png
new file mode 100755
index 000000000..011a46e0d
Binary files /dev/null and b/Day01-20/res/day09/lottery.png differ
diff --git a/Day01-20/res/day09/lottery_run_result.png b/Day01-20/res/day09/lottery_run_result.png
new file mode 100755
index 000000000..1258b7879
Binary files /dev/null and b/Day01-20/res/day09/lottery_run_result.png differ
diff --git a/Day01-20/res/day09/output_using_rich.png b/Day01-20/res/day09/output_using_rich.png
new file mode 100755
index 000000000..27f51f9f4
Binary files /dev/null and b/Day01-20/res/day09/output_using_rich.png differ
diff --git a/Day01-20/res/day09/run_pip_in_terminal.png b/Day01-20/res/day09/run_pip_in_terminal.png
new file mode 100755
index 000000000..ab623fed9
Binary files /dev/null and b/Day01-20/res/day09/run_pip_in_terminal.png differ
diff --git a/Day01-20/res/day11/ENIAC.jpg b/Day01-20/res/day11/ENIAC.jpg
new file mode 100755
index 000000000..75c5f42d1
Binary files /dev/null and b/Day01-20/res/day11/ENIAC.jpg differ
diff --git a/Day01-20/res/day12/set_operations.png b/Day01-20/res/day12/set_operations.png
new file mode 100755
index 000000000..0a5acad68
Binary files /dev/null and b/Day01-20/res/day12/set_operations.png differ
diff --git a/Day01-20/res/day13/xinhua_dictionary.jpg b/Day01-20/res/day13/xinhua_dictionary.jpg
new file mode 100755
index 000000000..6ba2479ea
Binary files /dev/null and b/Day01-20/res/day13/xinhua_dictionary.jpg differ
diff --git a/Day01-20/res/day14/function_definition.png b/Day01-20/res/day14/function_definition.png
new file mode 100755
index 000000000..0f44d05bc
Binary files /dev/null and b/Day01-20/res/day14/function_definition.png differ
diff --git a/Day01-20/res/day18/20210731182741.png b/Day01-20/res/day18/20210731182741.png
new file mode 100755
index 000000000..538fa4d86
Binary files /dev/null and b/Day01-20/res/day18/20210731182741.png differ
diff --git a/Day01-20/res/day18/20210731182914.png b/Day01-20/res/day18/20210731182914.png
new file mode 100755
index 000000000..c631e2fa2
Binary files /dev/null and b/Day01-20/res/day18/20210731182914.png differ
diff --git a/Day01/hello.py b/Day01/hello.py
deleted file mode 100644
index b91e9d6fd..000000000
--- a/Day01/hello.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-
-第一个Python程序 - hello, world!
-向伟大的Dennis M. Ritchie先生致敬
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-26
-
-请将该文件命名为hello.py并在终端中通过下面的命令运行它
-python hello.py
-
-"""
-
-print('hello, world!')
-# print("你好,世界!")
-print('你好', '世界')
-print('hello', 'world', sep=', ', end='!')
-print('goodbye, world', end='!\n')
diff --git a/Day01/res/python-idle.png b/Day01/res/python-idle.png
deleted file mode 100644
index 1e90598fc..000000000
Binary files a/Day01/res/python-idle.png and /dev/null differ
diff --git a/Day01/res/python-ipython.png b/Day01/res/python-ipython.png
deleted file mode 100644
index c3a9054fb..000000000
Binary files a/Day01/res/python-ipython.png and /dev/null differ
diff --git a/Day01/res/python-jupyter-1.png b/Day01/res/python-jupyter-1.png
deleted file mode 100644
index 58cedc50e..000000000
Binary files a/Day01/res/python-jupyter-1.png and /dev/null differ
diff --git a/Day01/res/python-jupyter-2.png b/Day01/res/python-jupyter-2.png
deleted file mode 100644
index 27249889e..000000000
Binary files a/Day01/res/python-jupyter-2.png and /dev/null differ
diff --git a/Day01/res/python-pycharm.png b/Day01/res/python-pycharm.png
deleted file mode 100644
index ffae8c3f4..000000000
Binary files a/Day01/res/python-pycharm.png and /dev/null differ
diff --git a/Day01/res/python-sublime.png b/Day01/res/python-sublime.png
deleted file mode 100644
index c786d13c1..000000000
Binary files a/Day01/res/python-sublime.png and /dev/null differ
diff --git "a/Day01/\345\210\235\350\257\206Python.md" "b/Day01/\345\210\235\350\257\206Python.md"
deleted file mode 100644
index 44f048b8f..000000000
--- "a/Day01/\345\210\235\350\257\206Python.md"
+++ /dev/null
@@ -1,253 +0,0 @@
-## Day01 - 初识Python
-
-### Python简介
-
-#### Python的历史
-
-1. 1989年圣诞节:Guido von Rossum开始写Python语言的编译器。
-2. 1991年2月:第一个Python编译器(同时也是解释器)诞生,它是用C语言实现的(后面又出现了Java和C#实现的版本Jython和IronPython,以及PyPy、Brython、Pyston等其他实现),可以调用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.6/2.7版本,因为目前还有公司在项目和运维中使用Python 2.x版本的代码。
-
-目前我们使用的Python 3.6.x的版本是在2016年的12月23日发布的,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. 学习曲线低,与其他很多语言比上手更容易。
-3. 开放源代码,拥有强大的社区和生态圈。
-4. 解释型语言,完美的平台可移植性。
-5. 支持两种主流的编程范式,可以使用面向对象和函数式编程。
-6. 可扩展性和可嵌入性,可以调用C/C++代码也可以在C/C++中调用。
-7. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。
-
-Python的缺点主要集中在以下几点。
-
-1. 执行效率低下,因此计算密集型任务可以由C/C++编写。
-2. 代码无法加密,但是现在的公司很多都不是卖软件而是卖服务,这个问题慢慢会淡化。
-3. 在开发时可以选择的框架太多,有选择的地方就有错误。
-
-#### Python的应用领域
-
-目前Python在云基础设施、DevOps、网络爬虫开发、数据分析挖掘、机器学习等领域都有着广泛的应用,因此也产生了服务器开发、数据接口开发、自动化运维、科学计算和数据可视化、聊天机器人开发、图像识别和处理等一系列的职位。
-
-### 搭建编程环境
-
-#### Windows环境
-
-可以在[Python的官方网站](https://www.python.org)下载到Python的Windows安装程序(exe文件),需要注意的是如果在Windows 7环境下安装需要先安装Service Pack 1补丁包(可以通过一些工具软件自动安装系统补丁的功能来安装),安装过程建议勾选“Add Python 3.6 to PATH”(将Python 3.6添加到PATH环境变量)并选择自定义安装,在设置“Optional Features”界面最好将“pip”、“tcl/tk”、“Python test suite”等项全部勾选上。强烈建议使用自定义的安装路径并保证路径中没有中文。安装完成会看到“Setup was successful”的提示,但是在启动Python环境时可能会因为缺失一些动态链接库文件而导致Python解释器无法运行,常见的问题主要是api-ms-win-crt\*.dll缺失以及更新DirectX之后导致某些动态链接库文件缺失,前者可以参照[《api-ms-win-crt\*.dll缺失原因分析和解决方法》]()一文讲解的方法进行处理或者直接在[微软官网](https://www.microsoft.com/zh-cn/download/details.aspx?id=48145)下载Visual C++ Redistributable for Visual Studio 2015文件进行修复,后者可以下载一个DirectX修复工具进行修复。
-
-#### Linux环境
-
-Linux环境自带了Python 2.x版本,但是如果要更新到3.x的版本,可以在[Python的官方网站](https://www.python.org)下载Python的源代码并通过源代码构建安装的方式进行安装,具体的步骤如下所示。
-
-安装依赖库(因为没有这些依赖库可能在源代码构件安装时因为缺失底层依赖库而失败)。
-
-```Shell
-yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel
-```
-
-下载Python源代码并解压缩到指定目录。
-
-```Shell
-wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tar.xz
-xz -d Python-3.6.1.tar.xz
-tar -xvf Python-3.6.1.tar
-```
-
-切换至Python源代码目录并执行下面的命令进行配置和安装。
-
-```Shell
-cd Python-3.6.1
-./configure --prefix=/usr/local/python3.6 --enable-optimizations
-make && make install
-```
-
-创建软链接,这样就可以直接通过python3直接启动Python解释器。
-
-```Shell
-ln -s /usr/local/python3.6/bin/python3 /usr/bin/python3
-```
-
-
-#### MacOS环境
-
-MacOS也是自带了Python 2.x版本的,可以通过[Python的官方网站](https://www.python.org)提供的安装文件(pkg文件)安装3.x的版本。默认安装完成后,可以通过在终端执行python命令来启动2.x版本的Python解释器,可以通过执行python3命令来启动3.x版本的Python解释器,当然也可以通过重新设置软链接来修改启动Python解释器的命令。
-
-### 从终端运行Python程序
-
-#### 确认Python的版本
-
-在终端或命令行提示符中键入下面的命令。
-
-```Shell
-python --version
-```
-当然也可以先输入python进入交互式环境,再执行以下的代码检查Python的版本。
-
-```Python
-import sys
-
-print(sys.version_info)
-print(sys.version)
-```
-
-#### 编写Python源代码
-
-可以用文本编辑工具(推荐使用Sublime、Atom、TextMate、VSCode等高级文本编辑工具)编写Python源代码并将其命名为hello.py保存起来,代码内容如下所示。
-
-```Python
-print('hello, world!')
-```
-
-#### 运行程序
-
-切换到源代码所在的目录并执行下面的命令,看看屏幕上是否输出了"hello, world!"。
-
-```Shell
-python hello.py
-```
-
-### 代码中的注释
-
-注释是编程语言的一个重要组成部分,用于在源代码中解释代码的作用从而增强程序的可读性和可维护性,当然也可以将源代码中不需要参与运行的代码段通过注释来去掉,这一点在调试程序的时候经常用到。注释在随源代码进入预处理器或编译时会被移除,不会在目标代码中保留也不会影响程序的执行结果。
-
-1. 单行注释 - 以#和空格开头的部分
-2. 多行注释 - 三个引号开头,三个引号结尾
-
-```Python
-"""
-
-第一个Python程序 - hello, world!
-向伟大的Dennis M. Ritchie先生致敬
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-26
-
-"""
-
-print('hello, world!')
-# print("你好,世界!")
-print('你好', '世界')
-print('hello', 'world', sep=', ', end='!')
-print('goodbye, world', end='!\n')
-```
-
-### 其他工具介绍
-
-#### IDLE - 自带的集成开发工具
-
-IDLE是安装Python环境时自带的集成开发工具,如下图所示。但是由于IDLE的用户体验并不是那么好所以很少在实际开发中被采用。
-
-
-
-#### IPython - 更好的交互式编程工具
-
-IPython是一种基于Python的交互式解释器。相较于原生的Python Shell,IPython提供了更为强大的编辑和交互功能。可以通过Python的包管理工具pip安装IPython和Jupyter,具体的操作如下所示。
-
-```Shell
-pip install ipython jupyter
-```
-
-或者
-
-```Shell
-python -m pip install ipython jupyter
-```
-
-安装成功后,可以通过下面的ipython命令启动IPython,如下图所示。
-
-
-
-当然我们也可以通过Jupyter运行名为notebook的项目在浏览器窗口中进行交互式操作。
-
-```Shell
-jupyter notebook
-```
-
-
-
-
-
-#### Sublime - 文本编辑神器
-
-
-
-- 首先可以通过[官方网站](https://www.sublimetext.com/)下载安装程序安装Sublime 3或Sublime 2。
-
-- 安装包管理工具。通过快捷键Ctrl+`或者在View菜单中选择Show Console打开控制台,输入下面的代码。
-
- - Sublime 3
-
- ```Python
- import urllib.request,os;pf='Package Control.sublime-package';ipp=sublime.installed_packages_path();urllib.request.install_opener(urllib.request.build_opener(urllib.request.ProxyHandler()));open(os.path.join(ipp,pf),'wb').write(urllib.request.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read())
- ```
-
- - Sublime 2
-
- ```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')
- ```
-
-- 安装插件。通过Preference菜单的Package Control或快捷键Ctrl+Shift+P打开命令面板,在面板中输入Install Package就可以找到安装插件的工具,然后再查找需要的插件。我们推荐大家安装以下几个插件。
-
- - SublimeCodeIntel - 代码自动补全工具插件
- - Emmet - 前端开发代码模板插件
- - Git - 版本控制工具插件
- - Python PEP8 Autoformat - PEP8规范自动格式化插件
- - ConvertToUTF8 - 将本地编码转换为UTF-8
-
-#### PyCharm - Python开发神器
-
-PyCharm的安装、配置和使用我们在后面会进行介绍。
-
-
-
-### 练习
-
-1. 在Python交互环境中下面的代码查看结果并将内容翻译成中文。
-
- ```Python
- import this
-
- Beautiful is better than ugly.
- Explicit is better than implicit.
- Simple is better than complex.
- Complex is better than complicated.
- Flat is better than nested.
- Sparse is better than dense.
- Readability counts.
- Special cases aren't special enough to break the rules.
- Although practicality beats purity.
- Errors should never pass silently.
- Unless explicitly silenced.
- In the face of ambiguity, refuse the temptation to guess.
- There should be one-- and preferably only one --obvious way to do it.
- Although that way may not be obvious at first unless you're Dutch.
- Now is better than never.
- Although never is often better than *right* now.
- If the implementation is hard to explain, it's a bad idea.
- If the implementation is easy to explain, it may be a good idea.
- Namespaces are one honking great idea -- let's do more of those!
- ```
-
-2. 学习使用turtle在屏幕上绘制图形。
-
- ```Python
- import turtle
-
- turtle.pensize(4)
- turtle.pencolor('red')
- turtle.forward(100)
- turtle.right(90)
- turtle.forward(100)
- turtle.right(90)
- turtle.forward(100)
- turtle.right(90)
- turtle.forward(100)
- turtle.mainloop()
- ```
diff --git a/Day02/centigrade.py b/Day02/centigrade.py
deleted file mode 100644
index e6d5f55a6..000000000
--- a/Day02/centigrade.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-
-将华氏温度转换为摄氏温度
-F = 1.8C + 32
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-f = float(input('请输入华氏温度: '))
-c = (f - 32) / 1.8
-print('%.1f华氏度 = %.1f摄氏度' % (f, c))
diff --git a/Day02/circle.py b/Day02/circle.py
deleted file mode 100644
index 020bf65e8..000000000
--- a/Day02/circle.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""
-
-输入半径计算圆的周长和面积
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-import math
-
-radius = float(input('请输入圆的半径: '))
-perimeter = 2 * math.pi * radius
-area = math.pi * radius * radius
-print('周长: %.2f' % perimeter)
-print('面积: %.2f' % area)
diff --git a/Day02/leap.py b/Day02/leap.py
deleted file mode 100644
index d83cc74eb..000000000
--- a/Day02/leap.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-
-输入年份 如果是闰年输出True 否则输出False
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-year = int(input('请输入年份: '))
-# 如果代码太长写成一行不便于阅读 可以使用\或()折行
-is_leap = (year % 4 == 0 and year % 100 != 0 or
- year % 400 == 0)
-print(is_leap)
diff --git a/Day02/operator.py b/Day02/operator.py
deleted file mode 100644
index 0bbd9fc99..000000000
--- a/Day02/operator.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""
-
-运算符的使用
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-a = 5
-b = 10
-c = 3
-d = 4
-e = 5
-a += b
-a -= c
-a *= d
-a /= e
-print("a = ", a)
-
-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)
diff --git a/Day02/string.py b/Day02/string.py
deleted file mode 100644
index bc60550c2..000000000
--- a/Day02/string.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-
-字符串常用操作
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-str1 = 'hello, world!'
-print('字符串的长度是:', len(str1))
-print('单词首字母大写: ', str1.title())
-print('字符串变大写: ', str1.upper())
-# str1 = str1.upper()
-print('字符串是不是大写: ', str1.isupper())
-print('字符串是不是以hello开头: ', str1.startswith('hello'))
-print('字符串是不是以hello结尾: ', str1.endswith('hello'))
-print('字符串是不是以感叹号开头: ', str1.startswith('!'))
-print('字符串是不是一感叹号结尾: ', str1.endswith('!'))
-str2 = '- \u9a86\u660a'
-str3 = str1.title() + ' ' + str2.lower()
-print(str3)
diff --git a/Day02/variable1.py b/Day02/variable1.py
deleted file mode 100644
index f2c5fe379..000000000
--- a/Day02/variable1.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-
-使用变量保存数据并进行操作
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-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)
diff --git a/Day02/variable2.py b/Day02/variable2.py
deleted file mode 100644
index 398cc69f2..000000000
--- a/Day02/variable2.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-
-将input函数输入的数据保存在变量中并进行操作
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-a = int(input('a = '))
-b = int(input('b = '))
-print(a + b)
-print(a - b)
-print(a * b)
-print(a / b)
-print(a // b)
-print(a % b)
-print(a ** b)
diff --git a/Day02/variable3.py b/Day02/variable3.py
deleted file mode 100644
index 4e4b4e175..000000000
--- a/Day02/variable3.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-
-格式化输出
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-a = int(input('a = '))
-b = int(input('b = '))
-print('%d + %d = %d' % (a, b, a + b))
-print('%d - %d = %d' % (a, b, a - b))
-print('%d * %d = %d' % (a, b, a * b))
-print('%d / %d = %f' % (a, b, a / b))
-print('%d // %d = %d' % (a, b, a // b))
-print('%d %% %d = %d' % (a, b, a % b))
-print('%d ** %d = %d' % (a, b, a ** b))
diff --git a/Day02/variable4.py b/Day02/variable4.py
deleted file mode 100644
index 62a08233f..000000000
--- a/Day02/variable4.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
-
-检查变量的类型
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-a = 100
-b = 1000000000000000000
-c = 12.345
-d = 1 + 5j
-e = 'A'
-f = 'hello, world'
-g = True
-print(type(a))
-print(type(b))
-print(type(c))
-print(type(d))
-print(type(e))
-print(type(f))
-print(type(g))
diff --git a/Day02/variable5.py b/Day02/variable5.py
deleted file mode 100644
index 6560f2dc7..000000000
--- a/Day02/variable5.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
-
-类型转换
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-a = 100
-b = str(a)
-c = 12.345
-d = str(c)
-e = '123'
-f = int(e)
-g = '123.456'
-h = float(g)
-i = False
-j = str(i)
-k = 'hello'
-m = bool(k)
-print(a)
-print(type(a))
-print(b)
-print(type(b))
-print(c)
-print(type(c))
-print(d)
-print(type(d))
-print(e)
-print(type(e))
-print(f)
-print(type(f))
-print(g)
-print(type(g))
-print(h)
-print(type(h))
-print(i)
-print(type(i))
-print(j)
-print(type(j))
-print(k)
-print(type(k))
-print(m)
-print(type(m))
diff --git "a/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" "b/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md"
deleted file mode 100644
index 87a3af640..000000000
--- "a/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md"
+++ /dev/null
@@ -1,242 +0,0 @@
-## Day02 - 语言元素
-
-#### 指令和程序
-
-计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说的中央处理器,它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。我们通常所说的程序实际上就是指令的集合,我们程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。今天我们使用的计算机虽然器件做工越来越精密,处理能力越来越强大,但究其本质来说仍然属于[“冯·诺依曼结构”](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://www.baidu.com)科普一下。
-
-### 变量和类型
-
-在程序设计中,变量是一种存储数据的载体。计算机中的变量是实际存在的数据或者说是存储器中存储数据的一块内存空间,变量的值可以被读取和修改,这是所有计算和控制的基础。计算机能处理的数据有很多中类型,除了数值之外还可以处理文本、图形、音频、视频等各种各样的数据,那么不同的数据就需要定义不同的存储类型。Python中的数据类型很多,而且也允许我们自定义新的数据类型(这一点在后面会讲到),我们先介绍几种常用的数据类型。
-
-- 整型: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。
-
-#### 变量命名
-
-对于每个变量我们需要给它取一个名字,就如同我们每个人都有属于自己的响亮的名字一样。在Python中,变量命名需要遵循以下这些必须遵守硬性规则和强烈建议遵守的非硬性规则。
-
-- 硬性规则:
- - 变量名由字母(广义的Unicode字符,不包括特殊字符)、数字和下划线构成,数字不能开头。
- - 大小写敏感(大写的`a`和小写的`A`是两个不同的变量)。
- - 不要跟关键字(有特殊含义的单词,后面会讲到)和系统保留字(如函数、模块等的名字)冲突。
-- PEP 8要求:
- - 用小写字母拼写,多个单词用下划线连接。
- - 受保护的实例属性用单个下划线开头(后面会讲到)。
- - 私有的实例属性用两个下划线开头(后面会讲到)。
-
-当然,作为一个专业的程序员,给变量(事实上应该是所有的标识符)命名做到见名知意也是非常重要的。
-
-#### 变量的使用
-
-下面通过几个例子来说明变量的类型和变量使用。
-
-```Python
-"""
-
-使用变量保存数据并进行算术运算
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-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)
-
-```
-
-```Python
-"""
-
-使用input函数输入
-使用int()进行类型转换
-用占位符格式化输出的字符串
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-a = int(input('a = '))
-b = int(input('b = '))
-print('%d + %d = %d' % (a, b, a + b))
-print('%d - %d = %d' % (a, b, a - b))
-print('%d * %d = %d' % (a, b, a * b))
-print('%d / %d = %f' % (a, b, a / b))
-print('%d // %d = %d' % (a, b, a // b))
-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():将字符串(一个字符)转换成对应的编码(整数)。
-
-### 运算符
-
-Python支持多种运算符,下表大致按照优先级从高到低的顺序列出了所有的运算符,我们会陆续使用到它们。
-
-| 运算符 | 描述 |
-| ------------------------------------------------------------ | ------------------------------ |
-| `[]` `[:]` | 下标,切片 |
-| `**` | 指数 |
-| `~` `+` `-` | 按位取反, 正负号 |
-| `*` `/` `%` `//` | 乘,除,模,整除 |
-| `+` `-` | 加,减 |
-| `>>` `<<` | 右移,左移 |
-| `&` | 按位与 |
-| `^` `|` | 按位异或,按位或 |
-| `<=` `<` `>` `>=` | 小于等于,小于,大于,大于等于 |
-| `==` `!=` | 等于,不等于 |
-| `is` `is not` | 身份运算符 |
-| `in` `not in` | 成员运算符 |
-| `not` `or` `and` | 逻辑运算符 |
-| `=` `+=` `-=` `*=` `/=` `%=` `//=` `**=` `&=` `|=` `^=` `>>=` `<<=` | (复合)赋值运算符 |
-
->**说明:**在实际开发中,如果搞不清楚优先级可以使用括号来确保运算的执行顺序。
-
-下面的例子演示了运算符的使用。
-
-```Python
-"""
-
-运算符的使用
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-a = 5
-b = 10
-c = 3
-d = 4
-e = 5
-a += b
-a -= c
-a *= d
-a /= e
-print("a = ", a)
-
-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)
-
-```
-
-### 练习
-
-#### 练习1:华氏温度转摄氏温度。
-
-```Python
-"""
-
-将华氏温度转换为摄氏温度
-F = 1.8C + 32
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-f = float(input('请输入华氏温度: '))
-c = (f - 32) / 1.8
-print('%.1f华氏度 = %.1f摄氏度' % (f, c))
-
-```
-
-#### 练习2:输入圆的半径计算计算周长和面积。
-
-```Python
-"""
-
-输入半径计算圆的周长和面积
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-import math
-
-radius = float(input('请输入圆的半径: '))
-perimeter = 2 * math.pi * radius
-area = math.pi * radius * radius
-print('周长: %.2f' % perimeter)
-print('面积: %.2f' % area)
-
-```
-
-#### 练习3:输入年份判断是不是闰年。
-
-```Python
-"""
-
-输入年份 如果是闰年输出True 否则输出False
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-27
-
-"""
-
-year = int(input('请输入年份: '))
-# 如果代码太长写成一行不便于阅读 可以使用\或()折行
-is_leap = (year % 4 == 0 and year % 100 != 0 or
- year % 400 == 0)
-print(is_leap)
-```
-
diff --git a/Day03/.py b/Day03/.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/Day03/convert.py b/Day03/convert.py
deleted file mode 100644
index 2e2f587f9..000000000
--- a/Day03/convert.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-
-英制单位英寸和公制单位厘米互换
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-value = float(input('请输入长度: '))
-unit = input('请输入单位: ')
-if unit == 'in' or unit == '英寸':
- print('%f英寸 = %f厘米' % (value, value * 2.54))
-elif unit == 'cm' or unit == '厘米':
- print('%f厘米 = %f英寸' % (value, value / 2.54))
-else:
- print('请输入有效的单位')
diff --git a/Day03/grade.py b/Day03/grade.py
deleted file mode 100644
index a48ecedba..000000000
--- a/Day03/grade.py
+++ /dev/null
@@ -1,27 +0,0 @@
-"""
-
-百分制成绩转等级制成绩
-90分以上 --> A
-80分~89分 --> B
-70分~79分 --> C
-60分~69分 --> D
-60分以下 --> E
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-score = float(input('请输入成绩: '))
-if score >= 90:
- grade = 'A'
-elif score >= 80:
- grade = 'B'
-elif score >= 70:
- grade = 'C'
-elif score >= 60:
- grade = 'D'
-else:
- grade = 'E'
-print('对应的等级是:', grade)
diff --git a/Day03/piecewise.py b/Day03/piecewise.py
deleted file mode 100644
index fc6bfbd9b..000000000
--- a/Day03/piecewise.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""
-
-分段函数求值
- 3x - 5 (x > 1)
-f(x) = x + 2 (-1 <= x <= 1)
- 5x + 3 (x < -1)
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-x = float(input('x = '))
-if x > 1:
- y = 3 * x - 5
-elif x >= -1:
- y = x + 2
-else:
- y = 5 * x + 3
-print('f(%.2f) = %.2f' % (x, y))
diff --git a/Day03/rolldice.py b/Day03/rolldice.py
deleted file mode 100644
index 76373d0e8..000000000
--- a/Day03/rolldice.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""
-
-掷骰子决定做什么事情
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-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)
diff --git a/Day03/tax.py b/Day03/tax.py
deleted file mode 100644
index d404bff64..000000000
--- a/Day03/tax.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""
-
-输入月收入和五险一金计算个人所得税
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-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))
diff --git a/Day03/triangle.py b/Day03/triangle.py
deleted file mode 100644
index 4c4c7a437..000000000
--- a/Day03/triangle.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-
-判断输入的边长能否构成三角形
-如果能则计算出三角形的周长和面积
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-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))
- print('面积: %f' % (area))
-else:
- print('不能构成三角形')
diff --git a/Day03/verify.py b/Day03/verify.py
deleted file mode 100644
index e2587dc44..000000000
--- a/Day03/verify.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""
-
-用户身份验证
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-# import getpass
-# from getpass import getpass
-# from getpass import *
-
-username = input('请输入用户名: ')
-password = input('请输入口令: ')
-# 输入口令的时候终端中没有回显
-# password = getpass.getpass('请输入口令: ')
-if username == 'admin' and password == '123456':
- print('身份验证成功!')
-else:
- print('身份验证失败!')
diff --git "a/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" "b/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md"
deleted file mode 100644
index 23da1e84d..000000000
--- "a/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md"
+++ /dev/null
@@ -1,262 +0,0 @@
-## Day03 - 分支结构
-
-### 分支结构的应用场景
-
-迄今为止,我们写的Python代码都是一条一条语句顺序执行,这种结构的代码我们称之为顺序结构。然而仅有顺序结构并不能解决所有的问题,比如我们设计一个游戏,游戏第一关的通关条件是玩家获得1000分,那么在完成本局游戏后我们要根据玩家得到分数来决定究竟是进入第二关还是告诉玩家“Game Over”,这里就会产生两个分支,而且这两个分支只有一个会被执行,这就是程序中分支结构。类似的场景还有很多,给大家一分钟的时间,你应该可以想到至少5个以上这样的例子,赶紧试一试。
-
-### if语句的使用
-
-在Python中,要构造分支结构可以使用`if`、`elif`和`else`关键字。所谓关键字就是有特殊含义的单词,像`if`和`else`就是专门用于构造分支结构的关键字,很显然你不能够使用它作为变量名(事实上,用作其他的标识符也是不可以)。下面的例子中演示了如何构造一个分支结构。
-
-```Python
-"""
-
-用户身份验证
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-username = input('请输入用户名: ')
-password = input('请输入口令: ')
-# 如果希望输入口令时 终端中没有回显 可以使用getpass模块的getpass函数
-# import getpass
-# password = getpass.getpass('请输入口令: ')
-if username == 'admin' and password == '123456':
- print('身份验证成功!')
-else:
- print('身份验证失败!')
-
-```
-
-唯一需要说明的是和C/C++、Java等语言不同,Python中没有用花括号来构造代码块而是使用了缩进的方式来设置代码的层次结构,如果`if`条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了,换句话说连续的代码如果又保持了相同的缩进那么它们属于同一个代码块,相当于是一个执行的整体。
-
-当然如果要构造出更多的分支,可以使用`if…elif…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}$$
-
-```Python
-"""
-
-分段函数求值
-
- 3x - 5 (x > 1)
-f(x) = x + 2 (-1 <= x <= 1)
- 5x + 3 (x < -1)
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-x = float(input('x = '))
-if x > 1:
- y = 3 * x - 5
-elif x >= -1:
- y = x + 2
-else:
- y = 5 * x + 3
-print('f(%.2f) = %.2f' % (x, y))
-
-```
-
-当然根据实际开发的需要,分支结构是可以嵌套的,例如判断是否通关以后还要根据你获得的宝物或者道具的数量对你的表现给出等级(比如点亮两颗或三颗星星),那么我们就需要在`if`的内部构造出一个新的分支结构,同理`elif`和`else`中也可以再构造新的分支,我们称之为嵌套的分支结构,也就是说上面的代码也可以写成下面的样子。
-
-```Python
-"""
-
-分段函数求值
- 3x - 5 (x > 1)
-f(x) = x + 2 (-1 <= x <= 1)
- 5x + 3 (x < -1)
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-x = float(input('x = '))
-if x > 1:
- y = 3 * x - 5
-else:
- if x >= -1:
- y = x + 2
- else:
- y = 5 * x + 3
-print('f(%.2f) = %.2f' % (x, y))
-```
-
-> **说明:**大家可以自己感受一下这两种写法到底是哪一种更好。在之前我们提到的Python之禅中有这么一句话“Flat is bettern than nested.”,之所以提出这个观点是因为嵌套结构的嵌套层次多了之后会严重的影响代码的可读性,如果可以使用扁平化的结构就不要去用嵌套,因此之前的写法是更好的做法。
-
-### 练习
-
-#### 练习1:英制单位与公制单位互换
-
-```Python
-"""
-
-英制单位英寸和公制单位厘米互换
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-value = float(input('请输入长度: '))
-unit = input('请输入单位: ')
-if unit == 'in' or unit == '英寸':
- print('%f英寸 = %f厘米' % (value, value * 2.54))
-elif unit == 'cm' or unit == '厘米':
- print('%f厘米 = %f英寸' % (value, value / 2.54))
-else:
- print('请输入有效的单位')
-
-```
-
-#### 练习2:掷骰子决定做什么
-
-```Python
-"""
-
-掷骰子决定做什么事情
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-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: 骆昊
-Date: 2018-02-28
-
-"""
-
-score = float(input('请输入成绩: '))
-if score >= 90:
- grade = 'A'
-elif score >= 80:
- grade = 'B'
-elif score >= 70:
- grade = 'C'
-elif score >= 60:
- grade = 'D'
-else:
- grade = 'E'
-print('对应的等级是:', grade)
-
-```
-#### 练习4:输入三条边长如果能构成三角形就计算周长和面积
-
-```Python
-"""
-
-判断输入的边长能否构成三角形
-如果能则计算出三角形的周长和面积
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-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))
- print('面积: %f' % (area))
-else:
- print('不能构成三角形')
-
-```
-> **说明:**上面的代码中使用了`math`模块的`sqrt`函数来计算平方根。用边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。
-
-#### 练习5:实现一个个人所得税计算器。
-
-```Python
-"""
-
-输入月收入和五险一金计算个人所得税
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-02-28
-
-"""
-
-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`的问题。
-
diff --git a/Day04/for1.py b/Day04/for1.py
deleted file mode 100644
index e2a0783ec..000000000
--- a/Day04/for1.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-
-用for循环实现1~100求和
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-sum = 0
-for x in range(1, 101):
- if x % 2 == 0:
- sum += x
-print(sum)
diff --git a/Day04/for2.py b/Day04/for2.py
deleted file mode 100644
index 65bd932b6..000000000
--- a/Day04/for2.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-
-用for循环实现1~100之间的偶数求和
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-sum = 0
-for x in range(2, 101, 2):
- sum += x
-print(sum)
diff --git a/Day04/for3.py b/Day04/for3.py
deleted file mode 100644
index bfc9d8533..000000000
--- a/Day04/for3.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-
-输入非负整数n计算n!
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-n = int(input('n = '))
-result = 1
-for x in range(1, n + 1):
- result *= x
-print('%d! = %d' % (n, result))
diff --git a/Day04/for4.py b/Day04/for4.py
deleted file mode 100644
index 0cd4e548f..000000000
--- a/Day04/for4.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-
-输入一个正整数判断它是不是素数
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-from math import sqrt
-
-num = int(input('请输入一个正整数: '))
-end = int(sqrt(num))
-is_prime = True
-for x in range(2, end + 1):
- if num % x == 0:
- is_prime = False
- break
-if is_prime and num != 1:
- print('%d是素数' % num)
-else:
- print('%d不是素数' % num)
diff --git a/Day04/for5.py b/Day04/for5.py
deleted file mode 100644
index b4683fa09..000000000
--- a/Day04/for5.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-
-输入两个正整数计算最大公约数和最小公倍数
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-x = int(input('x = '))
-y = int(input('y = '))
-if 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))
- print('%d和%d的最小公倍数是%d' % (x, y, x * y // factor))
- break
diff --git a/Day04/for6.py b/Day04/for6.py
deleted file mode 100644
index ed3c0fc73..000000000
--- a/Day04/for6.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-
-打印各种三角形图案
-
-*
-**
-***
-****
-*****
-
- *
- **
- ***
- ****
-*****
-
- *
- ***
- *****
- *******
-*********
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-row = int(input('请输入行数: '))
-for i in range(row):
- for _ in range(i + 1):
- print('*', end='')
- print()
-
-for i in range(row):
- for j in range(row):
- if j < row - i - 1:
- print(' ', end='')
- else:
- print('*', end='')
- print()
-
-for i in range(row):
- for _ in range(row - i - 1):
- print(' ', end='')
- for _ in range(2 * i + 1):
- print('*', end='')
- print()
diff --git a/Day04/while1.py b/Day04/while1.py
deleted file mode 100644
index 3a3d6e6cc..000000000
--- a/Day04/while1.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""
-
-用while循环实现1~100求和
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-sum = 0
-num = 1
-while num <= 100:
- sum += num
- num += 1
-print(sum)
diff --git a/Day04/while2.py b/Day04/while2.py
deleted file mode 100644
index 1e7066cc8..000000000
--- a/Day04/while2.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""
-
-用while循环实现1~100之间的偶数求和
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-sum = 0
-num = 2
-while num <= 100:
- sum += num
- num += 2
-print(sum)
diff --git "a/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" "b/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md"
deleted file mode 100644
index af5ab166d..000000000
--- "a/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md"
+++ /dev/null
@@ -1,243 +0,0 @@
-## Day04 - 循环结构
-
-### 循环结构的应用场景
-
-如果在程序中我们需要重复的执行某条或某些指令,例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了,刚才的描述中其实不仅仅有需要重复的动作,还有我们上一个章节讲到的分支结构。再举一个简单的例子,比如在我们的程序中要实现每隔1秒中在屏幕上打印一个"hello, world"这样的字符串并持续一个小时,我们肯定不能够将`print('hello, world')`这句代码写上3600遍,如果真的需要这样做那么我们的工作就太无聊了。因此,我们需要循环结构,使用循环结构我们就可以轻松的控制某件事或者某些事重复、重复、再重复的发生。在Python中构造循环结构有两种做法,一种是`for-in`循环,一种是`while`循环。
-
-### for-in循环
-
-如果明确的知道循环执行的次数或者是要对一个容器进行迭代(后面会讲到),那么我们推荐使用`for-in`循环,例如下面代码中计算$\sum_{n=1}^{100}n$。
-
-```Python
-"""
-
-用for循环实现1~100求和
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-sum = 0
-for x in range(101):
- sum += x
-print(sum)
-```
-
-需要说明的是上面代码中的`range`类型,`range`可以用来产生一个不变的数值序列,而且这个序列通常都是用在循环中的,例如:
-
-- `range(101)`可以产生一个0到100的整数序列。
-- `range(1, 100)`可以产生一个1到99的整数序列。
-- `range(1, 100, 2)`可以产生一个1到99的奇数序列,其中的2是步长,即数值序列的增量。
-
-知道了这一点,我们可以用下面的代码来实现1~100之间的偶数求和。
-
-```Python
-"""
-
-用for循环实现1~100之间的偶数求和
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-sum = 0
-for x in range(2, 101, 2):
- sum += x
-print(sum)
-```
-
-也可以通过在循环中使用分支结构的方式来实现相同的功能,代码如下所示。
-
-```Python
-"""
-
-用for循环实现1~100之间的偶数求和
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-sum = 0
-for x in range(1, 101):
- if x % 2 == 0:
- sum += x
-print(sum)
-
-```
-
-### while循环
-
-如果要构造不知道具体循环次数的循环结构,我们推荐使用`while`循环,`while`循环通过一个能够产生或转换出`bool`值的表达式来控制循环,表达式的值为`True`循环继续,表达式的值为`False`循环结束。下面我们通过一个“猜数字”的小游戏(计算机出一个1~100之间的随机数,人输入自己猜的数字,计算机给出对应的提示信息,直到人猜出计算机出的数字)来看看如何使用`while`循环。
-
-```Python
-"""
-
-猜数字游戏
-计算机出一个1~100之间的随机数由人来猜
-计算机根据人猜的数字分别给出提示大一点/小一点/猜对了
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-import random
-
-answer = random.randint(1, 100)
-counter = 0
-while True:
- counter += 1
- number = int(input('请输入: '))
- if number < answer:
- print('大一点')
- elif number > answer:
- print('小一点')
- else:
- print('恭喜你猜对了!')
- break
-print('你总共猜了%d次' % counter)
-if counter > 7:
- print('你的智商余额明显不足')
-
-```
-
-> **说明:**上面的代码中使用了`break`关键字来提前终止循环,需要注意的是`break`只能终止它所在的那个循环,这一点在使用嵌套的循环结构(下面会讲到)需要引起注意。除了`break`之外,还有另一个关键字是`continue`,它可以用来放弃本次循环后续的代码直接让循环进入下一轮。
-
-和分支结构一样,循环结构也是可以嵌套的,也就是说在循环中还可以构造循环结构。下面的例子演示了如何通过嵌套的循环来输出一个九九乘法表。
-
-```Python
-"""
-
-输出乘法口诀表(九九表)
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-for i in range(1, 10):
- for j in range(1, i + 1):
- print('%d*%d=%d' % (i, j, i * j), end='\t')
- print()
-
-```
-
-### 练习
-
-#### 练习1:输入一个数判断是不是素数。
-
-```Python
-"""
-
-输入一个正整数判断它是不是素数
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-from math import sqrt
-
-num = int(input('请输入一个正整数: '))
-end = int(sqrt(num))
-is_prime = True
-for x in range(2, end + 1):
- if num % x == 0:
- is_prime = False
- break
-if is_prime and num != 1:
- print('%d是素数' % num)
-else:
- print('%d不是素数' % num)
-
-```
-
-#### 练习2:输入两个正整数,计算最大公约数和最小公倍数。
-
-```Python
-"""
-
-输入两个正整数计算最大公约数和最小公倍数
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-x = int(input('x = '))
-y = int(input('y = '))
-if 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))
- print('%d和%d的最小公倍数是%d' % (x, y, x * y // factor))
- break
-
-```
-
-#### 练习3:打印三角形图案。
-
-```Python
-"""
-
-打印各种三角形图案
-
-*
-**
-***
-****
-*****
-
- *
- **
- ***
- ****
-*****
-
- *
- ***
- *****
- *******
-*********
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-01
-
-"""
-
-row = int(input('请输入行数: '))
-for i in range(row):
- for _ in range(i + 1):
- print('*', end='')
- print()
-
-
-for i in range(row):
- for j in range(row):
- if j < row - i - 1:
- print(' ', end='')
- else:
- print('*', end='')
- print()
-
-for i in range(row):
- for _ in range(row - i - 1):
- print(' ', end='')
- for _ in range(2 * i + 1):
- print('*', end='')
- print()
-
-```
-
diff --git a/Day05/chicken.py b/Day05/chicken.py
deleted file mode 100644
index 639cb0078..000000000
--- a/Day05/chicken.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-
-求解《百钱百鸡》问题
-1只公鸡5元 1只母鸡3元 3只小鸡1元 用100元买100只鸡
-问公鸡 母鸡 小鸡各有多少只
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-
-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))
-
-# 要理解程序背后的算法 - 穷举法
diff --git a/Day05/craps.py b/Day05/craps.py
deleted file mode 100644
index d0b66b897..000000000
--- a/Day05/craps.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-
-Craps赌博游戏
-玩家摇两颗色子 如果第一次摇出7点或11点 玩家胜
-如果摇出2点 3点 12点 庄家胜 其他情况游戏继续
-玩家再次要色子 如果摇出7点 庄家胜
-如果摇出第一次摇的点数 玩家胜
-否则游戏继续 玩家继续摇色子
-玩家进入游戏时有1000元的赌注 全部输光游戏结束
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-
-from random import randint
-
-money = 1000
-while money > 0:
- print('你的总资产为:', money)
- needs_go_on = False
- while True:
- debt = int(input('请下注: '))
- if debt > 0 and 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:
- current = randint(1, 6) + randint(1, 6)
- print('玩家摇出了%d点' % current)
- if current == 7:
- print('庄家胜')
- money -= debt
- needs_go_on = False
- elif current == first:
- print('玩家胜')
- money += debt
- needs_go_on = False
-
-print('你破产了, 游戏结束!')
diff --git a/Day05/fibonacci.py b/Day05/fibonacci.py
deleted file mode 100644
index c6a72b9b6..000000000
--- a/Day05/fibonacci.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""
-
-输出斐波那契数列的前20个数
-1 1 2 3 5 8 13 21 ...
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-
-a = 0
-b = 1
-for _ in range(20):
- (a, b) = (b, a + b)
- print(a, end=' ')
diff --git a/Day05/guess.py b/Day05/guess.py
deleted file mode 100644
index cd8880b60..000000000
--- a/Day05/guess.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""
-
-猜数字游戏
-计算机出一个1~100之间的随机数由人来猜
-计算机根据人猜的数字分别给出提示大一点/小一点/猜对了
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-
-import random
-
-answer = random.randint(1, 100)
-counter = 0
-while True:
- counter += 1
- number = int(input('请输入: '))
- if number < answer:
- print('大一点')
- elif number > answer:
- print('小一点')
- else:
- print('恭喜你猜对了!')
- break
-print('你总共猜了%d次' % counter)
-if counter > 7:
- print('你的智商余额明显不足')
diff --git a/Day05/lily.py b/Day05/lily.py
deleted file mode 100644
index a33361919..000000000
--- a/Day05/lily.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-
-找出100~999之间的所有水仙花数
-水仙花数是各位立方和等于这个数本身的数
-如: 153 = 1**3 + 5**3 + 3**3
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-
-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)
diff --git a/Day05/palindrome.py b/Day05/palindrome.py
deleted file mode 100644
index 02768f580..000000000
--- a/Day05/palindrome.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""
-
-判断输入的正整数是不是回文数
-回文数是指将一个正整数从左往右排列和从右往左排列值一样的数
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-
-num = int(input('请输入一个正整数: '))
-temp = num
-num2 = 0
-while temp > 0:
- num2 *= 10
- num2 += temp % 10
- temp //= 10
-if num == num2:
- print('%d是回文数' % num)
-else:
- print('%d不是回文数' % num)
diff --git a/Day05/perfect.py b/Day05/perfect.py
deleted file mode 100644
index b778e94eb..000000000
--- a/Day05/perfect.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""
-
-找出1~9999之间的所有完美数
-完美数是除自身外其他所有因子的和正好等于这个数本身的数
-例如: 6 = 1 + 2 + 3, 28 = 1 + 2 + 4 + 7 + 14
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-import time
-import math
-
-start = time.clock()
-for num in range(1, 10000):
- sum = 0
- for factor in range(1, int(math.sqrt(num)) + 1):
- if num % factor == 0:
- sum += factor
- if factor > 1 and num / factor != factor:
- sum += num / factor
- if sum == num:
- print(num)
-end = time.clock()
-print("执行时间:", (end - start), "秒")
-
-# 通过比较上面两种不同的解决方案的执行时间 意识到优化程序的重要性
diff --git a/Day05/prime.py b/Day05/prime.py
deleted file mode 100644
index b6d49a6ef..000000000
--- a/Day05/prime.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-
-输出2~99之间的素数
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-
-import math
-
-for num in range(2, 100):
- is_prime = True
- for factor in range(2, int(math.sqrt(num)) + 1):
- if num % factor == 0:
- is_prime = False
- break
- if is_prime:
- print(num, end=' ')
diff --git a/Day05/table.py b/Day05/table.py
deleted file mode 100644
index 470caf1ed..000000000
--- a/Day05/table.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-
-输出乘法口诀表(九九表)
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-02
-
-"""
-
-for i in range(1, 10):
- for j in range(1, i + 1):
- print('%d*%d=%d' % (i, j, i * j), end='\t')
- print()
diff --git "a/Day05/\347\273\203\344\271\240.md" "b/Day05/\347\273\203\344\271\240.md"
deleted file mode 100644
index 82923d8bd..000000000
--- "a/Day05/\347\273\203\344\271\240.md"
+++ /dev/null
@@ -1 +0,0 @@
-## 练习
diff --git a/Day06/function1.py b/Day06/function1.py
deleted file mode 100644
index d1c92f4cb..000000000
--- a/Day06/function1.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-
-函数的定义和使用 - 计算组合数C(7,3)
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-05
-
-"""
-
-
-# 将求阶乘的功能封装成一个函数
-def factorial(n):
- result = 1
- for num in range(1, n + 1):
- result *= num
- return result
-
-
-print(factorial(7) // factorial(3) // factorial(4))
diff --git a/Day06/function2.py b/Day06/function2.py
deleted file mode 100644
index b2ed2838c..000000000
--- a/Day06/function2.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""
-
-函数的定义和使用 - 求最大公约数和最小公倍数
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-05
-
-"""
-
-
-def gcd(x, y):
- if x > y:
- (x, y) = (y, x)
- for factor in range(x, 1, -1):
- if x % factor == 0 and y % factor == 0:
- return factor
- return 1
-
-
-def lcm(x, y):
- return x * y // gcd(x, y)
-
-
-print(gcd(15, 27))
-print(lcm(15, 27))
diff --git a/Day06/function3.py b/Day06/function3.py
deleted file mode 100644
index 16a980fee..000000000
--- a/Day06/function3.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""
-
-Python的内置函数
- - 数学相关: abs / divmod / pow / round / min / max / sum
- - 序列相关: len / range / next / filter / map / sorted / slice / reversed
- - 类型转换: chr / ord / str / bool / int / float / complex / bin / oct / hex
- - 数据结构: dict / list / set / tuple
- - 其他函数: all / any / id / input / open / print / type
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-05
-
-"""
-
-
-def myfilter(mystr):
- return len(mystr) == 6
-
-
-# help()
-print(chr(0x9a86))
-print(hex(ord('骆')))
-print(abs(-1.2345))
-print(round(-1.2345))
-print(pow(1.2345, 5))
-fruits = ['orange', 'peach', 'durian', 'watermelon']
-print(fruits[slice(1, 3)])
-fruits2 = list(filter(myfilter, fruits))
-print(fruits)
-print(fruits2)
diff --git a/Day06/function4.py b/Day06/function4.py
deleted file mode 100644
index 22d076fbb..000000000
--- a/Day06/function4.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-
-Python常用模块
- - 运行时服务相关模块: copy / pickle / sys / ...
- - 数学相关模块: decimal / math / random / ...
- - 字符串处理模块: codecs / re / ...
- - 文件处理相关模块: shutil / gzip / ...
- - 操作系统服务相关模块: datetime / os / time / logging / io / ...
- - 进程和线程相关模块: multiprocessing / threading / queue
- - 网络应用相关模块: ftplib / http / smtplib / urllib / ...
- - Web编程相关模块: cgi / webbrowser
- - 数据处理和编码模块: base64 / csv / html.parser / json / xml / ...
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-05
-
-"""
-
-import time
-import shutil
-import os
-
-seconds = time.time()
-print(seconds)
-localtime = time.localtime(seconds)
-print(localtime)
-print(localtime.tm_year)
-print(localtime.tm_mon)
-print(localtime.tm_mday)
-asctime = time.asctime(localtime)
-print(asctime)
-strtime = time.strftime('%Y-%m-%d %H:%M:%S', localtime)
-print(strtime)
-mydate = time.strptime('2018-1-1', '%Y-%m-%d')
-print(mydate)
-
-shutil.copy('/Users/Hao/hello.py', '/Users/Hao/Desktop/first.py')
-os.system('ls -l')
-os.chdir('/Users/Hao')
-os.system('ls -l')
-os.mkdir('test')
diff --git a/Day06/function5.py b/Day06/function5.py
deleted file mode 100644
index 90b618747..000000000
--- a/Day06/function5.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""
-
-函数的参数
- - 默认参数
- - 可变参数
- - 关键字参数
- - 命名关键字参数
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-05
-
-"""
-
-
-# 参数默认值
-def f1(a, b=5, c=10):
- return a + b * 2 + c * 3
-
-
-print(f1(1, 2, 3))
-print(f1(100, 200))
-print(f1(100))
-print(f1(c=2, b=3, a=1))
-
-
-# 可变参数
-def f2(*args):
- sum = 0
- for num in args:
- sum += num
- return sum
-
-
-print(f2(1, 2, 3))
-print(f2(1, 2, 3, 4, 5))
-print(f2())
-
-
-# 关键字参数
-def f3(**kw):
- if 'name' in kw:
- print('欢迎你%s!' % kw['name'])
- elif 'tel' in kw:
- print('你的联系电话是: %s!' % kw['tel'])
- else:
- print('没找到你的个人信息!')
-
-
-param = {'name': '骆昊', 'age': 38}
-f3(**param)
-f3(name='骆昊', age=38, tel='13866778899')
-f3(user='骆昊', age=38, tel='13866778899')
-f3(user='骆昊', age=38, mobile='13866778899')
diff --git a/Day06/function6.py b/Day06/function6.py
deleted file mode 100644
index 9ad898771..000000000
--- a/Day06/function6.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""
-
-作用域问题
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-05
-
-"""
-
-
-# 局部作用域
-def foo1():
- a = 5
-
-
-foo1()
-# print(a) # NameError
-
-# 全局作用域
-b = 10
-
-
-def foo2():
- print(b)
-
-
-foo2()
-
-
-def foo3():
- b = 100 # 局部变量
- print(b)
-
-
-foo3()
-print(b)
-
-
-def foo4():
- global b
- b = 200 # 全局变量
- print(b)
-
-
-foo4()
-print(b)
diff --git "a/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" "b/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md"
deleted file mode 100644
index a3550de63..000000000
--- "a/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md"
+++ /dev/null
@@ -1,368 +0,0 @@
-## 函数和模块的使用
-
-在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解。
-
-$$x_1 + x_2 + x_3 + x_4 = 8$$
-
-事实上,上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案。想到这一点问题的答案就呼之欲出了。
-
-$$C_M^N =\frac{M!}{N!(M-N)!}, \text{(M=7, N=3)} $$
-
-可以用Python的程序来计算出这个值,代码如下所示。
-
-```Python
-"""
-
-输入M和N计算C(M,N)
-
-"""
-
-m = int(input('m = '))
-n = int(input('n = '))
-fm = 1
-for num in range(1, m + 1):
- fm *= num
-fn = 1
-for num in range(1, n + 1):
- fn *= num
-fmn = 1
-for num in range(1, m - n + 1):
- fmn *= num
-print(fm // fn // fmn)
-
-```
-
-### 函数的作用
-
-不知道大家是否注意到,在上面的代码中,我们做了3次求阶乘,这样的代码实际上就是重复代码。编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”,要写出高质量的代码首先要解决的就是重复代码的问题。对于上面的代码来说,我们可以将计算阶乘的功能封装到一个称之为“函数”的功能模块中,在需要计算阶乘的地方,我们只需要“调用”这个“函数”就可以了。
-
-### 定义函数
-
-在Python中可以使用`def`关键字来定义函数,和变量一样每个函数也有一个响亮的名字,而且命名规则跟变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给函数的参数,这一点和数学上的函数非常相似,程序中函数的参数就相当于是数学上说的函数的自变量,而函数执行完成后我们可以通过`return`关键字来返回一个值,这相当于数学上说的函数的因变量。
-
-在了解了如何定义函数后,我们可以对上面的代码进行重构,所谓重构就是在不影响代码执行结果的前提下对代码的结构进行调整,重构之后的代码如下所示。
-
-```Python
-def factorial(num):
- """
- 求阶乘
-
- :param num: 非负整数
-
- :return: num的阶乘
- """
- result = 1
- for n in range(1, num + 1):
- result *= n
- return result
-
-
-m = int(input('m = '))
-n = int(input('n = '))
-# 当需要计算阶乘的时候不用再写循环求阶乘而是直接调用已经定义好的函数
-print(factorial(m) // factorial(n) // factorial(m - n))
-
-```
-
-> **说明:**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
-from random import randint
-
-
-def roll_dice(n=2):
- """
- 摇色子
-
- :param n: 色子的个数
-
- :return: n颗色子点数之和
- """
- total = 0
- for _ in range(n):
- total += randint(1, 6)
- return total
-
-
-def add(a=0, b=0, c=0):
- return a + b + c
-
-
-# 如果没有指定参数那么使用默认值摇两颗色子
-print(roll_dice())
-# 摇三颗色子
-print(roll_dice(3))
-print(add())
-print(add(1))
-print(add(1, 2))
-print(add(1, 2, 3))
-# 传递参数时可以不按照设定的顺序进行传递
-print(add(c=50, a=100, b=200))
-
-```
-
-我们给上面两个函数的参数都设定了默认值,这也就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值,所以在上面的代码中我们可以用各种不同的方式去调用`add`函数,这跟其他很多语言中函数重载的效果是一致的。
-
-其实上面的`add`函数还有更好的实现方案,因为我们可能会对0个或多个参数进行加法运算,而具体有多少个参数是由调用者来决定,我们作为函数的设计者对这一点是一无所知的,因此在不确定参数个数的时候,我们可以使用可变参数,代码如下所示。
-
-```Python
-# 在参数前使用*表示args是可变参数
-# 也就是说调用add函数时传入的参数个数可以是0个或多个
-def add(*args):
- total = 0
- for val in args:
- total += val
- return total
-
-
-print(add())
-print(add(1))
-print(add(1, 2))
-print(add(1, 2, 3))
-print(add(1, 3, 5, 7, 9))
-
-```
-
-### 用模块管理函数
-
-对于任何一种编程语言来说,给变量、函数这样的标识符起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。最简单的场景就是在同一个.py文件中定义了两个同名函数,由于Python没有函数重载的概念,那么后面的定义会覆盖之前的定义,也就意味着两个函数同名函数实际上只有一个是存在的。
-
-```Python
-def foo():
- print('hello, world!')
-
-
-def foo():
- print('goodbye, world!')
-
-
-foo() # 输出goodbye, world!
-
-```
-
-当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。
-
-module1.py
-
-```Python
-def foo():
- print('hello, world!')
-```
-
-module2.py
-
-```Python
-def foo():
- print('goodbye, world!')
-```
-
-test.py
-
-```Python
-from module1 import foo
-
-foo() # 输出hello, world!
-
-from module2 import foo
-
-foo() # 输出goodbye, world!
-
-```
-
-也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。
-
-test.py
-
-```Python
-import module1 as m1
-import module2 as m2
-
-m1.foo()
-m2.foo()
-
-```
-
-但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个`foo`,因为后导入的foo覆盖了之前导入的`foo`。
-
-test.py
-
-```Python
-from module1 import foo
-from module2 import foo
-
-foo() # 输出goodbye, world!
-
-```
-
-test.py
-
-```Python
-from module2 import foo
-from module1 import foo
-
-foo() # 输出hello, world!
-
-```
-
-需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是“\_\_main\_\_”。
-
-module3.py
-
-```Python
-def foo():
- pass
-
-
-def bar():
- pass
-
-
-# __name__是Python中一个隐含的变量它代表了模块的名字
-# 只有被Python解释器直接执行的模块的名字才是__main__
-if __name__ == '__main__':
- print('call foo()')
- foo()
- print('call bar()')
- bar()
-
-```
-
-test.py
-
-```Python
-import module3
-
-# 导入module3时 不会执行模块中if条件成立时的代码 因为模块的名字是module3而不是__main__
-
-```
-
-### 练习
-
-#### 练习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:
- return factor
-
-
-def lcm(x, y):
- return x * y // gcd(x, y)
-```
-
-#### 练习2:实现判断一个数是不是回文数的函数。
-
-```Python
-def is_palindrome(num):
- temp = num
- total = 0
- while temp > 0:
- total = total * 10 + temp % 10
- temp //= 10
- return total == num
-```
-
-#### 练习3:实现判断一个数是不是素数的函数。
-
-```Python
-def is_prime(num):
- for factor in range(2, num):
- if num % factor == 0:
- return False
- return True if num != 1 else False
-```
-
-#### 练习4:写一个程序判断输入的正整数是不是回文素数。
-
-```Python
-if __name__ == '__main__':
- num = int(input('请输入正整数: '))
- if is_palindrome(num) and is_prime(num):
- print('%d是回文素数' % num)
-
-```
-
-通过上面的程序可以看出,当我们将代码中重复出现的和相对独立的功能抽取成函数后,我们可以组合使用这些函数来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。
-
-最后,我们来讨论一下Python中有关变量作用域的问题。
-
-```Python
-def foo():
- b = 'hello'
-
- def bar(): # Python中可以在函数内部再定义函数
- c = True
- print(a)
- print(b)
- print(c)
-
- bar()
- # print(c) # NameError: name 'c' is not defined
-
-
-if __name__ == '__main__':
- a = 100
- # print(b) # NameError: name 'b' is not defined
- 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`等都属于内置作用域)。
-
-再看看下面这段代码,我们希望通过函数调用修改全局变量`a`的值,但实际上下面的代码是做不到的。
-
-```Python
-def foo():
- a = 200
- print(a) # 200
-
-
-if __name__ == '__main__':
- a = 100
- foo()
- print(a) # 100
-
-```
-
-在调用`foo`函数后,我们发现`a`的值仍然是100,这是因为当我们在函数`foo`中写`a = 200`的时候,是重新定义了一个名字为`a`的局部变量,它跟全局作用域的`a`并不是同一个变量,因为局部作用域中有了自己的变量`a`,因此`foo`函数不再搜索全局作用域中的`a`。如果我们希望在`foo`函数中修改全局作用域中的`a`,代码如下所示。
-
-```Python
-def foo():
- global a
- a = 200
- print(a) # 200
-
-
-if __name__ == '__main__':
- a = 100
- foo()
- print(a) # 200
-
-```
-
-我们可以使用`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%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们是不同的概念,如果想提前了解这个概念,推荐看看[维基百科](https://zh.wikipedia.org/wiki/)或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。
-
-说了那么多,其实结论很简单,从现在开始我们可以将Python代码按照下面的格式进行书写,这一点点的改进其实就是在我们理解了函数和作用域的基础上跨出的巨大的一步。
-
-```Python
-def main():
- # Todo: Add your code here
- pass
-
-
-if __name__ == '__main__':
- main()
-
-```
-
diff --git a/Day07/avgscore.py b/Day07/avgscore.py
deleted file mode 100644
index 7c525ccc6..000000000
--- a/Day07/avgscore.py
+++ /dev/null
@@ -1,27 +0,0 @@
-"""
-
-输入学生考试成绩计算平均分
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- number = int(input('请输入学生人数: '))
- names = [None] * number
- scores = [None] * number
- for index in range(len(names)):
- names[index] = input('请输入第%d个学生的名字: ' % (index + 1))
- scores[index] = float(input('请输入第%d个学生的成绩: ' % (index + 1)))
- total = 0
- for index in range(len(names)):
- print('%s: %.1f分' % (names[index], scores[index]))
- total += scores[index]
- print('平均成绩是: %.1f分' % (total / number))
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/dict1.py b/Day07/dict1.py
deleted file mode 100644
index 6d7b62a3b..000000000
--- a/Day07/dict1.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""
-
-定义和使用字典
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-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:
- print(scores['武则天'])
- print(scores.get('武则天'))
- print(scores.get('武则天', 60))
- print(scores.popitem())
- print(scores.popitem())
- print(scores.pop('骆昊', 100))
- scores.clear()
- print(scores)
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/dict2.py b/Day07/dict2.py
deleted file mode 100644
index 9b3dea9fe..000000000
--- a/Day07/dict2.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""
-
-字典的常用操作
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- stu = {'name': '骆昊', 'age': 38, 'gender': True}
- print(stu)
- print(stu.keys())
- print(stu.values())
- print(stu.items())
- for elem in stu.items():
- print(elem)
- print(elem[0], elem[1])
- if 'age' in stu:
- stu['age'] = 20
- print(stu)
- stu.setdefault('score', 60)
- print(stu)
- stu.setdefault('score', 100)
- print(stu)
- stu['score'] = 100
- print(stu)
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/fibonacci.py b/Day07/fibonacci.py
deleted file mode 100644
index 9dba36a53..000000000
--- a/Day07/fibonacci.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""
-
-生成斐波拉切数列
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- f = [1 , 1]
- for i in range(2, 20):
- f += [f[i - 1] + f[i - 2]]
- # f.append(f[i - 1] + f[i - 2])
- for val in f:
- print(val, end=' ')
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/findmax.py b/Day07/findmax.py
deleted file mode 100644
index 30e59b0cd..000000000
--- a/Day07/findmax.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""
-
-找出列表中最大或最小的元素
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- fruits = ['grape', 'apple', 'strawberry', 'waxberry', 'pitaya']
- # 直接使用内置的max和min函数找出列表中最大和最小元素
- # print(max(fruits))
- # print(min(fruits))
- max_value = min_value = fruits[0]
- for index in range(1, len(fruits)):
- if fruits[index] > max_value:
- max_value = fruits[index]
- elif fruits[index] < min_value:
- min_value = fruits[index]
- print('Max:', max_value)
- print('Min:', min_value)
-
-
-if __name__ == '__main__':
- main()
-# 想一想如果最大的元素有两个要找出第二大的又该怎么做
diff --git a/Day07/list1.py b/Day07/list1.py
deleted file mode 100644
index be7cdbc37..000000000
--- a/Day07/list1.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""
-
-定义和使用列表
- - 用下标访问元素
- - 添加元素
- - 删除元素
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- fruits = ['grape', '@pple', 'strawberry', 'waxberry']
- print(fruits)
- # 通过下标访问元素
- print(fruits[0])
- print(fruits[1])
- print(fruits[-1])
- print(fruits[-2])
- # print(fruits[-5]) # IndexError
- # print(fruits[4]) # IndexError
- fruits[1] = 'apple'
- print(fruits)
- # 添加元素
- fruits.append('pitaya')
- fruits.insert(0, 'banana')
- print(fruits)
- # 删除元素
- del fruits[1]
- fruits.pop()
- fruits.pop(0)
- fruits.remove('apple')
- print(fruits)
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/list2.py b/Day07/list2.py
deleted file mode 100644
index 049afa488..000000000
--- a/Day07/list2.py
+++ /dev/null
@@ -1,39 +0,0 @@
-"""
-
-列表常用操作
- - 列表连接
- - 获取长度
- - 遍历列表
- - 列表切片
- - 列表排序
- - 列表反转
- - 查找元素
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-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()
diff --git a/Day07/list3.py b/Day07/list3.py
deleted file mode 100644
index 74713486f..000000000
--- a/Day07/list3.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""
-
-生成列表
- - 用range创建数字列表
- - 生成表达式
- - 生成器
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-# 生成Fibonacci序列的生成器
-def fib(n):
- a, b = 0, 1
- for _ in range(n):
- a, b = b, a + b
- yield a
-
-
-def main():
- # 用range创建数值列表
- list1 = list(range(1, 11))
- print(list1)
- # 生成表达式
- list2 = [x * x for x in range(1, 11)]
- print(list2)
- list3 = [m + n for m in 'ABCDEFG' for n in '12345']
- print(list3)
- print(len(list3))
- # 生成器(节省空间但生成下一个元素时需要花费时间)
- gen = (m + n for m in 'ABCDEFG' for n in '12345')
- print(gen)
- for elem in gen:
- print(elem, end=' ')
- print()
- gen = fib(20)
- print(gen)
- for elem in gen:
- print(elem, end=' ')
- print()
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/lottery.py b/Day07/lottery.py
deleted file mode 100644
index 192051c55..000000000
--- a/Day07/lottery.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-
-双色球随机选号程序
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-from random import randrange, randint, sample
-
-
-def display(balls):
- """
- 输出列表中的双色球号码
- """
- for index, ball in enumerate(balls):
- if index == len(balls) - 1:
- print('|', end=' ')
- print('%02d' % ball, end=' ')
- print()
-
-
-def random_select():
- """
- 随机选择一组号码
- """
- red_balls = [x for x in range(1, 34)]
- selected_balls = []
- for _ in range(6):
- index = randrange(len(red_balls))
- selected_balls.append(red_balls[index])
- del red_balls[index]
- # 上面的for循环也可以写成下面这行代码
- # sample函数是random模块下的函数
- # selected_balls = sample(red_balls, 6)
- selected_balls.sort()
- selected_balls.append(randint(1, 16))
- return selected_balls
-
-
-def main():
- n = int(input('机选几注: '))
- for _ in range(n):
- display(random_select())
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/marquee.py b/Day07/marquee.py
deleted file mode 100644
index dc9750dd2..000000000
--- a/Day07/marquee.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""
-
-输入学生考试成绩计算平均分
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-import os
-import time
-
-
-def main():
- str = 'Welcome to 1000 Phone Chengdu Campus '
- while True:
- print(str)
- time.sleep(0.2)
- str = str[1:] + str[0:1]
- # for Windows use os.system('cls') instead
- os.system('clear')
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/res/fibonacci-blocks.png b/Day07/res/fibonacci-blocks.png
deleted file mode 100644
index a638bf123..000000000
Binary files a/Day07/res/fibonacci-blocks.png and /dev/null differ
diff --git a/Day07/res/ipython-timeit.png b/Day07/res/ipython-timeit.png
deleted file mode 100644
index 5b5238225..000000000
Binary files a/Day07/res/ipython-timeit.png and /dev/null differ
diff --git a/Day07/res/python-set.png b/Day07/res/python-set.png
deleted file mode 100644
index 2110598f3..000000000
Binary files a/Day07/res/python-set.png and /dev/null differ
diff --git a/Day07/scoretable.py b/Day07/scoretable.py
deleted file mode 100644
index 63fded25b..000000000
--- a/Day07/scoretable.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""
-
-学生考试成绩表
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- names = ['关羽', '张飞', '赵云', '马超', '黄忠']
- subjs = ['语文', '数学', '英语']
- scores = [[0] * 3] * 5
- for row, name in enumerate(names):
- print('请输入%s的成绩' % name)
- for col, subj in enumerate(subjs):
- scores[row][col] = float(input(subj + ': '))
- print(scores)
-# for row, name in enumerate(names):
-# print('请输入%s的成绩' % name)
-# scores[row] = [None] * len(subjs)
-# for col, subj in enumerate(subjs):
-# score = float(input(subj + ': '))
-# scores[row][col] = score
-# print(scores)
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/set1.py b/Day07/set1.py
deleted file mode 100644
index 9b2f0f535..000000000
--- a/Day07/set1.py
+++ /dev/null
@@ -1,39 +0,0 @@
-"""
-
-定义和使用集合
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-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:
- 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)
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/set2.py b/Day07/set2.py
deleted file mode 100644
index 506588765..000000000
--- a/Day07/set2.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-
-集合的常用操作
- - 交集
- - 并集
- - 差集
- - 子集
- - 超集
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- set1 = set(range(1, 7))
- print(set1)
- set2 = set(range(2, 11, 2))
- print(set2)
- set3 = set(range(1, 5))
- 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))
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/tic-tac-toe.py b/Day07/tic-tac-toe.py
deleted file mode 100644
index 0db6620ed..000000000
--- a/Day07/tic-tac-toe.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""
-
-井字棋游戏
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-import os
-
-
-
-def print_board(board):
- print(board['TL'] + '|' + board['TM'] + '|' + board['TR'])
- print('-+-+-')
- print(board['ML'] + '|' + board['MM'] + '|' + board['MR'])
- print('-+-+-')
- print(board['BL'] + '|' + board['BM'] + '|' + board['BR'])
-
-
-def main():
- init_board = {
- 'TL': ' ', 'TM': ' ', 'TR': ' ',
- 'ML': ' ', 'MM': ' ', 'MR': ' ',
- 'BL': ' ', 'BM': ' ', 'BR': ' '
- }
- begin = True
- while begin:
- curr_board = init_board.copy()
- begin = False
- turn = 'x'
- counter = 0
- os.system('clear')
- print_board(curr_board)
- while counter < 9:
- move = input('轮到%s走棋, 请输入位置: ' % turn)
- if curr_board[move] == ' ':
- counter += 1
- curr_board[move] = turn
- if turn == 'x':
- turn = 'o'
- else:
- turn = 'x'
- os.system('clear')
- print_board(curr_board)
- choice = input('再玩一局?(yes|no)')
- begin = choice == 'yes'
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day07/tuple.py b/Day07/tuple.py
deleted file mode 100644
index dcfaf2ffb..000000000
--- a/Day07/tuple.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-
-元组的定义和使用
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- # 定义元组
- t = ('骆昊', 38, True, '四川成都')
- print(t)
- # 获取元组中的元素
- print(t[0])
- print(t[1])
- print(t[2])
- 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)
- print(fruits_tuple[1])
-
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/Day07/yanghui.py b/Day07/yanghui.py
deleted file mode 100644
index 4be882a5c..000000000
--- a/Day07/yanghui.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""
-
-输出10行的杨辉三角 - 二项式的n次方展开系数
-1
-1 1
-1 2 1
-1 3 3 1
-1 4 6 4 1
-... ... ...
-
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-06
-
-"""
-
-
-def main():
- num = int(input('Number of rows: '))
- yh = [[]] * num
- for row in range(len(yh)):
- yh[row] = [None] * (row + 1)
- for col in range(len(yh[row])):
- if col == 0 or col == row:
- yh[row][col] = 1
- else:
- yh[row][col] = yh[row - 1][col] + yh[row - 1][col - 1]
- print(yh[row][col], end='\t')
- print()
-
-
-if __name__ == '__main__':
- main()
diff --git "a/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md"
deleted file mode 100644
index d1e176a83..000000000
--- "a/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md"
+++ /dev/null
@@ -1,637 +0,0 @@
-## 字符串和常用数据结构
-
-### 使用字符串
-
-第二次世界大战促使了现代电子计算机的诞生,当初的想法很简单,就是用计算机来计算导弹的弹道,因此在计算机刚刚诞生的那个年代,计算机处理的信息主要是数值,而世界上的第一台电子计算机ENIAC每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然对数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机处理得更多的数据都是以文本信息的方式存在的,而Python表示文本信息的方式我们在很早以前就说过了,那就是字符串类型。所谓**字符串**,就是由零个或多个字符组成的有限序列,一般记为[$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](https://wikimedia.org/api/rest_v1/media/math/render/svg/e29bf631b090323edd6889f810e6cff29538b161)。
-
-我们可以通过下面的代码来了解字符串的使用。
-
-```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())
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典。
-
-### 使用列表
-
-下面的代码演示了如何定义列表、使用下标访问列表元素以及添加和删除元素的操作。
-
-```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.remove(3)
- if 1234 in list1:
- list1.remove(1234)
- del list1[0]
- print(list1)
- # 清空列表元素
- list1.clear()
- print(list1)
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。
-
-```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()
-
-```
-
-下面的代码实现了对列表的排序操作。
-
-```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()
-
-```
-
-我们还可以使用列表的生成式语法来创建列表,代码如下所示。
-
-```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:
- 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)的方法来进行定义:
-
-$${\displaystyle F_{0}=0}$$
-
-$${\displaystyle F_{1}=1}$$
-
-$${\displaystyle F_{n}=F_{n-1}+F_{n-2}}({n}\geq{2})$$
-
-
-
-```Python
-def fib(n):
- a, b = 0, 1
- for _ in range(n):
- a, b = b, a + b
- yield a
-
-
-def main():
- for val in fib(20):
- print(val)
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-### 使用元组
-
-Python 的元组与列表类似,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
-
-```Python
-def main():
- # 定义元组
- 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()
-```
-
-这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢?
-
-1. 元组中的元素是无法修改的,事实上我们在项目中尤其是[多线程](https://zh.wikipedia.org/zh-hans/%E5%A4%9A%E7%BA%BF%E7%A8%8B)环境(后面会讲到)中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。
-2. 元组在创建时间和占用的空间上面都优于列表。我们可以使用sys模块的getsizeof函数来检查存储同样的元素的元组和列表各自占用了多少内存空间,这个很容易做到。我们也可以在ipython中使用魔法指令%timeit来分析创建同样内容的元组和列表所花费的时间,下图是我的macOS系统上测试的结果。
-
-
-
-### 使用集合
-
-Python中的集合跟数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集等运算。
-
-
-
-```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:
- 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))
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-> **说明**:Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符(后面的章节中会讲到),上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如`&`运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。
-
-### 使用字典
-
-字典是另一种可变容器模型,类似于我们生活中使用的字典,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。
-
-```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:
- 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()
-
-```
-
-### 练习
-
-#### 练习1:在屏幕上显示跑马灯文字
-
-```Python
-import os
-import time
-
-
-def main():
- content = '北京欢迎你为你开天辟地…………'
- while True:
- # 清理屏幕上的输出
- os.system('cls') # os.system('clear')
- print(content)
- # 休眠200毫秒
- time.sleep(0.2)
- content = content[1:] + content[0]
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-#### 练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成。
-
-```Python
-import random
-
-
-def generate_code(code_len=4):
- """
- 生成指定长度的验证码
-
- :param code_len: 验证码的长度(默认4个字符)
-
- :return: 由大小写英文字母和数字构成的随机验证码
- """
- all_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
- last_pos = len(all_chars) - 1
- code = ''
- for _ in range(code_len):
- index = random.randint(0, last_pos)
- code += all_chars[index]
- return code
-```
-
-#### 练习3:设计一个函数返回给定文件名的后缀名。
-
-```Python
-def get_suffix(filename, has_dot=False):
- """
- 获取文件名的后缀名
-
- :param filename: 文件名
- :param has_dot: 返回的后缀名是否需要带点
-
- :return: 文件的后缀名
- """
- pos = filename.rfind('.')
- if 0 < pos < len(filename) - 1:
- index = pos if has_dot else pos + 1
- return filename[index:]
- else:
- return ''
-
-```
-
-#### 练习4:设计一个函数返回传入的列表中最大和第二大的元素的值。
-
-```Python
-def max2(x):
- m1, m2 = (x[0], x[1]) if x[0] > x[1] else (x[1], x[0])
- for index in range(2, len(x)):
- if x[index] > m1:
- m2 = m1
- m1 = x[index]
- elif x[index] > m2:
- m2 = x[index]
- return m1, m2
-```
-
-#### 练习5:计算指定的年月日是这一年的第几天
-
-```Python
-def is_leap_year(year):
- """
- 判断指定的年份是不是闰年
-
- :param year: 年份
-
- :return: 闰年返回True平年返回False
- """
- return year % 4 == 0 and year % 100 != 0 or year % 400 == 0
-
-
-def which_day(year, month, date):
- """
- 计算传入的日期是这一年的第几天
-
- :param year: 年
- :param month: 月
- :param date: 日
-
- :return: 第几天
- """
- days_of_month = [
- [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
- [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
- ][is_leap_year(year)]
- total = 0
- for index in range(month - 1):
- total += days_of_month[index]
- return total + date
-
-
-def main():
- print(which_day(1980, 11, 28))
- print(which_day(1981, 12, 31))
- print(which_day(2018, 1, 1))
- print(which_day(2016, 3, 1))
-
-
-if __name__ == '__main__':
- 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: '))
- yh = [[]] * num
- for row in range(len(yh)):
- yh[row] = [None] * (row + 1)
- for col in range(len(yh[row])):
- if col == 0 or col == row:
- yh[row][col] = 1
- else:
- yh[row][col] = yh[row - 1][col] + yh[row - 1][col - 1]
- print(yh[row][col], end='\t')
- print()
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-### 综合案例
-
-#### 案例1:双色球选号
-
-```Python
-from random import randrange, randint, sample
-
-
-def display(balls):
- """
- 输出列表中的双色球号码
- """
- for index, ball in enumerate(balls):
- if index == len(balls) - 1:
- print('|', end=' ')
- print('%02d' % ball, end=' ')
- print()
-
-
-def random_select():
- """
- 随机选择一组号码
- """
- red_balls = [x for x in range(1, 34)]
- selected_balls = []
- for _ in range(6):
- index = randrange(len(red_balls))
- selected_balls.append(red_balls[index])
- del red_balls[index]
- # 上面的for循环也可以写成下面这行代码
- # sample函数是random模块下的函数
- # selected_balls = sample(red_balls, 6)
- selected_balls.sort()
- selected_balls.append(randint(1, 16))
- return selected_balls
-
-
-def main():
- n = int(input('机选几注: '))
- for _ in range(n):
- display(random_select())
-
-
-if __name__ == '__main__':
- 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)
-
-```Python
-"""
-
-《幸运的基督徒》
-有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人扔到海里面去,有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15个人。由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置是基督徒哪些位置是非基督徒。
-
-"""
-
-
-def main():
- persons = [True] * 30
- counter, index, number = 0, 0, 0
- while counter < 15:
- if persons[index]:
- number += 1
- if number == 9:
- persons[index] = False
- counter += 1
- number = 0
- index += 1
- index %= 30
- for person in persons:
- print('基' if person else '非', end='')
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-#### 综合案例3:[井字棋](https://zh.wikipedia.org/wiki/%E4%BA%95%E5%AD%97%E6%A3%8B)游戏
-
-```Python
-import os
-
-
-def print_board(board):
- print(board['TL'] + '|' + board['TM'] + '|' + board['TR'])
- print('-+-+-')
- print(board['ML'] + '|' + board['MM'] + '|' + board['MR'])
- print('-+-+-')
- print(board['BL'] + '|' + board['BM'] + '|' + board['BR'])
-
-
-def main():
- init_board = {
- 'TL': ' ', 'TM': ' ', 'TR': ' ',
- 'ML': ' ', 'MM': ' ', 'MR': ' ',
- 'BL': ' ', 'BM': ' ', 'BR': ' '
- }
- begin = True
- while begin:
- curr_board = init_board.copy()
- begin = False
- turn = 'x'
- counter = 0
- os.system('clear')
- print_board(curr_board)
- while counter < 9:
- move = input('轮到%s走棋, 请输入位置: ' % turn)
- if curr_board[move] == ' ':
- counter += 1
- curr_board[move] = turn
- if turn == 'x':
- turn = 'o'
- else:
- turn = 'x'
- os.system('clear')
- print_board(curr_board)
- choice = input('再玩一局?(yes|no)')
- begin = choice == 'yes'
-
-
-if __name__ == '__main__':
- main()
-
-```
-
->**说明**:最后这个案例来自[《Python编程快速上手:让繁琐工作自动化》](https://item.jd.com/11943853.html)一书(这本书对有编程基础想迅速使用Python将日常工作自动化的人来说还是不错的教材),对代码做了一点点的调整。
\ No newline at end of file
diff --git a/Day08/access.py b/Day08/access.py
deleted file mode 100644
index 8ed1558a9..000000000
--- a/Day08/access.py
+++ /dev/null
@@ -1,18 +0,0 @@
-class Test:
-
- def __init__(self, foo):
- self.__foo = foo
-
- def __bar(self):
- print(self.__foo)
- print('__bar')
-
-
-def main():
- test = Test('hello')
- test._Test__bar()
- print(test._Test__foo)
-
-
-if __name__ == "__main__":
- main()
diff --git a/Day08/circle.py b/Day08/circle.py
deleted file mode 100644
index 7e0ddc02c..000000000
--- a/Day08/circle.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""
-
-练习
-修一个游泳池 半径(以米为单位)在程序运行时输入 游泳池外修一条3米宽的过道
-过道的外侧修一圈围墙 已知过道的造价为25元每平米 围墙的造价为32.5元每米
-输出围墙和过道的总造价分别是多少钱(精确到小数点后2位)
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-08
-
-"""
-
-import math
-
-
-class Circle(object):
-
- def __init__(self, radius):
- self._radius = radius
-
- @property
- def radius(self):
- return self._radius
-
- @radius.setter
- def radius(self, radius):
- self._radius = radius if radius > 0 else 0
-
- @property
- def perimeter(self):
- return 2 * math.pi * self._radius
-
- @property
- def area(self):
- return math.pi * self._radius * self._radius
-
-
-if __name__ == '__main__':
- radius = float(input('请输入游泳池的半径: '))
- small = Circle(radius)
- big = Circle(radius + 3)
- print('围墙的造价为: ¥%.1f元' % (big.perimeter * 115))
- print('过道的造价为: ¥%.1f元' % ((big.area - small.area) * 65))
diff --git a/Day08/clock.py b/Day08/clock.py
deleted file mode 100644
index ffdb7923b..000000000
--- a/Day08/clock.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""
-
-定义和使用时钟类
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-08
-
-"""
-
-import time
-import os
-
-
-class Clock(object):
-
- # Python中的函数是没有重载的概念的
- # 因为Python中函数的参数没有类型而且支持缺省参数和可变参数
- # 用关键字参数让构造器可以传入任意多个参数来实现其他语言中的构造器重载
- def __init__(self, **kw):
- if 'hour' in kw and 'minute' in kw and 'second' in kw:
- self._hour = kw['hour']
- self._minute = kw['minute']
- self._second = kw['second']
- else:
- tm = time.localtime(time.time())
- self._hour = tm.tm_hour
- self._minute = tm.tm_min
- self._second = tm.tm_sec
-
- def run(self):
- self._second += 1
- if self._second == 60:
- self._second = 0
- self._minute += 1
- if self._minute == 60:
- self._minute = 0
- self._hour += 1
- if self._hour == 24:
- self._hour = 0
-
- def show(self):
- return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)
-
-
-if __name__ == '__main__':
- # clock = Clock(hour=10, minute=5, second=58)
- clock = Clock()
- while True:
- os.system('clear')
- print(clock.show())
- time.sleep(1)
- clock.run()
diff --git a/Day08/guess.py b/Day08/guess.py
deleted file mode 100644
index 095c52824..000000000
--- a/Day08/guess.py
+++ /dev/null
@@ -1,58 +0,0 @@
-"""
-
-面向对象版本的猜数字游戏
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-08
-
-"""
-
-from random import randint
-
-
-class GuessMachine(object):
-
- def __init__(self):
- self._answer = None
- self._counter = None
- self._hint = None
-
- def reset(self):
- self._answer = randint(1, 100)
- self._counter = 0
- self._hint = None
-
- def guess(self, your_answer):
- self._counter += 1
- if your_answer > self._answer:
- self._hint = '小一点'
- elif your_answer < self._answer:
- self._hint = '大一点'
- else:
- self._hint = '恭喜你猜对了'
- return True
- return False
-
- @property
- def counter(self):
- return self._counter
-
- @property
- def hint(self):
- return self._hint
-
-
-if __name__ == '__main__':
- gm = GuessMachine()
- play_again = True
- while play_again:
- game_over = False
- gm.reset()
- while not game_over:
- your_answer = int(input('请输入: '))
- game_over = gm.guess(your_answer)
- print(gm.hint)
- if gm.counter > 7:
- print('智商余额不足!')
- play_again = input('再玩一次?(yes|no)') == 'yes'
diff --git a/Day08/hack.py b/Day08/hack.py
deleted file mode 100644
index 701f3a034..000000000
--- a/Day08/hack.py
+++ /dev/null
@@ -1,27 +0,0 @@
-"""
-
-另一种创建类的方式
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-08
-
-"""
-
-
-def bar(self, name):
- self._name = name
-
-
-def foo(self, course_name):
- print('%s正在学习%s.' % (self._name, course_name))
-
-
-def main():
- Student = type('Student', (object,), dict(__init__=bar, study=foo))
- stu1 = Student('骆昊')
- stu1.study('Python程序设计')
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day08/rect.py b/Day08/rect.py
deleted file mode 100644
index 3698b2589..000000000
--- a/Day08/rect.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""
-
-定义和使用矩形类
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-08
-
-"""
-
-
-class Rect(object):
- """矩形类"""
-
- def __init__(self, width=0, height=0):
- """构造器"""
- self.__width = width
- self.__height = height
-
- def perimeter(self):
- """计算周长"""
- return (self.__width + self.__height) * 2
-
- def area(self):
- """计算面积"""
- return self.__width * self.__height
-
- def __str__(self):
- """矩形对象的字符串表达式"""
- return '矩形[%f,%f]' % (self.__width, self.__height)
-
- def __del__(self):
- """析构器"""
- print('销毁矩形对象')
-
-
-if __name__ == '__main__':
- rect1 = Rect()
- print(rect1)
- print(rect1.perimeter())
- print(rect1.area())
- rect2 = Rect(3.5, 4.5)
- print(rect2)
- print(rect2.perimeter())
- print(rect2.area())
diff --git a/Day08/res/object-feature.png b/Day08/res/object-feature.png
deleted file mode 100644
index 62c605762..000000000
Binary files a/Day08/res/object-feature.png and /dev/null differ
diff --git a/Day08/res/oop-zhihu.png b/Day08/res/oop-zhihu.png
deleted file mode 100644
index c473984a0..000000000
Binary files a/Day08/res/oop-zhihu.png and /dev/null differ
diff --git a/Day08/student.py b/Day08/student.py
deleted file mode 100644
index f5ac8552f..000000000
--- a/Day08/student.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
-
-定义和使用学生类
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-08
-
-"""
-
-
-def _foo():
- print('test')
-
-
-class Student(object):
-
- # __init__是一个特殊方法用于在创建对象时进行初始化操作
- # 通过这个方法我们可以为学生对象绑定name和age两个属性
- def __init__(self, name, age):
- self.name = name
- self.age = age
-
- def study(self, course_name):
- print('%s正在学习%s.' % (self.name, course_name))
-
- # PEP 8要求标识符的名字用全小写多个单词用下划线连接
- # 但是很多程序员和公司更倾向于使用驼峰命名法(驼峰标识)
- def watch_av(self):
- if self.age < 18:
- print('%s只能观看《熊出没》.' % self.name)
- else:
- print('%s正在观看岛国爱情动作片.' % self.name)
-
-
-def main():
- stu1 = Student('骆昊', 38)
- stu1.study('Python程序设计')
- stu1.watch_av()
- stu2 = Student('王大锤', 15)
- stu2.study('思想品德')
- stu2.watch_av()
-
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/Day08/test.py b/Day08/test.py
deleted file mode 100644
index e69de29bb..000000000
diff --git "a/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md" "b/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md"
deleted file mode 100644
index 880db4514..000000000
--- "a/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md"
+++ /dev/null
@@ -1,252 +0,0 @@
-## 面向对象编程基础
-
-活在当下的程序员应该都听过“面向对象编程”一词,也经常有人问能不能用一句话解释下什么是“面向对象编程”,我们先来看看比较正式的说法。
-
-> 把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。
-
-这样一说是不是更不明白了。所以我们还是看看更通俗易懂的说法,下面这段内容来自于[知乎](https://www.zhihu.com/)。
-
-
-
-> **说明**:以上的内容来自于网络,不代表作者本人的观点和看法,与作者本人立场无关,相关责任不由作者承担。(终于有机会享受一下把这段话反过来说的乐趣了,乐得牙都快碎了。)
-
-之前我们说过“程序是指令的集合”,我们在程序中书写的语句在执行时会变成一条或多条指令然后由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)语言)。按照这种编程理念,程序中的数据和操作数据的函数是一个逻辑上的整体,我们称之为“对象”,而我们解决问题的方式就是创建出需要的对象并向对象发出各种各样的消息,多个对象的协同工作最终可以让我们构造出复杂的系统来解决现实中的问题。
-
-> **说明**:当然面向对象也不是解决软件开发中所有问题的最后的“银弹”,所以今天的高级程序设计语言几乎都提供了对多种编程范式的支持,Python也不例外。
-
-### 类和对象
-
-简单的说,类是对象的蓝图和模板,而对象是类的实例。这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。
-
-
-
-### 定义类
-
-在Python中可以使用`class`关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。
-
-```Python
-class Student(object):
-
- # __init__是一个特殊方法用于在创建对象时进行初始化操作
- # 通过这个方法我们可以为学生对象绑定name和age两个属性
- def __init__(self, name, age):
- self.name = name
- self.age = age
-
- def study(self, course_name):
- print('%s正在学习%s.' % (self.name, course_name))
-
- # PEP 8要求标识符的名字用全小写多个单词用下划线连接
- # 但是很多程序员和公司更倾向于使用驼峰命名法(驼峰标识)
- def watch_av(self):
- if self.age < 18:
- print('%s只能观看《熊出没》.' % self.name)
- else:
- print('%s正在观看岛国爱情动作片.' % self.name)
-```
-
-> **说明**:写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。
-
-### 创建和使用对象
-
-当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。
-
-```Python
-def main():
- # 创建学生对象并指定姓名和年龄
- stu1 = Student('骆昊', 38)
- # 给对象发study消息
- stu1.study('Python程序设计')
- # 给对象发watch_av消息
- stu1.watch_av()
- stu2 = Student('王大锤', 15)
- stu2.study('思想品德')
- stu2.watch_av()
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-### 访问可见性问题
-
-对于上面的代码,有C++、Java、C#等编程经验的程序员可能会问,我们给`Student`对象绑定的`name`和`age`属性到底具有怎样的访问权限(也称为可见性)。因为在很多面向对象编程语言中,我们通常会将对象的属性设置为私有的(private)或受保护的(protected),简单的说就是不允许外界访问,而对象的方法通常都是公开的(public),因为公开的方法就是对象能够接受的消息。在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,下面的代码可以验证这一点。
-
-```Python
-class Test:
-
- def __init__(self, foo):
- self.__foo = foo
-
- def __bar(self):
- print(self.__foo)
- print('__bar')
-
-
-def main():
- test = Test('hello')
- # AttributeError: 'Test' object has no attribute '__bar'
- test.__bar()
- # AttributeError: 'Test' object has no attribute '__foo'
- print(test.__foo)
-
-
-if __name__ == "__main__":
- main()
-
-```
-
-但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。
-
-```Python
-class Test:
-
- def __init__(self, foo):
- self.__foo = foo
-
- def __bar(self):
- print(self.__foo)
- print('__bar')
-
-
-def main():
- test = Test('hello')
- test._Test__bar()
- print(test._Test__foo)
-
-
-if __name__ == "__main__":
- main()
-
-```
-
-在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻,关于这一点可以看看我的[《Python - 那些年我们踩过的那些坑》](http://blog.csdn.net/jackfrued/article/details/79521404)文章中的讲解。
-
-### 面向对象的支柱
-
-面向对象有三大支柱:封装、继承和多态。后面两个概念在下一个章节中进行详细的说明,这里我们先说一下什么是封装。我自己对封装的理解是“隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口”。我们在类中定义的方法其实就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数(方法的外部视图),而不需要知道方法内部的实现细节(方法的内部视图)。
-
-### 练习
-
-#### 练习1:定义一个类描述数字时钟
-
-```Python
-class Clock(object):
- """
- 数字时钟
- """
-
- def __init__(self, hour=0, minute=0, second=0):
- """
- 构造器
-
- :param hour: 时
- :param minute: 分
- :param second: 秒
- """
- self._hour = hour
- self._minute = minute
- self._second = second
-
- def run(self):
- """走字"""
- self._second += 1
- if self._second == 60:
- self._second = 0
- self._minute += 1
- if self._minute == 60:
- self._minute = 0
- self._hour += 1
- if self._hour == 24:
- self._hour = 0
-
- def __str__(self):
- """显示时间"""
- return '%02d:%02d:%02d' % \
- (self._hour, self._minute, self._second)
-
-
-def main():
- clock = Clock(23, 59, 58)
- while True:
- print(clock)
- sleep(1)
- clock.run()
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-#### 练习2:定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法。
-
-```Python
-from math import sqrt
-
-
-class Point(object):
-
- def __init__(self, x=0, y=0):
- """
- 构造器
-
- :param x: 横坐标
- :param y: 纵坐标
- """
- self.x = x
- self.y = y
-
- def move_to(self, x, y):
- """
- 移动到指定位置
-
- :param x: 新的横坐标
- "param y: 新的纵坐标
- """
- self.x = x
- self.y = y
-
- def move_by(self, dx, dy):
- """
- 移动指定的增量
-
- :param dx: 横坐标的增量
- "param dy: 纵坐标的增量
- """
- self.x += dx
- self.y += dy
-
- def distance_to(self, other):
- """
- 计算与另一个点的距离
-
- :param other: 另一个点
- """
- dx = self.x - other.x
- dy = self.y - other.y
- return sqrt(dx ** 2 + dy ** 2)
-
- def __str__(self):
- return '(%s, %s)' % (str(self.x), str(self.y))
-
-
-def main():
- p1 = Point(3, 5)
- p2 = Point()
- print(p1)
- print(p2)
- p2.move_by(-1, 2)
- print(p2)
- print(p1.distance_to(p2))
-
-
-if __name__ == '__main__':
- main()
-
-```
-
-> **说明**:本章中的插图来自于Grady Booch等著作的[《面向对象分析与设计》](https://item.jd.com/20476561918.html)一书,该书是讲解面向对象编程的经典著作,有兴趣的读者可以购买和阅读这本书来了解更多的面向对象的相关知识。
\ No newline at end of file
diff --git a/Day09/association.py b/Day09/association.py
deleted file mode 100644
index d614abe9d..000000000
--- a/Day09/association.py
+++ /dev/null
@@ -1,73 +0,0 @@
-"""
-
-对象之间的关联关系
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-12
-
-"""
-
-from math import sqrt
-
-
-class Point(object):
-
- def __init__(self, x=0, y=0):
- self._x = x
- self._y = y
-
- def move_to(self, x, y):
- self._x = x
- self._y = y
-
- def move_by(self, dx, dy):
- self._x += dx
- self._y += dy
-
- def distance_to(self, other):
- dx = self._x - other._x
- dy = self._y - other._y
- return sqrt(dx ** 2 + dy ** 2)
-
- def __str__(self):
- return '(%s, %s)' % (str(self._x), str(self._y))
-
-
-class Line(object):
-
- def __init__(self, start=Point(0, 0), end=Point(0, 0)):
- self._start = start
- self._end = end
-
- @property
- def start(self):
- return self._start
-
- @start.setter
- def start(self, start):
- self._start = start
-
- @property
- def end(self):
- return self.end
-
- @end.setter
- def end(self, end):
- self._end = end
-
- @property
- def length(self):
- return self._start.distance_to(self._end)
-
-
-if __name__ == '__main__':
- p1 = Point(3, 5)
- print(p1)
- p2 = Point(-2, -1.5)
- print(p2)
- line = Line(p1, p2)
- print(line.length)
- line.start.move_to(2, 1)
- line.end = Point(1, 2)
- print(line.length)
diff --git a/Day09/car1.py b/Day09/car1.py
deleted file mode 100644
index 813b4a89b..000000000
--- a/Day09/car1.py
+++ /dev/null
@@ -1,65 +0,0 @@
-"""
-
-属性的使用
- - 访问器/修改器/删除器
- - 使用__slots__对属性加以限制
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-12
-
-"""
-
-
-class Car(object):
-
- __slots__ = ('_brand', '_max_speed')
-
- def __init__(self, brand, max_speed):
- self._brand = brand
- self._max_speed = max_speed
-
- @property
- def brand(self):
- return self._brand
-
- @brand.setter
- def brand(self, brand):
- self._brand = brand
-
- @brand.deleter
- def brand(self):
- del self._brand
-
- @property
- def max_speed(self):
- return self._max_speed
-
- @max_speed.setter
- def max_speed(self, max_speed):
- if max_speed < 0:
- raise ValueError('Invalid max speed for car')
- self._max_speed = max_speed
-
- def __str__(self):
- return 'Car: [品牌=%s, 最高时速=%d]' % (self._brand, self._max_speed)
-
-
-car = Car('QQ', 120)
-print(car)
-# ValueError
-# car.max_speed = -100
-car.max_speed = 320
-car.brand = "Benz"
-# 使用__slots__属性限制后下面的代码将产生异常
-# car.current_speed = 80
-print(car)
-# 如果提供了删除器可以执行下面的代码
-# del car.brand
-# 属性的实现
-print(Car.brand)
-print(Car.brand.fget)
-print(Car.brand.fset)
-print(Car.brand.fdel)
-# 通过上面的代码帮助学生理解之前提到的包装器的概念
-# Python中有很多类似的语法糖后面还会出现这样的东西
diff --git a/Day09/car2.py b/Day09/car2.py
deleted file mode 100644
index c8d2b4b37..000000000
--- a/Day09/car2.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-
-属性的使用
- - 使用已有方法定义访问器/修改器/删除器
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-12
-
-"""
-
-
-class Car(object):
-
- def __init__(self, brand, max_speed):
- self.set_brand(brand)
- self.set_max_speed(max_speed)
-
- def get_brand(self):
- return self._brand
-
- def set_brand(self, brand):
- self._brand = brand
-
- def get_max_speed(self):
- return self._max_speed
-
- def set_max_speed(self, max_speed):
- if max_speed < 0:
- raise ValueError('Invalid max speed for car')
- self._max_speed = max_speed
-
- def __str__(self):
- return 'Car: [品牌=%s, 最高时速=%d]' % (self._brand, self._max_speed)
-
- # 用已有的修改器和访问器定义属性
- brand = property(get_brand, set_brand)
- max_speed = property(get_max_speed, set_max_speed)
-
-
-car = Car('QQ', 120)
-print(car)
-# ValueError
-# car.max_speed = -100
-car.max_speed = 320
-car.brand = "Benz"
-print(car)
-print(Car.brand)
-print(Car.brand.fget)
-print(Car.brand.fset)
diff --git a/Day09/clock.py b/Day09/clock.py
deleted file mode 100644
index 136c93fe9..000000000
--- a/Day09/clock.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from time import time, localtime, sleep
-
-
-class Clock(object):
- """数字时钟"""
-
- def __init__(self, hour=0, minute=0, second=0):
- self._hour = hour
- self._minute = minute
- self._second = second
-
- @classmethod
- def now(cls):
- ctime = localtime(time())
- return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)
-
- def run(self):
- """走字"""
- self._second += 1
- if self._second == 60:
- self._second = 0
- self._minute += 1
- if self._minute == 60:
- self._minute = 0
- self._hour += 1
- if self._hour == 24:
- self._hour = 0
-
- def show(self):
- """显示时间"""
- return '%02d:%02d:%02d' % \
- (self._hour, self._minute, self._second)
-
-
-def main():
- clock = Clock.now()
- while True:
- print(clock.show())
- sleep(1)
- clock.run()
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day09/dependency.py b/Day09/dependency.py
deleted file mode 100644
index b315fb296..000000000
--- a/Day09/dependency.py
+++ /dev/null
@@ -1,83 +0,0 @@
-"""
-
-对象之间的依赖关系和运算符重载
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-12
-
-"""
-
-
-class Car(object):
-
- def __init__(self, brand, max_speed):
- self._brand = brand
- self._max_speed = max_speed
- self._current_speed = 0
-
- @property
- def brand(self):
- return self._brand
-
- def accelerate(self, delta):
- self._current_speed += delta
- if self._current_speed > self._max_speed:
- self._current_speed = self._max_speed
-
- def brake(self):
- self._current_speed = 0
-
- def __str__(self):
- return '%s当前时速%d' % (self._brand, self._current_speed)
-
-
-class Student(object):
-
- def __init__(self, name, age):
- self._name = name
- self._age = age
-
- @property
- def name(self):
- return self._name
-
- # 学生和车之间存在依赖关系 - 学生使用了汽车
- def drive(self, car):
- print('%s驾驶着%s欢快的行驶在去西天的路上' % (self._name, car._brand))
- car.accelerate(30)
- print(car)
- car.accelerate(50)
- print(car)
- car.accelerate(50)
- print(car)
-
- def study(self, course_name):
- print('%s正在学习%s.' % (self._name, course_name))
-
- def watch_av(self):
- if self._age < 18:
- print('%s只能观看《熊出没》.' % self._name)
- else:
- print('%s正在观看岛国爱情动作片.' % self._name)
-
- # 重载大于(>)运算符
- def __gt__(self, other):
- return self._age > other._age
-
- # 重载小于(<)运算符
- def __lt__(self, other):
- return self._age < other._age
-
-
-if __name__ == '__main__':
- stu1 = Student('骆昊', 38)
- stu1.study('Python程序设计')
- stu1.watch_av()
- stu2 = Student('王大锤', 15)
- stu2.study('思想品德')
- stu2.watch_av()
- car = Car('QQ', 120)
- stu2.drive(car)
- print(stu1 > stu2)
- print(stu1 < stu2)
diff --git a/Day09/diamond.py b/Day09/diamond.py
deleted file mode 100644
index 97479203f..000000000
--- a/Day09/diamond.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""
-
-多重继承
- - 菱形继承(钻石继承)
- - C3算法(替代DFS的算法)
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-12
-
-"""
-
-
-class A(object):
-
- def foo(self):
- print('foo of A')
-
-
-class B(A):
- pass
-
-
-class C(A):
-
- def foo(self):
- print('foo fo C')
-
-
-class D(B, C):
- pass
-
-
-class E(D):
-
- def foo(self):
- print('foo in E')
- super().foo()
- super(B, self).foo()
- super(C, self).foo()
-
-
-if __name__ == '__main__':
- d = D()
- d.foo()
- e = E()
- e.foo()
diff --git a/Day09/employee.py b/Day09/employee.py
deleted file mode 100644
index 8e8628b85..000000000
--- a/Day09/employee.py
+++ /dev/null
@@ -1,77 +0,0 @@
-"""
-
-抽象类 / 方法重写 / 多态
-实现一个工资结算系统 公司有三种类型的员工
- - 部门经理固定月薪12000元/月
- - 程序员按本月工作小时数每小时100元
- - 销售员1500元/月的底薪加上本月销售额5%的提成
-输入员工的信息 输出每位员工的月薪信息
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-12
-
-"""
-
-from abc import ABCMeta, abstractmethod
-
-
-class Employee(object, metaclass=ABCMeta):
-
- def __init__(self, name):
- self._name = name
-
- @property
- def name(self):
- return self._name
-
- @abstractmethod
- def get_salary(self):
- pass
-
-
-class Manager(Employee):
-
- # 想一想: 如果不定义构造方法会怎么样
- def __init__(self, name):
- # 想一想: 如果不调用父类构造器会怎么样
- super().__init__(name)
-
- def get_salary(self):
- return 12000
-
-
-class Programmer(Employee):
-
- def __init__(self, name):
- super().__init__(name)
-
- def set_working_hour(self, working_hour):
- self._working_hour = working_hour
-
- def get_salary(self):
- return 100 * self._working_hour
-
-
-class Salesman(Employee):
-
- def __init__(self, name):
- super().__init__(name)
-
- def set_sales(self, sales):
- self._sales = sales
-
- def get_salary(self):
- return 1500 + self._sales * 0.05
-
-
-if __name__ == '__main__':
- emps = [Manager('武则天'), Programmer('狄仁杰'), Salesman('白元芳')]
- for emp in emps:
- if isinstance(emp, Programmer):
- working_hour = int(input('请输入%s本月工作时间: ' % emp.name))
- emp.set_working_hour(working_hour)
- elif isinstance(emp, Salesman):
- sales = float(input('请输入%s本月销售额: ' % emp.name))
- emp.set_sales(sales)
- print('%s本月月薪为: ¥%.2f元' % (emp.name, emp.get_salary()))
diff --git a/Day09/multi.py b/Day09/multi.py
deleted file mode 100644
index d24eb9ab8..000000000
--- a/Day09/multi.py
+++ /dev/null
@@ -1,68 +0,0 @@
-"""
-
-多重继承
- - 通过多重继承可以给一个类的对象具备多方面的能力
- - 这样在设计类的时候可以避免设计太多层次的复杂的继承关系
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-12
-
-"""
-
-
-class Father(object):
-
- def __init__(self, name):
- self._name = name
-
- def gamble(self):
- print('%s在打麻将.' % self._name)
-
- def eat(self):
- print('%s在大吃大喝.' % self._name)
-
-
-class Monk(object):
-
- def __init__(self, name):
- self._name = name
-
- def eat(self):
- print('%s在吃斋.' % self._name)
-
- def chant(self):
- print('%s在念经.' % self._name)
-
-
-class Musician(object):
-
- def __init__(self, name):
- self._name = name
-
- def eat(self):
- print('%s在细嚼慢咽.' % self._name)
-
- def play_piano(self):
- print('%s在弹钢琴.' % self._name)
-
-
-# 试一试下面的代码看看有什么区别
-# class Son(Monk, Father, Musician):
-# class Son(Musician, Father, Monk):
-
-
-class Son(Father, Monk, Musician):
-
- def __init__(self, name):
- Father.__init__(self, name)
- Monk.__init__(self, name)
- Musician.__init__(self, name)
-
-
-son = Son('王大锤')
-son.gamble()
-# 调用继承自Father的eat方法
-son.eat()
-son.chant()
-son.play_piano()
diff --git a/Day09/pet.py b/Day09/pet.py
deleted file mode 100644
index 06035849e..000000000
--- a/Day09/pet.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from abc import ABCMeta, abstractmethod
-
-
-class Pet(object, metaclass=ABCMeta):
-
- def __init__(self, nickname):
- self._nickname = nickname
-
- @abstractmethod
- def make_voice(self):
- pass
-
-
-class Dog(Pet):
-
- def make_voice(self):
- print('%s: 汪汪汪...' % self._nickname)
-
-
-class Cat(Pet):
-
- def make_voice(self):
- print('%s: 喵...喵...' % self._nickname)
-
-
-def main():
- pets = [Dog('旺财'), Cat('凯蒂'), Dog('大黄')]
- for pet in pets:
- pet.make_voice()
-
-
-if __name__ == '__main__':
- main()
diff --git a/Day09/rational.py b/Day09/rational.py
deleted file mode 100644
index 4ff859638..000000000
--- a/Day09/rational.py
+++ /dev/null
@@ -1,76 +0,0 @@
-"""
-
-运算符重载 - 自定义分数类
-
-Version: 0.1
-Author: 骆昊
-Date: 2018-03-12
-
-"""
-
-from math import gcd
-
-
-class Rational(object):
-
- def __init__(self, num, den=1):
- if den == 0:
- raise ValueError('分母不能为0')
- self._num = num
- self._den = den
- self.normalize()
-
- def simplify(self):
- x = abs(self._num)
- y = abs(self._den)
- factor = gcd(x, y)
- if factor > 1:
- self._num //= factor
- self._den //= factor
- return self
-
- def normalize(self):
- if self._den < 0:
- self._den = -self._den
- self._num = -self._num
- return self
-
- def __add__(self, other):
- new_num = self._num * other._den + other._num * self._den
- new_den = self._den * other._den
- return Rational(new_num, new_den).simplify().normalize()
-
- def __sub__(self, other):
- new_num = self._num * other._den - other._num * self._den
- new_den = self._den * other._den
- return Rational(new_num, new_den).simplify().normalize()
-
- def __mul__(self, other):
- new_num = self._num * other._num
- new_den = self._den * other._den
- return Rational(new_num, new_den).simplify().normalize()
-
- def __truediv__(self, other):
- new_num = self._num * other._den
- new_den = self._den * other._num
- return Rational(new_num, new_den).simplify().normalize()
-
- def __str__(self):
- if self._num == 0:
- return '0'
- elif self._den == 1:
- return str(self._num)
- else:
- return '(%d/%d)' % (self._num, self._den)
-
-
-if __name__ == '__main__':
- r1 = Rational(2, 3)
- print(r1)
- r2 = Rational(6, -8)
- print(r2)
- print(r2.simplify())
- print('%s + %s = %s' % (r1, r2, r1 + r2))
- print('%s - %s = %s' % (r1, r2, r1 - r2))
- print('%s * %s = %s' % (r1, r2, r1 * r2))
- print('%s / %s = %s' % (r1, r2, r1 / r2))
diff --git a/Day09/res/uml-components.png b/Day09/res/uml-components.png
deleted file mode 100644
index 0394cd7f2..000000000
Binary files a/Day09/res/uml-components.png and /dev/null differ
diff --git a/Day09/res/uml-example.gliffy b/Day09/res/uml-example.gliffy
deleted file mode 100644
index 015163a2f..000000000
--- a/Day09/res/uml-example.gliffy
+++ /dev/null
@@ -1 +0,0 @@
-{"contentType":"application/gliffy+json","version":"1.1","metadata":{"title":"untitled","revision":0,"exportBorder":false},"embeddedResources":{"index":0,"resources":[]},"stage":{"objects":[{"x":211,"y":179.5,"rotation":0,"id":79,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":60,"height":28,"lockAspectRatio":false,"lockShape":false,"order":71,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"继承关系
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":702,"y":249,"rotation":0,"id":78,"uid":"com.gliffy.shape.uml.uml_v1.default.generalization","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":70,"graphic":{"type":"Line","Line":{"strokeWidth":1,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":4,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":null,"controlPath":[[82,-4],[50,-4],[50,-69.32485578727801],[18,-69.32485578727801]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":70,"px":0,"py":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":32,"px":0.9999999999999998,"py":0.7071067811865475}}},"linkMap":[]},{"x":615,"y":70,"rotation":0,"id":77,"uid":"com.gliffy.shape.uml.uml_v1.default.generalization","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":69,"graphic":{"type":"Line","Line":{"strokeWidth":1,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":4,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":null,"controlPath":[[169,6.75],[137,6.75],[137,70.32485578727798],[105,70.32485578727798]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":63,"px":0,"py":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":32,"px":1,"py":0.29289321881345237}}},"linkMap":[]},{"x":228,"y":356,"rotation":0,"id":55,"uid":"com.gliffy.shape.uml.uml_v1.default.association","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":0,"graphic":{"type":"Line","Line":{"strokeWidth":1,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":null,"controlPath":[[202,-46],[202,-22.666666666666686],[202,0.6666666666666856],[202,24]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":24,"px":0.5,"py":1}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":48,"px":0.5,"py":0}}},"linkMap":[]},{"x":667,"y":225,"rotation":0,"id":47,"uid":"com.gliffy.shape.uml.uml_v1.default.aggregation","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":40,"graphic":{"type":"Line","Line":{"strokeWidth":1,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":5,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-17,-17.5],[-17,8.333333333333343],[-17,34.166666666666686],[-17,60]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":32,"px":0.5,"py":1}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":56,"px":0.5,"py":0}}},"linkMap":[]},{"x":403,"y":390,"rotation":0,"id":39,"uid":"com.gliffy.shape.uml.uml_v1.default.dependency","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":39,"graphic":{"type":"Line","Line":{"strokeWidth":1,"strokeColor":"#000000","fillColor":"none","dashStyle":"8.0,2.0","startArrow":0,"endArrow":6,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":null,"controlPath":[[97,-125],[137,-125],[137,-230],[177,-230]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":24,"px":1,"py":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":32,"px":0,"py":0.5}}},"linkMap":[]},{"x":289,"y":219,"rotation":0,"id":31,"uid":"com.gliffy.shape.uml.uml_v1.default.generalization","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":31,"graphic":{"type":"Line","Line":{"strokeWidth":1,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":4,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":null,"controlPath":[[141,1],[141,-124],[51,-124]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":24,"px":0.5,"py":0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":0,"px":1,"py":0.5}}},"linkMap":[]},{"x":325,"y":185,"rotation":0,"id":22,"uid":"com.gliffy.shape.uml.uml_v1.default.generalization","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":23,"graphic":{"type":"Line","Line":{"strokeWidth":1,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":4,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-55,35],[-55,11.666666666666657],[-55,-11.666666666666657],[-55,-35]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":14,"px":0.5,"py":0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":0,"px":0.5,"py":1}}},"linkMap":[]},{"x":98,"y":173,"rotation":0,"id":21,"uid":"com.gliffy.shape.uml.uml_v1.default.generalization","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":22,"graphic":{"type":"Line","Line":{"strokeWidth":1,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":4,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":null,"controlPath":[[12,47],[12,-78],[102,-78]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":7,"px":0.5,"py":0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":0,"px":0,"py":0.5}}},"linkMap":[]},{"x":200,"y":220,"rotation":0,"id":14,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":75,"lockAspectRatio":false,"lockShape":false,"order":15,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":15,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":16,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"Teacher
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":16,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":17,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":18,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"title
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":15,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":18,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":36,"rotation":0,"id":19,"uid":null,"width":140,"height":39,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":20,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"teach
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":14,"magnitude":1},{"id":15,"magnitude":-1},{"id":17,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":17,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":15,"magnitude":1},{"id":17,"magnitude":1},{"id":20,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":40,"y":220,"rotation":0,"id":7,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":75,"lockAspectRatio":false,"lockShape":false,"order":8,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":8,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":9,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"Student
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":9,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":10,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":11,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"grade
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":8,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":11,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":36,"rotation":0,"id":12,"uid":null,"width":140,"height":39,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":13,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"study
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":7,"magnitude":1},{"id":8,"magnitude":-1},{"id":10,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":10,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":8,"magnitude":1},{"id":10,"magnitude":1},{"id":13,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":200,"y":40,"rotation":0,"id":0,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":110,"lockAspectRatio":false,"lockShape":false,"order":1,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":1,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":2,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"Person
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":2,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":3,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":4,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"name\n
age
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":1,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":4,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":50,"rotation":0,"id":5,"uid":null,"width":140,"height":60,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":6,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"eat\n
play
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":0,"magnitude":1},{"id":1,"magnitude":-1},{"id":3,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":3,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":1,"magnitude":1},{"id":3,"magnitude":1},{"id":6,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":360,"y":220,"rotation":0,"id":24,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":90,"lockAspectRatio":false,"lockShape":false,"order":24,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":25,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":26,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"Driver
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":26,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":27,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":28,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"work_experience\n
license
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":25,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":28,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":50,"rotation":0,"id":29,"uid":null,"width":140,"height":40,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":30,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"drive
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":24,"magnitude":1},{"id":25,"magnitude":-1},{"id":27,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":27,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":25,"magnitude":1},{"id":27,"magnitude":1},{"id":30,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":580,"y":112.5,"rotation":0,"id":32,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":95,"lockAspectRatio":false,"lockShape":false,"order":32,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":33,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":34,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"Vehicle
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":34,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":35,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":36,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"brand\n
engine
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":33,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":36,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":50,"rotation":0,"id":37,"uid":null,"width":140,"height":45,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":38,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"accelerate\n
slow_down
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":32,"magnitude":1},{"id":33,"magnitude":-1},{"id":35,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":35,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":33,"magnitude":1},{"id":35,"magnitude":1},{"id":38,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":360,"y":380,"rotation":0,"id":48,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":75,"lockAspectRatio":false,"lockShape":false,"order":41,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":49,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":50,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"License
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":50,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":51,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":52,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"Attribute
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":49,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":52,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":36,"rotation":0,"id":53,"uid":null,"width":140,"height":39,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":54,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"Method
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":48,"magnitude":1},{"id":49,"magnitude":-1},{"id":51,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":51,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":49,"magnitude":1},{"id":51,"magnitude":1},{"id":54,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":580,"y":285,"rotation":0,"id":56,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":75,"lockAspectRatio":false,"lockShape":false,"order":48,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":57,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":58,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"Engine
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":58,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":59,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":60,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"number
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":57,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":60,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":36,"rotation":0,"id":61,"uid":null,"width":140,"height":39,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":62,"uid":null,"width":140,"height":4,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":56,"magnitude":1},{"id":57,"magnitude":-1},{"id":59,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":59,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":57,"magnitude":1},{"id":59,"magnitude":1},{"id":62,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":784,"y":41,"rotation":0,"id":63,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":71.5,"lockAspectRatio":false,"lockShape":false,"order":55,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":64,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":65,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"
Car
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":65,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":66,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":67,"uid":null,"width":140,"height":32,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"seats\n
displacement
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":64,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":67,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":50,"rotation":0,"id":68,"uid":null,"width":140,"height":21.5,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":69,"uid":null,"width":140,"height":4,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":63,"magnitude":1},{"id":64,"magnitude":-1},{"id":66,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":66,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":64,"magnitude":1},{"id":66,"magnitude":1},{"id":69,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":784,"y":207.5,"rotation":0,"id":70,"uid":"com.gliffy.shape.uml.uml_v1.default.class","width":140,"height":75,"lockAspectRatio":false,"lockShape":false,"order":62,"graphic":null,"children":[{"x":0,"y":0,"rotation":0,"id":71,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":72,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"
Truck
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":72,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":18,"rotation":0,"id":73,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":74,"uid":null,"width":140,"height":18,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"capacity
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"PositionConstraint","PositionConstraint":{"nodeId":71,"px":0,"py":1}},{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":74,"magnitude":1}],"growParent":true,"padding":0}}]}},{"x":0,"y":36,"rotation":0,"id":75,"uid":null,"width":140,"height":39,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"shadowX":4,"shadowY":4,"opacity":1}},"children":[{"x":0,"y":0,"rotation":0,"id":76,"uid":null,"width":140,"height":4,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":false,"heightInfo":[{"id":70,"magnitude":1},{"id":71,"magnitude":-1},{"id":73,"magnitude":-1}],"growParent":false,"padding":0}},{"type":"PositionConstraint","PositionConstraint":{"nodeId":73,"px":0,"py":1}}]}}],"constraints":{"constraints":[{"type":"HeightConstraint","HeightConstraint":{"isMin":true,"heightInfo":[{"id":71,"magnitude":1},{"id":73,"magnitude":1},{"id":76,"magnitude":1}],"growParent":false,"padding":0}}]},"linkMap":[]},{"x":371,"y":346,"rotation":0,"id":81,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":60,"height":14,"lockAspectRatio":false,"lockShape":false,"order":72,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"
关联关系
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":490,"y":186.5,"rotation":0,"id":82,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":60,"height":14,"lockAspectRatio":false,"lockShape":false,"order":73,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"依赖关系
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":591,"y":238,"rotation":0,"id":83,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":60,"height":14,"lockAspectRatio":false,"lockShape":false,"order":74,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"聚合关系
","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]}],"background":"#FFFFFF","width":926,"height":455,"maxWidth":5000,"maxHeight":5000,"nodeIndex":84,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":true,"drawingGuidesOn":true,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"shapeStyles":{},"lineStyles":{},"textStyles":{},"themeData":null}} \ No newline at end of file diff --git a/Day09/res/uml-example.png b/Day09/res/uml-example.png deleted file mode 100644 index 14e8e9c37..000000000 Binary files a/Day09/res/uml-example.png and /dev/null differ diff --git a/Day09/shape.py b/Day09/shape.py deleted file mode 100644 index 8bd292c26..000000000 --- a/Day09/shape.py +++ /dev/null @@ -1,66 +0,0 @@ -""" - -继承的应用 - - 抽象类 - - 抽象方法 - - 方法重写 - - 多态 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-12 - -""" - -from abc import ABCMeta, abstractmethod -from math import pi - - -class Shape(object, metaclass=ABCMeta): - - @abstractmethod - def perimeter(self): - pass - - @abstractmethod - def area(self): - pass - - -class Circle(Shape): - - def __init__(self, radius): - self._radius = radius - - def perimeter(self): - return 2 * pi * self._radius - - def area(self): - return pi * self._radius ** 2 - - def __str__(self): - return '我是一个圆' - - -class Rect(Shape): - - def __init__(self, width, height): - self._width = width - self._height = height - - def perimeter(self): - return 2 * (self._width + self._height) - - def area(self): - return self._width * self._height - - def __str__(self): - return '我是一个矩形' - - -if __name__ == '__main__': - shapes = [Circle(5), Circle(3.2), Rect(3.2, 6.3)] - for shape in shapes: - print(shape) - print('周长:', shape.perimeter()) - print('面积:', shape.area()) diff --git a/Day09/triangle.py b/Day09/triangle.py deleted file mode 100644 index d6feb762d..000000000 --- a/Day09/triangle.py +++ /dev/null @@ -1,53 +0,0 @@ -""" - -实例方法和类方法的应用 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-12 - -""" - -from math import sqrt - - -class Triangle(object): - - def __init__(self, a, b, c): - self._a = a - self._b = b - self._c = c - - # 静态方法 - @staticmethod - def is_valid(a, b, c): - return a + b > c and b + c > a and c + a > b - - # 实例方法 - def perimeter(self): - return self._a + self._b + self._c - - # 实例方法 - def area(self): - p = self.perimeter() / 2 - return sqrt(p * (p - self._a) * (p - self._b) * (p - self._c)) - - -if __name__ == '__main__': - # 用字符串的split方法将字符串拆分成一个列表 - # 再通过map函数对列表中的每个字符串进行映射处理成小数 - a, b, c = map(float, input('请输入三条边: ').split()) - # 先判断给定长度的三条边能否构成三角形 - # 如果能才创建三角形对象 - if Triangle.is_valid(a, b, c): - tri = Triangle(a, b, c) - print('周长:', tri.perimeter()) - print('面积:', tri.area()) - # 如果传入对象作为方法参数也可以通过类调用实例方法 - # print('周长:', Triangle.perimeter(tri)) - # print('面积:', Triangle.area(tri)) - # 看看下面的代码就知道其实二者本质上是一致的 - # print(type(tri.perimeter)) - # print(type(Triangle.perimeter)) - else: - print('不能构成三角形.') diff --git "a/Day09/\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" "b/Day09/\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" deleted file mode 100644 index 836f22bad..000000000 --- "a/Day09/\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" +++ /dev/null @@ -1,766 +0,0 @@ -## 面向对象进阶 - -在前面的章节我们已经了解了面向对象的入门知识,知道了如何定义类,如何创建对象以及如何给对象发消息。为了能够更好的使用面向对象编程思想进行程序开发,我们还需要对Python中的面向对象编程进行更为深入的了解。 - -### @property装饰器 - -之前我们讨论过Python中属性和方法访问权限的问题,虽然我们不建议将属性设置为私有的,但是如果直接将属性暴露给外界也是有问题的,比如我们没有办法检查赋给属性的值是否有效。我们之前的建议是将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下所示。 - -```Python -class Person(object): - - def __init__(self, name, age): - self._name = name - self._age = age - - # 访问器 - getter方法 - @property - def name(self): - return self._name - - # 访问器 - getter方法 - @property - def age(self): - return self._age - - # 修改器 - setter方法 - @age.setter - def age(self, age): - self._age = age - - def play(self): - if self._age <= 16: - print('%s正在玩飞行棋.' % self._name) - else: - print('%s正在玩斗地主.' % self._name) - - -def main(): - person = Person('王大锤', 12) - person.play() - person.age = 22 - person.play() - # person.name = '白元芳' # AttributeError: can't set attribute - - -if __name__ == '__main__': - main() - -``` - -### \_\_slots\_\_魔法 - -我们讲到这里,不知道大家是否已经意识到,Python是一门[动态语言](https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%AF%AD%E8%A8%80)。通常,动态语言允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行解绑定。但是如果我们需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义\_\_slots\_\_变量来进行限定。需要注意的是\_\_slots\_\_的限定只对当前类的对象生效,对子类并不起任何作用。 - -```Python -class Person(object): - - # 限定Person对象只能绑定_name, _age和_gender属性 - __slots__ = ('_name', '_age', '_gender') - - def __init__(self, name, age): - self._name = name - self._age = age - - @property - def name(self): - return self._name - - @property - def age(self): - return self._age - - @age.setter - def age(self, age): - self._age = age - - def play(self): - if self._age <= 16: - print('%s正在玩飞行棋.' % self._name) - else: - print('%s正在玩斗地主.' % self._name) - - -def main(): - person = Person('王大锤', 22) - person.play() - person._gender = '男' - # AttributeError: 'Person' object has no attribute '_is_gay' - # person._is_gay = True - -``` - -### 静态方法和类方法 - -之前,我们在类中定义的方法都是对象方法,也就是说这些方法都是发送给对象的消息。实际上,我们写在类中的方法并不需要都是对象方法,例如我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长未必能构造出三角形对象,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),所以这个方法是属于三角形类而并不属于三角形对象的。我们可以使用静态方法来解决这类问题,代码如下所示。 - -```Python -from math import sqrt - - -class Triangle(object): - - def __init__(self, a, b, c): - self._a = a - self._b = b - self._c = c - - @staticmethod - def is_valid(a, b, c): - return a + b > c and b + c > a and a + c > b - - def perimeter(self): - return self._a + self._b + self._c - - def area(self): - half = self.perimeter() / 2 - return sqrt(half * (half - self._a) * - (half - self._b) * (half - self._c)) - - -def main(): - a, b, c = 3, 4, 5 - # 静态方法和类方法都是通过给类发消息来调用的 - if Triangle.is_valid(a, b, c): - t = Triangle(a, b, c) - print(t.perimeter()) - # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数 - # print(Triangle.perimeter(t)) - print(t.area()) - # print(Triangle.area(t)) - else: - print('无法构成三角形.') - - -if __name__ == '__main__': - main() - -``` - -和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。 - -```Python -from time import time, localtime, sleep - - -class Clock(object): - """数字时钟""" - - def __init__(self, hour=0, minute=0, second=0): - self._hour = hour - self._minute = minute - self._second = second - - @classmethod - def now(cls): - ctime = localtime(time()) - return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec) - - def run(self): - """走字""" - self._second += 1 - if self._second == 60: - self._second = 0 - self._minute += 1 - if self._minute == 60: - self._minute = 0 - self._hour += 1 - if self._hour == 24: - self._hour = 0 - - def show(self): - """显示时间""" - return '%02d:%02d:%02d' % \ - (self._hour, self._minute, self._second) - - -def main(): - # 通过类方法创建对象并获取系统时间 - clock = Clock.now() - while True: - print(clock.show()) - sleep(1) - clock.run() - - -if __name__ == '__main__': - main() - -``` - -### 类之间的关系 - -简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。 - -- is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。 -- has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。 -- use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。 - -我们可以使用一种叫做[UML](https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E5%BB%BA%E6%A8%A1%E8%AF%AD%E8%A8%80)(统一建模语言)的东西来进行面向对象建模,其中一项重要的工作就是把类和类之间的关系用标准化的图形符号描述出来。关于UML我们在这里不做详细的介绍,有兴趣的读者可以自行阅读[《UML面向对象设计基础》](https://e.jd.com/30392949.html)一书。 - - - - - -利用类之间的这些关系,我们可以在已有类的基础上来完成某些操作,也可以在已有类的基础上创建新的类,这些都是实现代码复用的重要手段。复用现有的代码不仅可以减少开发的工作量,也有利于代码的管理和维护,这是我们在日常工作中都会使用到的技术手段。 - -### 继承和多态 - -刚才我们提到了,可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为[里氏替换原则](https://zh.wikipedia.org/wiki/%E9%87%8C%E6%B0%8F%E6%9B%BF%E6%8D%A2%E5%8E%9F%E5%88%99)。下面我们先看一个继承的例子。 - -```Python -class Person(object): - """人""" - - def __init__(self, name, age): - self._name = name - self._age = age - - @property - def name(self): - return self._name - - @property - def age(self): - return self._age - - @age.setter - def age(self, age): - self._age = age - - def play(self): - print('%s正在愉快的玩耍.' % self._name) - - def watch_av(self): - if self._age >= 18: - print('%s正在观看爱情动作片.' % self._name) - else: - print('%s只能观看《熊出没》.' % self._name) - - -class Student(Person): - """学生""" - - def __init__(self, name, age, grade): - super().__init__(name, age) - self._grade = grade - - @property - def grade(self): - return self._grade - - @grade.setter - def grade(self, grade): - self._grade = grade - - def study(self, course): - print('%s的%s正在学习%s.' % (self._grade, self._name, course)) - - -class Teacher(Person): - """老师""" - - def __init__(self, name, age, title): - super().__init__(name, age) - self._title = title - - @property - def title(self): - return self._title - - @title.setter - def title(self, title): - self._title = title - - def teach(self, course): - print('%s%s正在讲%s.' % (self._name, self._title, course)) - - -def main(): - stu = Student('王大锤', 15, '初三') - stu.study('数学') - stu.watch_av() - t = Teacher('骆昊', 38, '老叫兽') - t.teach('Python程序设计') - t.watch_av() - - -if __name__ == '__main__': - main() - -``` - -子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。 - -```Python -from abc import ABCMeta, abstractmethod - - -class Pet(object, metaclass=ABCMeta): - """宠物""" - - def __init__(self, nickname): - self._nickname = nickname - - @abstractmethod - def make_voice(self): - """发出声音""" - pass - - -class Dog(Pet): - """狗""" - - def make_voice(self): - print('%s: 汪汪汪...' % self._nickname) - - -class Cat(Pet): - """猫""" - - def make_voice(self): - print('%s: 喵...喵...' % self._nickname) - - -def main(): - pets = [Dog('旺财'), Cat('凯蒂'), Dog('大黄')] - for pet in pets: - pet.make_voice() - - -if __name__ == '__main__': - main() - -``` - -在上面的代码中,我们将`Pet`类处理成了一个抽象类,所谓抽象类就是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它。Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过`abc`模块的`ABCMeta`元类和`abstractmethod`包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,`Dog`和`Cat`两个子类分别对`Pet`类中的`make_voice`抽象方法进行了重写并给出了不同的实现版本,当我们在`main`函数中调用该方法时,这个方法就表现出了多态行为(同样的方法做了不同的事情)。 - -### 综合案例 - -#### 案例1:奥特曼打小怪兽 - -```Python -from abc import ABCMeta, abstractmethod -from random import randint, randrange - - -class Fighter(object, metaclass=ABCMeta): - """战斗者""" - - # 通过__slots__魔法限定对象可以绑定的成员变量 - __slots__ = ('_name', '_hp') - - def __init__(self, name, hp): - """ - 初始化方法 - - :param name: 名字 - :param hp: 生命值 - """ - self._name = name - self._hp = hp - - @property - def name(self): - return self._name - - @property - def hp(self): - return self._hp - - @hp.setter - def hp(self, hp): - self._hp = hp if hp >= 0 else 0 - - @property - def alive(self): - return self._hp > 0 - - @abstractmethod - def attack(self, other): - """ - 攻击 - - :param other: 被攻击的对象 - """ - pass - - -class Ultraman(Fighter): - """奥特曼""" - - __slots__ = ('_name', '_hp', '_mp') - - def __init__(self, name, hp, mp): - """ - 初始化方法 - - :param name: 名字 - :param hp: 生命值 - :param mp: 魔法值 - """ - super().__init__(name, hp) - self._mp = mp - - def attack(self, other): - other.hp -= randint(15, 25) - - def huge_attack(self, other): - """ - 究极必杀技(打掉对方至少50点或四分之三的血) - - :param other: 被攻击的对象 - - :return: 使用成功返回True否则返回False - """ - if self._mp >= 50: - self._mp -= 50 - injury = other.hp * 3 // 4 - injury = injury if injury >= 50 else 50 - other.hp -= injury - return True - else: - self.attack(other) - return False - - def magic_attack(self, others): - """ - 魔法攻击 - - :param others: 被攻击的群体 - - :return: 使用魔法成功返回True否则返回False - """ - if self._mp >= 20: - self._mp -= 20 - for temp in others: - if temp.alive: - temp.hp -= randint(10, 15) - return True - else: - return False - - def resume(self): - """恢复魔法值""" - incr_point = randint(1, 10) - self._mp += incr_point - return incr_point - - def __str__(self): - return '~~~%s奥特曼~~~\n' % self._name + \ - '生命值: %d\n' % self._hp + \ - '魔法值: %d\n' % self._mp - - -class Monster(Fighter): - """小怪兽""" - - __slots__ = ('_name', '_hp') - - def attack(self, other): - other.hp -= randint(10, 20) - - def __str__(self): - return '~~~%s小怪兽~~~\n' % self._name + \ - '生命值: %d\n' % self._hp - - -def is_any_alive(monsters): - """判断有没有小怪兽是活着的""" - for monster in monsters: - if monster.alive > 0: - return True - return False - - -def select_alive_one(monsters): - """选中一只活着的小怪兽""" - monsters_len = len(monsters) - while True: - index = randrange(monsters_len) - monster = monsters[index] - if monster.alive > 0: - return monster - - -def display_info(ultraman, monsters): - """显示奥特曼和小怪兽的信息""" - print(ultraman) - for monster in monsters: - print(monster, end='') - - -def main(): - u = Ultraman('骆昊', 1000, 120) - m1 = Monster('舒小玲', 250) - m2 = Monster('白元芳', 500) - m3 = Monster('王大锤', 750) - ms = [m1, m2, m3] - fight_round = 1 - while u.alive and is_any_alive(ms): - print('========第%02d回合========' % fight_round) - m = select_alive_one(ms) # 选中一只小怪兽 - skill = randint(1, 10) # 通过随机数选择使用哪种技能 - if skill <= 6: # 60%的概率使用普通攻击 - print('%s使用普通攻击打了%s.' % (u.name, m.name)) - u.attack(m) - print('%s的魔法值恢复了%d点.' % (u.name, u.resume())) - elif skill <= 9: # 30%的概率使用魔法攻击(可能因魔法值不足而失败) - if u.magic_attack(ms): - print('%s使用了魔法攻击.' % u.name) - else: - print('%s使用魔法失败.' % u.name) - else: # 10%的概率使用究极必杀技(如果魔法值不足则使用普通攻击) - if u.huge_attack(m): - print('%s使用究极必杀技虐了%s.' % (u.name, m.name)) - else: - print('%s使用普通攻击打了%s.' % (u.name, m.name)) - print('%s的魔法值恢复了%d点.' % (u.name, u.resume())) - if m.alive > 0: # 如果选中的小怪兽没有死就回击奥特曼 - print('%s回击了%s.' % (m.name, u.name)) - m.attack(u) - display_info(u, ms) # 每个回合结束后显示奥特曼和小怪兽的信息 - fight_round += 1 - print('\n========战斗结束!========\n') - if u.alive > 0: - print('%s奥特曼胜利!' % u.name) - else: - print('小怪兽胜利!') - - -if __name__ == '__main__': - main() - -``` - -#### 案例2:扑克游戏 - -```Python -from random import randrange - - -class Card(object): - """一张牌""" - - def __init__(self, suite, face): - self._suite = suite - self._face = face - - @property - def face(self): - return self._face - - @property - def suite(self): - return self._suite - - def __str__(self): - all_suites = ('♠', '♥', '♣', '♦') - if self._face == 1: - face_str = 'A' - elif self._face == 11: - face_str = 'J' - elif self._face == 12: - face_str = 'Q' - elif self._face == 13: - face_str = 'K' - else: - face_str = str(self._face) - return '%s%s' % (all_suites[self._suite], face_str) - - -class Poker(object): - """一副牌""" - - def __init__(self): - self._cards = [] - self._current = 0 - for suite in range(4): - for face in range(1, 14): - card = Card(suite, face) - self._cards.append(card) - - @property - def cards(self): - return self._cards - - def shuffle(self): - """洗牌(随机乱序)""" - self._current = 0 - cards_len = len(self._cards) - for index in range(cards_len): - pos = randrange(cards_len) - self._cards[index], self._cards[pos] = \ - self._cards[pos], self._cards[index] - - @property - def next(self): - """发牌""" - card = self._cards[self._current] - self._current += 1 - return card - - @property - def has_next(self): - """还有没有牌""" - return self._current < len(self._cards) - - -class Player(object): - """玩家""" - - def __init__(self, name): - self._name = name - self._cards_on_hand = [] - - @property - def name(self): - return self._name - - @property - def cards_on_hand(self): - return self._cards_on_hand - - def get(self, card): - """摸牌""" - self._cards_on_hand.append(card) - - def arrange(self, card_key): - """玩家整理手上的牌""" - self._cards_on_hand.sort(key=card_key) - - -# 排序规则-先根据花色再根据点数排序 -def get_key(card): - return (card.suite, card.face) - - -def main(): - p = Poker() - p.shuffle() - players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')] - for _ in range(13): - for player in players: - player.get(p.next) - for player in players: - print(player.name + ':', end=' ') - player.arrange(get_key) - for card in player.cards_on_hand: - print(card, end=' ') - print() - - -if __name__ == '__main__': - main() - -``` - ->**说明**:大家可以自己尝试在上面代码的基础上写一个简单的扑克游戏,例如21点(Black Jack),游戏的规则可以自己在网上找一找。 - -#### 案例3:工资结算系统 - -```Python -""" - -某公司有三种类型的员工 分别是部门经理、程序员和销售员 -需要设计一个工资结算系统 根据提供的员工信息来计算月薪 -部门经理的月薪是每月固定15000元 -程序员的月薪按本月工作时间计算 每小时150元 -销售员的月薪是1200元的底薪加上销售额5%的提成 - -""" -from abc import ABCMeta, abstractmethod - - -class Employee(object, metaclass=ABCMeta): - """员工""" - - def __init__(self, name): - """ - 初始化方法 - - :param name: 姓名 - """ - self._name = name - - @property - def name(self): - return self._name - - @abstractmethod - def get_salary(self): - """ - 获得月薪 - - :return: 月薪 - """ - pass - - -class Manager(Employee): - """部门经理""" - - def get_salary(self): - return 15000.0 - - -class Programmer(Employee): - """程序员""" - - def __init__(self, name, working_hour=0): - super().__init__(name) - self._working_hour = working_hour - - @property - def working_hour(self): - return self._working_hour - - @working_hour.setter - def working_hour(self, working_hour): - self._working_hour = working_hour if working_hour > 0 else 0 - - def get_salary(self): - return 150.0 * self._working_hour - - -class Salesman(Employee): - """销售员""" - - def __init__(self, name, sales=0): - super().__init__(name) - self._sales = sales - - @property - def sales(self): - return self._sales - - @sales.setter - def sales(self, sales): - self._sales = sales if sales > 0 else 0 - - def get_salary(self): - return 1200.0 + self._sales * 0.05 - - -def main(): - emps = [ - Manager('刘备'), Programmer('诸葛亮'), - Manager('曹操'), Salesman('荀彧'), - Salesman('吕布'), Programmer('张辽'), - Programmer('赵云') - ] - for emp in emps: - if isinstance(emp, Programmer): - emp.working_hour = int(input('请输入%s本月工作时间: ' % emp.name)) - elif isinstance(emp, Salesman): - emp.sales = float(input('请输入%s本月销售额: ' % emp.name)) - # 同样是接收get_salary这个消息但是不同的员工表现出了不同的行为(多态) - print('%s本月工资为: ¥%s元' % - (emp.name, emp.get_salary())) - - -if __name__ == '__main__': - main() - -``` - diff --git a/Day10/ball.py b/Day10/ball.py deleted file mode 100644 index 3d5369561..000000000 --- a/Day10/ball.py +++ /dev/null @@ -1,109 +0,0 @@ -from enum import Enum, unique -from math import sqrt -from random import randint - -import pygame - - -@unique -class Color(Enum): - """颜色""" - - RED = (255, 0, 0) - GREEN = (0, 255, 0) - BLUE = (0, 0, 255) - BLACK = (0, 0, 0) - WHITE = (255, 255, 255) - GRAY = (242, 242, 242) - - @staticmethod - def random_color(): - """获得随机颜色""" - r = randint(0, 255) - g = randint(0, 255) - b = randint(0, 255) - return (r, g, b) - - -class Ball(object): - """球""" - - def __init__(self, x, y, radius, sx, sy, color=Color.RED): - """初始化方法""" - self.x = x - self.y = y - self.radius = radius - self.sx = sx - self.sy = sy - self.color = color - self.alive = True - - def move(self, screen): - """移动""" - self.x += self.sx - self.y += self.sy - if self.x - self.radius <= 0 or self.x + self.radius >= screen.get_width(): - self.sx = -self.sx - if self.y - self.radius <= 0 or self.y + self.radius >= screen.get_height(): - self.sy = -self.sy - - def eat(self, other): - """吃其他球""" - if self.alive and other.alive and self != other: - dx, dy = self.x - other.x, self.y - other.y - distance = sqrt(dx ** 2 + dy ** 2) - if distance < self.radius + other.radius \ - and self.radius > other.radius: - other.alive = False - self.radius = self.radius + int(other.radius * 0.146) - - def draw(self, screen): - """在窗口上绘制球""" - pygame.draw.circle(screen, self.color, - (self.x, self.y), self.radius, 0) - - -def main(): - # 定义用来装所有球的容器 - balls = [] - # 初始化导入的pygame中的模块 - pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 - screen = pygame.display.set_mode((800, 600)) - print(screen.get_width()) - print(screen.get_height()) - # 设置当前窗口的标题 - pygame.display.set_caption('大球吃小球') - # 定义变量来表示小球在屏幕上的位置 - x, y = 50, 50 - running = True - # 开启一个事件循环处理发生的事件 - while running: - # 从消息队列中获取事件并对事件进行处理 - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: - x, y = event.pos - radius = randint(10, 100) - sx, sy = randint(-10, 10), randint(-10, 10) - color = Color.random_color() - ball = Ball(x, y, radius, sx, sy, color) - balls.append(ball) - screen.fill((255, 255, 255)) - for ball in balls: - if ball.alive: - ball.draw(screen) - else: - balls.remove(ball) - pygame.display.flip() - # 每隔50毫秒就改变小球的位置再刷新窗口 - pygame.time.delay(50) - for ball in balls: - ball.move(screen) - for other in balls: - ball.eat(other) - - -if __name__ == '__main__': - main() diff --git a/Day10/gui1.py b/Day10/gui1.py deleted file mode 100644 index 6eb747bd8..000000000 --- a/Day10/gui1.py +++ /dev/null @@ -1,57 +0,0 @@ -""" - -使用tkinter创建GUI - - 顶层窗口 - - 控件 - - 布局 - - 事件回调 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-14 - -""" - -import tkinter -import tkinter.messagebox - - -def main(): - flag = True - - # 修改标签上的文字 - def change_label_text(): - nonlocal flag - flag = not flag - color, msg = ('red', 'Hello, world!')\ - if flag else ('blue', 'Goodbye, world!') - label.config(text=msg, fg=color) - - # 确认退出 - def confirm_to_quit(): - if tkinter.messagebox.askokcancel('温馨提示', '确定要退出吗?'): - top.quit() - - # 创建顶层窗口 - top = tkinter.Tk() - # 设置窗口大小 - top.geometry('240x160') - # 设置窗口标题 - top.title('小游戏') - # 创建标签对象 - label = tkinter.Label(top, text='Hello, world!', font='Arial -32', fg='red') - label.pack(expand=1) - # 创建一个装按钮的容器 - panel = tkinter.Frame(top) - # 创建按钮对象 - button1 = tkinter.Button(panel, text='修改', command=change_label_text) - button1.pack(side='left') - button2 = tkinter.Button(panel, text='退出', command=confirm_to_quit) - button2.pack(side='right') - panel.pack(side='bottom') - # 开启主事件循环 - tkinter.mainloop() - - -if __name__ == '__main__': - main() diff --git a/Day10/gui2.py b/Day10/gui2.py deleted file mode 100644 index 69c81d0da..000000000 --- a/Day10/gui2.py +++ /dev/null @@ -1,43 +0,0 @@ -""" - -使用tkinter创建GUI - - 使用画布绘图 - - 处理鼠标事件 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-14 - -""" - -import tkinter - - -def mouse_evt_handler(evt=None): - row = round((evt.y - 20) / 40) - col = round((evt.x - 20) / 40) - pos_x = 40 * col - pos_y = 40 * row - canvas.create_oval(pos_x, pos_y, 40 + pos_x, 40 + pos_y, fill='black') - - -top = tkinter.Tk() -# 设置窗口尺寸 -top.geometry('620x620') -# 设置窗口标题 -top.title('五子棋') -# 设置窗口大小不可改变 -top.resizable(False, False) -# 设置窗口置顶 -top.wm_attributes('-topmost', 1) -canvas = tkinter.Canvas(top, width=600, height=600, bd=0, highlightthickness=0) -canvas.bind('| - | 成都 | -北京 | -杭州 | -
| Python从入门到住院全国巡演 | -2018年2月28日 上午9:30 | -2018年3月28日 上午9:30 | -2018年4月28日 上午9:30 | -
| MySQL从删库到跑路公开课 | -2018年2月27日 上午9:30 | -2018年3月5日 上午9:30 | -2018年4月2日 上午9:30 | -
| Django从学习到上吊交流会 | -2018年2月28日 上午9:30 | -- | 2018年5月21日 上午9:30 | -
| 爬虫从精通到坐牢无遮大会 | -2018年3月3日 上午9:30 | -2018年4月17日 上午9:30 | -2018年1月15日 上午9:30 | -
| One | +Two | +
|---|---|
| Three | +Four | +
| Five | +Six | +
| Seven | +Eight | +
| Nine | +Ten | +
| Eleven | +Twelve | +
+
+5. 1972年~1973年:Dennis Ritchie发明了C语言来取代可移植性较差的B语言,并开启了用C语言重写Unix的工作。
+
+6. 1974年:Unix推出了里程碑意义的第5版,几乎完全用C语言来实现。
+
+7. 1979年:从Unix第7版开始,AT&T发布新的使用条款,将Unix私有化。
+
+8. 1987年:Andrew S. Tanenbaum教授为了能在课堂上为学生讲解操作系统运作的细节,决定在不使用任何AT&T的源代码前提下,自行开发与Unix兼容的操作系统以避免版权上的争议,该系统被命名为Minix。
+
+
+
+9. 1991年:Linus Torvalds就读于芬兰赫尔辛基大学期间,尝试在Minix上做一些开发工作,但因为Minix只是作为教学用途的操作系统,功能并不强大,为了方便在学校的新闻组和邮件系统中读写和下载文件,Linus编写了磁盘驱动程序和文件系统,这些东西形成了Linux系统内核的雏形。
+
+ 
+
+下图是Unix操作系统家族的图谱。
+
+
+
+### Linux概述
+
+Linux是一个通用操作系统。一个操作系统要负责任务调度、内存分配、处理外围设备I/O等操作。操作系统通常由内核(运行其他程序,管理像磁盘、打印机等硬件设备的核心程序)和系统程序(设备驱动、底层库、shell、服务程序等)两部分组成。
+
+Linux内核是芬兰人Linus Torvalds开发的,于1991年9月发布。而Linux操作系统作为Internet时代的产物,它是由全世界许多开发者共同合作开发的,是一个自由的操作系统(注意自由和免费并不是同一个概念,想了解二者的差别可以[点击这里](https://www.debian.org/intro/free))。
+
+### Linux系统优点
+
+1. 通用操作系统,不跟特定的硬件绑定。
+2. 用C语言编写,可移植性强,有内核编程接口。
+3. 支持多用户和多任务,支持安全的分层文件系统。
+4. 大量的实用程序,完善的网络功能以及强大的支持文档。
+5. 可靠的安全性和良好的稳定性,对开发者更友好。
+
+### Linux系统发行版本
+
+1. [Redhat](https://www.redhat.com/en)
+2. [Ubuntu](https://www.ubuntu.com/)
+3. [CentOS](https://www.centos.org/)
+4. [Fedora](https://getfedora.org/)
+5. [Debian](https://www.debian.org/)
+6. [openSUSE](https://www.opensuse.org/)
+
+### 基础命令
+
+Linux系统的命令通常都是如下所示的格式:
+
+```Shell
+命令名称 [命名参数] [命令对象]
+```
+
+1. 获取登录信息 - **w** / **who** / **last**/ **lastb**。
+
+ ```Shell
+ [root ~]# w
+ 23:31:16 up 12:16, 2 users, load average: 0.00, 0.01, 0.05
+ USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
+ root pts/0 182.139.66.250 23:03 4.00s 0.02s 0.00s w
+ jackfrue pts/1 182.139.66.250 23:26 3:56 0.00s 0.00s -bash
+ [root ~]# who
+ root pts/0 2018-04-12 23:03 (182.139.66.250)
+ jackfrued pts/1 2018-04-12 23:26 (182.139.66.250)
+ [root ~]# who am i
+ root pts/0 2018-04-12 23:03 (182.139.66.250)
+ [root ~]# who mom likes
+ root pts/0 2018-04-12 23:03 (182.139.66.250)
+ [root ~]# last
+ root pts/0 117.136.63.184 Sun May 26 18:57 still logged in
+ reboot system boot 3.10.0-957.10.1. Mon May 27 02:52 - 19:10 (-7:-42)
+ root pts/4 117.136.63.184 Sun May 26 18:51 - crash (08:01)
+ root pts/4 117.136.63.184 Sun May 26 18:49 - 18:49 (00:00)
+ root pts/3 117.136.63.183 Sun May 26 18:35 - crash (08:17)
+ root pts/2 117.136.63.183 Sun May 26 18:34 - crash (08:17)
+ root pts/0 117.136.63.183 Sun May 26 18:10 - crash (08:42)
+ ```
+
+2. 查看自己使用的Shell - **ps**。
+
+ Shell也被称为“壳”或“壳程序”,它是用户与操作系统内核交流的翻译官,简单的说就是人与计算机交互的界面和接口。目前很多Linux系统默认的Shell都是bash(Bourne Again SHell),因为它可以使用tab键进行命令和路径补全、可以保存历史命令、可以方便的配置环境变量以及执行批处理操作。
+
+ ```Shell
+ [root ~]# ps
+ PID TTY TIME CMD
+ 3531 pts/0 00:00:00 bash
+ 3553 pts/0 00:00:00 ps
+ ```
+
+3. 查看命令的说明和位置 - **whatis** / **which** / **whereis**。
+
+ ```Shell
+ [root ~]# whatis ps
+ ps (1) - report a snapshot of the current processes.
+ [root ~]# whatis python
+ python (1) - an interpreted, interactive, object-oriented programming language
+ [root ~]# whereis ps
+ ps: /usr/bin/ps /usr/share/man/man1/ps.1.gz
+ [root ~]# whereis python
+ python: /usr/bin/python /usr/bin/python2.7 /usr/lib/python2.7 /usr/lib64/python2.7 /etc/python /usr/include/python2.7 /usr/share/man/man1/python.1.gz
+ [root ~]# which ps
+ /usr/bin/ps
+ [root ~]# which python
+ /usr/bin/python
+ ```
+
+4. 清除屏幕上显示的内容 - **clear**。
+
+5. 查看帮助文档 - **man** / **info** / **--help** / **apropos**。
+ ```Shell
+ [root@izwz97tbgo9lkabnat2lo8z ~]# ps --help
+ Usage:
+ ps [options]
+ Try 'ps --help