diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 837e02cb4..cf0540a4f --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,14 @@ venv *.pyc __pycache__ +### VisualStudioCode ### +.vscode/* +.vscode/** +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history diff --git "a/Day01-15/01.\345\210\235\350\257\206Python.md" "b/Day01-15/01.\345\210\235\350\257\206Python.md" new file mode 100755 index 000000000..366e71f8f --- /dev/null +++ "b/Day01-15/01.\345\210\235\350\257\206Python.md" @@ -0,0 +1,258 @@ +## 初识Python + +> **温馨提示**:2018年创建这个仓库的时候,关于Python语言基础这个部分的内容写得相对是比较粗糙,对粗学者可能不是那么友好。如果你正好是一名初学者,建议你移步到我的另一个仓库[Python-for-Freshmen-2023](https://github.com/jackfrued/Python-for-Freshmen-2023),这个仓库对初学者更加友好,对应的内容,大家也可以在我的知乎专栏“从零开始学Python”中找到,点击进入[传送门](https://www.zhihu.com/column/c_1216656665569013760)。 + +### Python简介 + +#### Python的历史 + +1. 1989年圣诞节:Guido von Rossum开始写Python语言的编译器。 +2. 1991年2月:第一个Python编译器(同时也是解释器)诞生,它是用C语言实现的(后面),可以调用C语言的库函数。在最早的版本中,Python已经提供了对“类”,“函数”,“异常处理”等构造块的支持,还有对列表、字典等核心数据类型,同时支持以模块为基础来构造应用程序。 +3. 1994年1月:Python 1.0正式发布。 +4. 2000年10月16日:Python 2.0发布,增加了完整的[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8)),提供了对[Unicode](https://zh.wikipedia.org/wiki/Unicode)的支持。与此同时,Python的整个开发过程更加透明,社区对开发进度的影响逐渐扩大,生态圈开始慢慢形成。 +5. 2008年12月3日:Python 3.0发布,它并不完全兼容之前的Python代码,不过因为目前还有不少公司在项目和运维中使用Python 2.x版本,所以Python 3.x的很多新特性后来也被移植到Python 2.6/2.7版本中。 + +目前我使用的Python 3.7.x的版本是在2018年发布的,Python的版本号分为三段,形如A.B.C。其中A表示大版本号,一般当整体重写,或出现不向后兼容的改变时,增加A;B表示功能更新,出现新功能时增加B;C表示小的改动(例如:修复了某个Bug),只要有修改就增加C。如果对Python的历史感兴趣,可以阅读名为[《Python简史》](http://www.cnblogs.com/vamei/archive/2013/02/06/2892628.html)的网络文章。 + +#### Python的优缺点 + +Python的优点很多,简单的可以总结为以下几点。 + +1. 简单明了,学习曲线低,比很多编程语言都容易上手。 +2. 开放源代码,拥有强大的社区和生态圈,尤其是在数据分析和机器学习领域。 +3. 解释型语言,天生具有平台可移植性,代码可以工作于不同的操作系统。 +4. 对两种主流的编程范式(面向对象编程和函数式编程)都提供了支持。 +5. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。 + +Python的缺点主要集中在以下几点。 + +1. 执行效率稍低,对执行效率要求高的部分可以由其他语言(如:C、C++)编写。 +2. 代码无法加密,但是现在很多公司都不销售卖软件而是销售服务,这个问题会被弱化。 +3. 在开发时可以选择的框架太多(如Web框架就有100多个),有选择的地方就有错误。 + +#### Python的应用领域 + +目前Python在Web应用后端开发、云基础设施建设、DevOps、网络数据采集(爬虫)、自动化测试、数据分析、机器学习等领域都有着广泛的应用。 + +### 安装Python解释器 + +想要开始Python编程之旅,首先得在自己使用的计算机上安装Python解释器环境,下面将以安装官方的Python解释器为例,讲解如何在不同的操作系统上安装Python环境。官方的Python解释器是用C语言实现的,也是使用最为广泛的Python解释器,通常称之为CPython。除此之外,Python解释器还有Java语言实现的Jython、C#语言实现的IronPython以及PyPy、Brython、Pyston等版本,有兴趣的读者可以自行了解。 + +#### Windows环境 + +可以在[Python官方网站](https://www.python.org)下载到Python的Windows安装程序(exe文件),需要注意的是如果在Windows 7环境下安装Python 3.x,需要先安装Service Pack 1补丁包(可以通过一些工具软件自动安装系统补丁的功能来安装),安装过程建议勾选“Add Python 3.x to PATH”(将Python 3.x添加到PATH环境变量)并选择自定义安装,在设置“Optional Features”界面最好将“pip”、“tcl/tk”、“Python test suite”等项全部勾选上。强烈建议选择自定义的安装路径并保证路径中没有中文。安装完成会看到“Setup was successful”的提示。如果稍后运行Python程序时,出现因为缺失一些动态链接库文件而导致Python解释器无法工作的问题,可以按照下面的方法加以解决。 + +如果系统显示api-ms-win-crt\*.dll文件缺失,可以参照[《api-ms-win-crt\*.dll缺失原因分析和解决方法》]()一文讲解的方法进行处理或者直接在[微软官网](https://www.microsoft.com/zh-cn/download/details.aspx?id=48145)下载Visual C++ Redistributable for Visual Studio 2015文件进行修复;如果是因为更新Windows的DirectX之后导致某些动态链接库文件缺失问题,可以下载一个[DirectX修复工具]()进行修复。 + +#### Linux环境 + +Linux环境自带了Python 2.x版本,但是如果要更新到3.x的版本,可以在[Python的官方网站](https://www.python.org)下载Python的源代码并通过源代码构建安装的方式进行安装,具体的步骤如下所示(以CentOS为例)。 + +1. 安装依赖库(因为没有这些依赖库可能在源代码构件安装时因为缺失底层依赖库而失败)。 + +```Shell +yum -y install wget gcc zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel +``` + +2. 下载Python源代码并解压缩到指定目录。 + +```Shell +wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tar.xz +xz -d Python-3.7.6.tar.xz +tar -xvf Python-3.7.6.tar +``` + +3. 切换至Python源代码目录并执行下面的命令进行配置和安装。 + +```Shell +cd Python-3.7.6 +./configure --prefix=/usr/local/python37 --enable-optimizations +make && make install +``` + +4. 修改用户主目录下名为.bash_profile的文件,配置PATH环境变量并使其生效。 + +```Shell +cd ~ +vim .bash_profile +``` + +```Shell +# ... 此处省略上面的代码 ... + +export PATH=$PATH:/usr/local/python37/bin + +# ... 此处省略下面的代码 ... +``` + +5. 激活环境变量。 + +```Shell +source .bash_profile +``` + +#### macOS环境 + +macOS也自带了Python 2.x版本,可以通过[Python的官方网站](https://www.python.org)提供的安装文件(pkg文件)安装Python 3.x的版本。默认安装完成后,可以通过在终端执行`python`命令来启动2.x版本的Python解释器,启动3.x版本的Python解释器需要执行`python3`命令。 + +### 运行Python程序 + +#### 确认Python的版本 + +可以Windows的命令行提示符中键入下面的命令。 + +```Shell +python --version +``` +在Linux或macOS系统的终端中键入下面的命令。 + +```Shell +python3 --version +``` + +当然也可以先输入`python`或`python3`进入交互式环境,再执行以下的代码检查Python的版本。 + +```Python +import sys + +print(sys.version_info) +print(sys.version) +``` + +#### 编写Python源代码 + +可以用文本编辑工具(推荐使用[Sublime]()、[Visual Studio Code]()等高级文本编辑工具)编写Python源代码并用py作为后缀名保存该文件,代码内容如下所示。 + +```Python +print('hello, world!') +``` + +#### 运行程序 + +切换到源代码所在的目录并执行下面的命令,看看屏幕上是否输出了"hello, world!"。 + +```Shell +python hello.py +``` + +或 + +```Shell +python3 hello.py +``` + +#### 代码中的注释 + +注释是编程语言的一个重要组成部分,用于在源代码中解释代码的作用从而增强程序的可读性和可维护性,当然也可以将源代码中不需要参与运行的代码段通过注释来去掉,这一点在调试程序的时候经常用到。注释在随源代码进入预处理器或编译时会被移除,不会在目标代码中保留也不会影响程序的执行结果。 + +1. 单行注释 - 以#和空格开头的部分 +2. 多行注释 - 三个引号开头,三个引号结尾 + +```Python +""" +第一个Python程序 - hello, world! +向伟大的Dennis M. Ritchie先生致敬 + +Version: 0.1 +Author: 骆昊 +""" +print('hello, world!') +# print("你好, 世界!") +``` + +### Python开发工具 + +#### IDLE - 自带的集成开发工具 + +IDLE是安装Python环境时自带的集成开发工具,如下图所示。但是由于IDLE的用户体验并不是那么好所以很少在实际开发中被采用。 + +![](./res/python-idle.png) + +#### IPython - 更好的交互式编程工具 + +IPython是一种基于Python的交互式解释器。相较于原生的Python交互式环境,IPython提供了更为强大的编辑和交互功能。可以通过Python的包管理工具pip安装IPython,具体的操作如下所示。 + +```Shell +pip install ipython +``` + +或 + +```Shell +pip3 install ipython +``` + +安装成功后,可以通过下面的ipython命令启动IPython,如下图所示。 + +![](./res/python-ipython.png) + +#### Sublime Text - 高级文本编辑器 + +![](./res/python-sublime.png) + +- 首先可以通过[官方网站](https://www.sublimetext.com/)下载安装程序安装Sublime Text 3或Sublime Text 2。 + +- 安装包管理工具。 + 1. 通过快捷键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') + ``` + 2. 在浏览器中输入 https://sublime.wbond.net/Package%20Control.sublime-package 下载包管理工具的安装包,并找到安装Sublime目录下名为"Installed Packages"的目录,把刚才下载的文件放到这个文件加下,然后重启Sublime Text就搞定了。 + + +- 安装插件。通过Preference菜单的Package Control或快捷键Ctrl+Shift+P打开命令面板,在面板中输入Install Package就可以找到安装插件的工具,然后再查找需要的插件。我们推荐大家安装以下几个插件: + + - SublimeCodeIntel - 代码自动补全工具插件。 + - Emmet - 前端开发代码模板插件。 + - Git - 版本控制工具插件。 + - Python PEP8 Autoformat - PEP8规范自动格式化插件。 + - ConvertToUTF8 - 将本地编码转换为UTF-8。 + +> **说明**:事实上[Visual Studio Code]()可能是更好的选择,它不用花钱并提供了更为完整和强大的功能,有兴趣的读者可以自行研究。 + +#### PyCharm - Python开发神器 + +PyCharm的安装、配置和使用在[《玩转PyCharm》](../番外篇/玩转PyCharm.md)进行了介绍,有兴趣的读者可以选择阅读。 + +![](./res/python-pycharm.png) + +### 练习 + +1. 在Python交互式环境中输入下面的代码并查看结果,请尝试将看到的内容翻译成中文。 + + ```Python + import this + ``` + + > **说明**:输入上面的代码,在Python的交互式环境中可以看到Tim Peter撰写的[“Python之禅”](../Python之禅.md),里面讲述的道理不仅仅适用于Python,也适用于其他编程语言。 + +2. 学习使用turtle在屏幕上绘制图形。 + + > **说明**:turtle是Python内置的一个非常有趣的模块,特别适合对计算机程序设计进行初体验的小伙伴,它最早是Logo语言的一部分,Logo语言是Wally Feurzig和Seymour Papert在1966发明的编程语言。 + + ```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/Day01-15/02.\350\257\255\350\250\200\345\205\203\347\264\240.md" "b/Day01-15/02.\350\257\255\350\250\200\345\205\203\347\264\240.md" new file mode 100755 index 000000000..f78e19906 --- /dev/null +++ "b/Day01-15/02.\350\257\255\350\250\200\345\205\203\347\264\240.md" @@ -0,0 +1,238 @@ +## 语言元素 + +#### 指令和程序 + +计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说的中央处理器,它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。我们通常所说的程序实际上就是指令的集合,我们程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。今天我们大多数时候使用的计算机,虽然它们的元器件做工越来越精密,处理能力越来越强大,但究其本质来说仍然属于[“冯·诺依曼结构”](https://zh.wikipedia.org/wiki/%E5%86%AF%C2%B7%E8%AF%BA%E4%BC%8A%E6%9B%BC%E7%BB%93%E6%9E%84)的计算机。“冯·诺依曼结构”有两个关键点,一是指出要将存储设备与中央处理器分开,二是提出了将数据以二进制方式编码。二进制是一种“逢二进一”的计数法,跟我们人类使用的“逢十进一”的计数法没有实质性的区别,人类因为有十根手指所以使用了十进制(因为在数数时十根手指用完之后就只能进位了,当然凡事都有例外,玛雅人可能是因为长年光着脚的原因把脚趾头也算上了,于是他们使用了二十进制的计数法,在这种计数法的指导下玛雅人的历法就与我们平常使用的历法不一样,而按照玛雅人的历法,2012年是上一个所谓的“太阳纪”的最后一年,而2013年则是新的“太阳纪”的开始,后来这件事情被以讹传讹的方式误传为”2012年是玛雅人预言的世界末日“这种荒诞的说法,今天我们可以大胆的猜测,玛雅文明之所以发展缓慢估计也与使用了二十进制有关)。对于计算机来说,二进制在物理器件上来说是最容易实现的(高电压表示1,低电压表示0),于是在“冯·诺依曼结构”的计算机都使用了二进制。虽然我们并不需要每个程序员都能够使用二进制的思维方式来工作,但是了解二进制以及它与我们生活中的十进制之间的转换关系,以及二进制与八进制和十六进制的转换关系还是有必要的。如果你对这一点不熟悉,可以自行使用[维基百科](https://zh.wikipedia.org/wiki/%E4%BA%8C%E8%BF%9B%E5%88%B6)或者[百度百科](https://baike.baidu.com)科普一下。 + +> **说明**:近期关于**量子计算机**的研究已经被推倒了风口浪尖,量子计算机基于量子力学进行运算,使用量子瞬移的方式来传递信息。2018年6月,Intel宣布开发出新款量子芯片并通过了在接近绝对零度环境下的测试;2019年,IBM和Google都推出了自己的量子计算机。 + +### 变量和类型 + +在程序设计中,变量是一种存储数据的载体。计算机中的变量是实际存在的数据或者说是存储器中存储数据的一块内存空间,变量的值可以被读取和修改,这是所有计算和控制的基础。计算机能处理的数据有很多种类型,除了数值之外还可以处理文本、图形、音频、视频等各种各样的数据,那么不同的数据就需要定义不同的存储类型。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: 骆昊 +""" +a = 321 +b = 12 +print(a + b) # 333 +print(a - b) # 309 +print(a * b) # 3852 +print(a / b) # 26.75 +``` + +在Python中可以使用`type`函数对变量的类型进行检查。程序设计中函数的概念跟数学上函数的概念是一致的,数学上的函数相信大家并不陌生,它包括了函数名、自变量和因变量。如果暂时不理解这个概念也不要紧,我们会在后续的章节中专门讲解函数的定义和使用。 + +```Python +""" +使用type()检查变量的类型 + +Version: 0.1 +Author: 骆昊 +""" +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 +""" +使用input()函数获取键盘输入(字符串) +使用int()函数将输入的字符串转换成整数 +使用print()函数输出带占位符的字符串 + +Version: 0.1 +Author: 骆昊 +""" +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)) +``` + +> **说明**:上面的print函数中输出的字符串使用了占位符语法,其中`%d`是整数的占位符,`%f`是小数的占位符,`%%`表示百分号(因为百分号代表了占位符,所以带占位符的字符串中要表示百分号必须写成`%%`),字符串之后的`%`后面跟的变量值会替换掉占位符然后输出到终端中,运行上面的程序,看看程序执行结果就明白啦。 + +### 运算符 + +Python支持多种运算符,下表大致按照优先级从高到低的顺序列出了所有的运算符,运算符的优先级指的是多个运算符同时出现时,先做什么运算然后再做什么运算。除了我们之前已经用过的赋值运算符和算术运算符,我们稍后会陆续讲到其他运算符的使用。 + +| 运算符 | 描述 | +| ------------------------------------------------------------ | ------------------------------ | +| `[]` `[:]` | 下标,切片 | +| `**` | 指数 | +| `~` `+` `-` | 按位取反, 正负号 | +| `*` `/` `%` `//` | 乘,除,模,整除 | +| `+` `-` | 加,减 | +| `>>` `<<` | 右移,左移 | +| `&` | 按位与 | +| `^` `\|` | 按位异或,按位或 | +| `<=` `<` `>` `>=` | 小于等于,小于,大于,大于等于 | +| `==` `!=` | 等于,不等于 | +| `is` `is not` | 身份运算符 | +| `in` `not in` | 成员运算符 | +| `not` `or` `and` | 逻辑运算符 | +| `=` `+=` `-=` `*=` `/=` `%=` `//=` `**=` `&=` `|=` `^=` `>>=` `<<=` | (复合)赋值运算符 | + +>**说明:** 在实际开发中,如果搞不清楚运算符的优先级,可以使用括号来确保运算的执行顺序。 + +#### 赋值运算符 + +赋值运算符应该是最为常见的运算符,它的作用是将右边的值赋给左边的变量。下面的例子演示了赋值运算符和复合赋值运算符的使用。 + +```Python +""" +赋值运算符和复合赋值运算符 + +Version: 0.1 +Author: 骆昊 +""" +a = 10 +b = 3 +a += b # 相当于:a = a + b +a *= a + 2 # 相当于:a = a * (a + 2) +print(a) # 算一下这里会输出什么 +``` + +### 比较运算符和逻辑运算符 + +比较运算符有的地方也称为关系运算符,包括`==`、`!=`、`<`、`>`、`<=`、`>=`,我相信没有什么好解释的,大家一看就能懂,唯一需要提醒的是比较相等用的是`==`,请注意这个地方是两个等号,因为`=`是赋值运算符,我们在上面刚刚讲到过,`==`才是比较相等的比较运算符。比较运算符会产生布尔值,要么是`True`要么是`False`。 + +逻辑运算符有三个,分别是`and`、`or`和`not`。`and`字面意思是“而且”,所以`and`运算符会连接两个布尔值,如果两个布尔值都是`True`,那么运算的结果就是`True`;左右两边的布尔值有一个是`False`,最终的运算结果就是`False`。相信大家已经想到了,如果`and`左边的布尔值是`False`,不管右边的布尔值是什么,最终的结果都是`False`,所以在做运算的时候右边的值会被跳过(短路处理),这也就意味着在`and`运算符左边为`False`的情况下,右边的表达式根本不会执行。`or`字面意思是“或者”,所以`or`运算符也会连接两个布尔值,如果两个布尔值有任意一个是`True`,那么最终的结果就是`True`。当然,`or`运算符也是有短路功能的,在它左边的布尔值为`True`的情况下,右边的表达式根本不会执行。`not`运算符的后面会跟上一个布尔值,它的作用是得到与该布尔值相反的值,也就是说,后面的布尔值如果是`True`运算结果就是`False`,而后面的布尔值如果是`False`则运算结果就是`True`。 + +```Python +""" +比较运算符和逻辑运算符的使用 + +Version: 0.1 +Author: 骆昊 +""" +flag0 = 1 == 1 +flag1 = 3 > 2 +flag2 = 2 < 1 +flag3 = flag1 and flag2 +flag4 = flag1 or flag2 +flag5 = not (1 != 2) +print('flag0 =', flag0) # flag0 = True +print('flag1 =', flag1) # flag1 = True +print('flag2 =', flag2) # flag2 = False +print('flag3 =', flag3) # flag3 = False +print('flag4 =', flag4) # flag4 = True +print('flag5 =', flag5) # flag5 = False +``` + +> **说明**:比较运算符的优先级高于赋值运算符,所以`flag0 = 1 == 1`先做`1 == 1`产生布尔值`True`,再将这个值赋值给变量`flag0`。`print`函数可以输出多个值,多个值之间可以用`,`进行分隔,输出的内容之间默认以空格分开。 + +### 练习 + +#### 练习1:华氏温度转换为摄氏温度。 + +> 提示:华氏温度到摄氏温度的转换公式为:$C=(F - 32) \div 1.8$。 + +参考答案: + +```Python +""" +将华氏温度转换为摄氏温度 + +Version: 0.1 +Author: 骆昊 +""" +f = float(input('请输入华氏温度: ')) +c = (f - 32) / 1.8 +print('%.1f华氏度 = %.1f摄氏度' % (f, c)) +``` + +> **说明**:在使用`print`函数输出时,也可以对字符串内容进行格式化处理,上面`print`函数中的字符串`%.1f`是一个占位符,稍后会由一个`float`类型的变量值替换掉它。同理,如果字符串中有`%d`,后面可以用一个`int`类型的变量值替换掉它,而`%s`会被字符串的值替换掉。除了这种格式化字符串的方式外,还可以用下面的方式来格式化字符串,其中`{f:.1f}`和`{c:.1f}`可以先看成是`{f}`和`{c}`,表示输出时会用变量`f`和变量`c`的值替换掉这两个占位符,后面的`:.1f`表示这是一个浮点数,小数点后保留1位有效数字。 +> +> ```Python +> print(f'{f:.1f}华氏度 = {c:.1f}摄氏度') +> ``` + +#### 练习2:输入圆的半径计算计算周长和面积。 + +参考答案: + +```Python +""" +输入半径计算圆的周长和面积 + +Version: 0.1 +Author: 骆昊 +""" +radius = float(input('请输入圆的半径: ')) +perimeter = 2 * 3.1416 * radius +area = 3.1416 * radius * radius +print('周长: %.2f' % perimeter) +print('面积: %.2f' % area) +``` + +#### 练习3:输入年份判断是不是闰年。 + +参考答案: + +```Python +""" +输入年份 如果是闰年输出True 否则输出False + +Version: 0.1 +Author: 骆昊 +""" +year = int(input('请输入年份: ')) +# 如果代码太长写成一行不便于阅读 可以使用\对代码进行折行 +is_leap = year % 4 == 0 and year % 100 != 0 or \ + year % 400 == 0 +print(is_leap) +``` + +> **说明**:比较运算符会产生布尔值,而逻辑运算符`and`和`or`会对这些布尔值进行组合,最终也是得到一个布尔值,闰年输出`True`,平年输出`False`。 \ No newline at end of file diff --git "a/Day01-15/03.\345\210\206\346\224\257\347\273\223\346\236\204.md" "b/Day01-15/03.\345\210\206\346\224\257\347\273\223\346\236\204.md" new file mode 100755 index 000000000..abd08e79c --- /dev/null +++ "b/Day01-15/03.\345\210\206\346\224\257\347\273\223\346\236\204.md" @@ -0,0 +1,153 @@ +## 分支结构 + +### 应用场景 + +迄今为止,我们写的Python代码都是一条一条语句顺序执行,这种代码结构通常称之为顺序结构。然而仅有顺序结构并不能解决所有的问题,比如我们设计一个游戏,游戏第一关的通关条件是玩家获得1000分,那么在完成本局游戏后,我们要根据玩家得到分数来决定究竟是进入第二关,还是告诉玩家“Game Over”,这里就会产生两个分支,而且这两个分支只有一个会被执行。类似的场景还有很多,我们将这种结构称之为“分支结构”或“选择结构”。给大家一分钟的时间,你应该可以想到至少5个以上这样的例子,赶紧试一试。 + +### if语句的使用 + +在Python中,要构造分支结构可以使用`if`、`elif`和`else`关键字。所谓**关键字**就是有特殊含义的单词,像`if`和`else`就是专门用于构造分支结构的关键字,很显然你不能够使用它作为变量名(事实上,用作其他的标识符也是不可以)。下面的例子中演示了如何构造一个分支结构。 + +```Python +""" +用户身份验证 + +Version: 0.1 +Author: 骆昊 +""" +username = input('请输入用户名: ') +password = input('请输入口令: ') +# 用户名是admin且密码是123456则身份验证成功否则身份验证失败 +if username == 'admin' and password == '123456': + print('身份验证成功!') +else: + print('身份验证失败!') +``` + +需要说明的是和C/C++、Java等语言不同,Python中没有用花括号来构造代码块而是**使用了缩进的方式来表示代码的层次结构**,如果`if`条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了。换句话说**连续的代码如果又保持了相同的缩进那么它们属于同一个代码块**,相当于是一个执行的整体。**缩进**可以使用任意数量的空格,但**通常使用4个空格**,建议大家**不要使用制表键**或者**设置你的代码编辑工具自动将制表键变成4个空格**。 + +当然如果要构造出更多的分支,可以使用`if...elif...else...`结构或者嵌套的`if...else...`结构,下面的代码演示了如何利用多分支结构实现分段函数求值。 + +![$$f(x)=\begin{cases} 3x-5&\text{(x>1)}\\x+2&\text{(-1}\leq\text{x}\leq\text{1)}\\5x+3&\text {(x<-1)}\end{cases}$$](./res/formula_1.png) + +```Python +""" +分段函数求值 + + 3x - 5 (x > 1) +f(x) = x + 2 (-1 <= x <= 1) + 5x + 3 (x < -1) + +Version: 0.1 +Author: 骆昊 +""" + +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: 骆昊 +""" + +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 better than nested.”,之所以提倡代码“扁平化”是因为嵌套结构的嵌套层次多了之后会严重的影响代码的可读性,所以能使用扁平化的结构时就不要使用嵌套。 + +### 练习 + +#### 练习1:英制单位英寸与公制单位厘米互换。 + +参考答案: + +```Python +""" +英制单位英寸和公制单位厘米互换 + +Version: 0.1 +Author: 骆昊 +""" +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:百分制成绩转换为等级制成绩。 + +> **要求**:如果输入的成绩在90分以上(含90分)输出A;80分-90分(不含90分)输出B;70分-80分(不含80分)输出C;60分-70分(不含70分)输出D;60分以下输出E。 + +参考答案: + +```Python +""" +百分制成绩转换为等级制成绩 + +Version: 0.1 +Author: 骆昊 +""" +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) +``` +#### 练习3:输入三条边长,如果能构成三角形就计算周长和面积。 + +参考答案: + +```Python +""" +判断输入的边长能否构成三角形,如果能则计算出三角形的周长和面积 + +Version: 0.1 +Author: 骆昊 +""" +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 = (p * (p - a) * (p - b) * (p - c)) ** 0.5 + print('面积: %f' % (area)) +else: + print('不能构成三角形') +``` +> **说明:** 上面使用的通过边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。 + diff --git "a/Day01-15/04.\345\276\252\347\216\257\347\273\223\346\236\204.md" "b/Day01-15/04.\345\276\252\347\216\257\347\273\223\346\236\204.md" new file mode 100755 index 000000000..e13409ca5 --- /dev/null +++ "b/Day01-15/04.\345\276\252\347\216\257\347\273\223\346\236\204.md" @@ -0,0 +1,236 @@ +## 循环结构 + +### 应用场景 + +我们在写程序的时候,一定会遇到需要重复执行某条或某些指令的场景。例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向移动的指令。在这个场景中,让机器人向球门方向移动就是一个需要重复的动作,当然这里还会用到上一课讲的分支结构来判断机器人是否持球以及是否进入射门范围。再举一个简单的例子,如果要实现每隔1秒中在屏幕上打印一次“hello, world”并持续打印一个小时,我们肯定不能够直接把`print('hello, world')`这句代码写3600遍,这里同样需要循环结构。 + +循环结构就是程序中控制某条或某些指令重复执行的结构。在Python中构造循环结构有两种做法,一种是`for-in`循环,一种是`while`循环。 + +### for-in循环 + +如果明确的知道循环执行的次数或者要对一个容器进行迭代(后面会讲到),那么我们推荐使用`for-in`循环,例如下面代码中计算1~100求和的结果($\displaystyle \sum \limits_{n=1}^{100}n$)。 + +```Python +""" +用for循环实现1~100求和 + +Version: 0.1 +Author: 骆昊 +""" + +sum = 0 +for x in range(101): + sum += x +print(sum) +``` + +需要说明的是上面代码中的`range(1, 101)`可以用来构造一个从1到100的范围,当我们把这样一个范围放到`for-in`循环中,就可以通过前面的循环变量`x`依次取出从1到100的整数。当然,`range`的用法非常灵活,下面给出了一个例子: + +- `range(101)`:可以用来产生0到100范围的整数,需要注意的是取不到101。 +- `range(1, 101)`:可以用来产生1到100范围的整数,相当于前面是闭区间后面是开区间。 +- `range(1, 101, 2)`:可以用来产生1到100的奇数,其中2是步长,即每次数值递增的值。 +- `range(100, 0, -2)`:可以用来产生100到1的偶数,其中-2是步长,即每次数字递减的值。 + +知道了这一点,我们可以用下面的代码来实现1~100之间的偶数求和。 + +```Python +""" +用for循环实现1~100之间的偶数求和 + +Version: 0.1 +Author: 骆昊 +""" + +sum = 0 +for x in range(2, 101, 2): + sum += x +print(sum) +``` + +当然,也可以通过在循环中使用分支结构的方式来实现相同的功能,代码如下所示。 + +```Python +""" +用for循环实现1~100之间的偶数求和 + +Version: 0.1 +Author: 骆昊 +""" + +sum = 0 +for x in range(1, 101): + if x % 2 == 0: + sum += x +print(sum) +``` + +> **说明**:相较于上面直接跳过奇数的做法,下面这种做法很明显并不是很好的选择。 + +### while循环 + +如果要构造不知道具体循环次数的循环结构,我们推荐使用`while`循环。`while`循环通过一个能够产生或转换出`bool`值的表达式来控制循环,表达式的值为`True`则继续循环;表达式的值为`False`则结束循环。 + +下面我们通过一个“猜数字”的小游戏来看看如何使用`while`循环。猜数字游戏的规则是:计算机出一个1到100之间的随机数,玩家输入自己猜的数字,计算机给出对应的提示信息(大一点、小一点或猜对了),如果玩家猜中了数字,计算机提示用户一共猜了多少次,游戏结束,否则游戏继续。 + +```Python +""" +猜数字游戏 + +Version: 0.1 +Author: 骆昊 +""" +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: 骆昊 +""" + +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:输入一个正整数判断是不是素数。 + +> **提示**:素数指的是只能被1和自身整除的大于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 = ')) +# 如果x大于y就交换x和y的值 +if x > y: + # 通过下面的操作将y的值赋给x, 将x的值赋给y + x, y = y, x +# 从两个数中较小的数开始做递减的循环 +for factor in range(x, 0, -1): + if x % factor == 0 and y % factor == 0: + print('%d和%d的最大公约数是%d' % (x, y, factor)) + print('%d和%d的最小公倍数是%d' % (x, y, x * y // factor)) + break +``` + +#### 练习3:打印如下所示的三角形图案。 + +``` +* +** +*** +**** +***** +``` + +``` + * + ** + *** + **** +***** +``` + +``` + * + *** + ***** + ******* +********* +``` + +参考答案: + +```Python +""" +打印三角形图案 + +Version: 0.1 +Author: 骆昊 +""" + +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/Day01-15/05.\346\236\204\351\200\240\347\250\213\345\272\217\351\200\273\350\276\221.md" "b/Day01-15/05.\346\236\204\351\200\240\347\250\213\345\272\217\351\200\273\350\276\221.md" new file mode 100755 index 000000000..075583c11 --- /dev/null +++ "b/Day01-15/05.\346\236\204\351\200\240\347\250\213\345\272\217\351\200\273\350\276\221.md" @@ -0,0 +1,130 @@ +## 构造程序逻辑 + +学完前面的几个章节后,我觉得有必要在这里带大家做一些练习来巩固之前所学的知识,虽然迄今为止我们学习的内容只是Python的冰山一角,但是这些内容已经足够我们来构建程序中的逻辑。对于编程语言的初学者来说,在学习了Python的核心语言元素(变量、类型、运算符、表达式、分支结构、循环结构等)之后,必须做的一件事情就是尝试用所学知识去解决现实中的问题,换句话说就是锻炼自己把用人类自然语言描述的算法(解决问题的方法和步骤)翻译成Python代码的能力,而这件事情必须通过大量的练习才能达成。 + +我们在本章为大家整理了一些经典的案例和习题,希望通过这些例子,一方面帮助大家巩固之前所学的Python知识,另一方面帮助大家了解如何建立程序中的逻辑以及如何运用一些简单的算法解决现实中的问题。 + +### 经典的例子 + +1. 寻找**水仙花数**。 + + > **说明**:水仙花数也被称为超完全数字不变数、自恋数、自幂数、阿姆斯特朗数,它是一个3位数,该数字每个位上数字的立方之和正好等于它本身,例如:$1^3 + 5^3+ 3^3=153$。 + + ```Python + """ + 找出所有水仙花数 + + Version: 0.1 + Author: 骆昊 + """ + + for num in range(100, 1000): + low = num % 10 + mid = num // 10 % 10 + high = num // 100 + if num == low ** 3 + mid ** 3 + high ** 3: + print(num) + ``` + + 在上面的代码中,我们通过整除和求模运算分别找出了一个三位数的个位、十位和百位,这种小技巧在实际开发中还是常用的。用类似的方法,我们还可以实现将一个正整数反转,例如:将12345变成54321,代码如下所示。 + + ```Python + """ + 正整数的反转 + + Version: 0.1 + Author: 骆昊 + """ + + num = int(input('num = ')) + reversed_num = 0 + while num > 0: + reversed_num = reversed_num * 10 + num % 10 + num //= 10 + print(reversed_num) + ``` + +2. **百钱百鸡**问题。 + + > **说明**:百钱百鸡是我国古代数学家[张丘建](https://baike.baidu.com/item/%E5%BC%A0%E4%B8%98%E5%BB%BA/10246238)在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?翻译成现代文是:公鸡5元一只,母鸡3元一只,小鸡1元三只,用100块钱买一百只鸡,问公鸡、母鸡、小鸡各有多少只? + + ```Python + """ + 《百钱百鸡》问题 + + Version: 0.1 + Author: 骆昊 + """ + + for x in range(0, 20): + for y in range(0, 33): + z = 100 - x - y + if 5 * x + 3 * y + z / 3 == 100: + print('公鸡: %d只, 母鸡: %d只, 小鸡: %d只' % (x, y, z)) + ``` + + 上面使用的方法叫做**穷举法**,也称为**暴力搜索法**,这种方法通过一项一项的列举备选解决方案中所有可能的候选项并检查每个候选项是否符合问题的描述,最终得到问题的解。这种方法看起来比较笨拙,但对于运算能力非常强大的计算机来说,通常都是一个可行的甚至是不错的选择,而且问题的解如果存在,这种方法一定能够找到它。 + +3. **CRAPS赌博游戏**。 + + > **说明**:CRAPS又称花旗骰,是美国拉斯维加斯非常受欢迎的一种的桌上赌博游戏。该游戏使用两粒骰子,玩家通过摇两粒骰子获得点数进行游戏。简单的规则是:玩家第一次摇骰子如果摇出了7点或11点,玩家胜;玩家第一次如果摇出2点、3点或12点,庄家胜;其他点数玩家继续摇骰子,如果玩家摇出了7点,庄家胜;如果玩家摇出了第一次摇的点数,玩家胜;其他点数,玩家继续要骰子,直到分出胜负。 + + ```Python + """ + Craps赌博游戏 + 我们设定玩家开始游戏时有1000元的赌注 + 游戏结束的条件是玩家输光所有的赌注 + + Version: 0.1 + Author: 骆昊 + """ + from random import randint + + money = 1000 + while money > 0: + print('你的总资产为:', money) + needs_go_on = False + while True: + debt = int(input('请下注: ')) + if 0 < debt <= money: + break + first = randint(1, 6) + randint(1, 6) + print('玩家摇出了%d点' % first) + if first == 7 or first == 11: + print('玩家胜!') + money += debt + elif first == 2 or first == 3 or first == 12: + print('庄家胜!') + money -= debt + else: + needs_go_on = True + while needs_go_on: + needs_go_on = False + current = randint(1, 6) + randint(1, 6) + print('玩家摇出了%d点' % current) + if current == 7: + print('庄家胜') + money -= debt + elif current == first: + print('玩家胜') + money += debt + else: + needs_go_on = True + print('你破产了, 游戏结束!') + ``` + +###有用的练习 + +1. 生成**斐波那契数列**的前20个数。 + + > **说明**:斐波那契数列(Fibonacci sequence),又称黄金分割数列,是意大利数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)在《计算之书》中提出一个在理想假设条件下兔子成长率的问题而引入的数列,所以这个数列也被戏称为"兔子数列"。斐波那契数列的特点是数列的前两个数都是1,从第三个数开始,每个数都是它前面两个数的和,形如:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...。斐波那契数列在现代物理、准晶体结构、化学等领域都有直接的应用。 + +2. 找出10000以内的**完美数**。 + + > **说明**:完美数又称为完全数或完备数,它的所有的真因子(即除了自身以外的因子)的和(即因子函数)恰好等于它本身。例如:6($6=1+2+3$)和28($28=1+2+4+7+14$)就是完美数。完美数有很多神奇的特性,有兴趣的可以自行了解。 + +3. 输出**100以内所有的素数**。 + + > **说明**:素数指的是只能被1和自身整除的正整数(不包括1)。 + +上面练习的参考答案在本章对应的代码目录中,如果需要帮助请读者自行查看参考答案。 \ No newline at end of file diff --git "a/Day01-15/06.\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/Day01-15/06.\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" new file mode 100755 index 000000000..a033cfea6 --- /dev/null +++ "b/Day01-15/06.\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" @@ -0,0 +1,366 @@ +## 函数和模块的使用 + +在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解。 + +![$$x_1 + x_2 + x_3 + x_4 = 8$$](./res/formula_3.png) + +事实上,上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案。想到这一点问题的答案就呼之欲出了。 + +![$$C_M^N =\frac{M!}{N!(M-N)!}, \text{(M=7, N=3)} $$](./res/formula_4.png) + +可以用Python的程序来计算出这个值,代码如下所示。 + +```Python +""" +输入M和N计算C(M,N) + +Version: 0.1 +Author: 骆昊 +""" +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 +fm_n = 1 +for num in range(1, m - n + 1): + fm_n *= num +print(fm // fn // fm_n) +``` + +### 函数的作用 + +不知道大家是否注意到,在上面的代码中,我们做了3次求阶乘,这样的代码实际上就是重复代码。编程大师*Martin Fowler*先生曾经说过:“**代码有很多种坏味道,重复是最坏的一种!**”,要写出高质量的代码首先要解决的就是重复代码的问题。对于上面的代码来说,我们可以将计算阶乘的功能封装到一个称之为“函数”的功能模块中,在需要计算阶乘的地方,我们只需要“调用”这个“函数”就可以了。 + +### 定义函数 + +在Python中可以使用`def`关键字来定义函数,和变量一样每个函数也有一个响亮的名字,而且命名规则跟变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给函数的参数,这一点和数学上的函数非常相似,程序中函数的参数就相当于是数学上说的函数的自变量,而函数执行完成后我们可以通过`return`关键字来返回一个值,这相当于数学上说的函数的因变量。 + +在了解了如何定义函数后,我们可以对上面的代码进行重构,所谓重构就是在不影响代码执行结果的前提下对代码的结构进行调整,重构之后的代码如下所示。 + +```Python +""" +输入M和N计算C(M,N) + +Version: 0.1 +Author: 骆昊 +""" +def fac(num): + """求阶乘""" + result = 1 + for n in range(1, num + 1): + result *= n + return result + + +m = int(input('m = ')) +n = int(input('n = ')) +# 当需要计算阶乘的时候不用再写循环求阶乘而是直接调用已经定义好的函数 +print(fac(m) // fac(n) // fac(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): + """摇色子""" + 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是一个可变参数 +def add(*args): + total = 0 + for val in args: + total += val + return total + + +# 在调用add函数时可以传入0个或多个参数 +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() +``` + +当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`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 + +# 输出hello, world! +foo() + +from module2 import foo + +# 输出goodbye, world! +foo() +``` + +也可以按照如下所示的方式来区分到底要使用哪一个`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 + +# 输出goodbye, world! +foo() +``` + +`test.py` + +```Python +from module2 import foo +from module1 import foo + +# 输出hello, world! +foo() +``` + +需要说明的是,如果我们导入的模块除了定义函数之外还有可以执行代码,那么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, int(num ** 0.5) + 1): + 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' + + # Python中可以在函数内部再定义函数 + def bar(): + 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和True,但我们注意到了,在`bar`函数的内部并没有定义`a`和`b`两个变量,那么`a`和`b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些标识符,我们之前用过的`input`、`print`、`int`等都属于内置作用域。 + +再看看下面这段代码,我们希望通过函数调用修改全局变量`a`的值,但实际上下面的代码是做不到的。 + +```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/Day01-15/07.\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/Day01-15/07.\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" new file mode 100755 index 000000000..f0e257546 --- /dev/null +++ "b/Day01-15/07.\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" @@ -0,0 +1,693 @@ +## 字符串和常用数据结构 + +### 使用字符串 + +第二次世界大战促使了现代电子计算机的诞生,最初计算机被应用于导弹弹道的计算,而在计算机诞生后的很多年时间里,计算机处理的信息基本上都是数值型的信息。世界上的第一台电子计算机叫ENIAC(电子数值积分计算机),诞生于美国的宾夕法尼亚大学,每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机处理得更多的数据可能都是以文本的方式存在的,如果我们希望通过Python程序操作这些文本信息,就必须要先了解字符串类型以及与它相关的知识。 + +所谓**字符串**,就是由零个或多个字符组成的有限序列,一般记为![$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](./res/formula_5.png)。在Python程序中,如果我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。 + +```Python +s1 = 'hello, world!' +s2 = "hello, world!" +# 以三个双引号或单引号开头的字符串可以折行 +s3 = """ +hello, +world! +""" +print(s1, s2, s3, end='') +``` + +可以在字符串中使用`\`(反斜杠)来表示转义,也就是说`\`后面的字符不再是它原来的意义,例如:`\n`不是代表反斜杠和字符n,而是表示换行;而`\t`也不是代表反斜杠和字符t,而是表示制表符。所以如果想在字符串中表示`'`要写成`\'`,同理想表示`\`要写成`\\`。可以运行下面的代码看看会输出什么。 + +```Python +s1 = '\'hello, world!\'' +s2 = '\n\\hello, world!\\\n' +print(s1, s2, end='') +``` + +在`\`后面还可以跟一个八进制或者十六进制数来表示字符,例如`\141`和`\x61`都代表小写字母`a`,前者是八进制的表示法,后者是十六进制的表示法。也可以在`\`后面跟Unicode字符编码来表示字符,例如`\u9a86\u660a`代表的是中文“骆昊”。运行下面的代码,看看输出了什么。 + +```Python +s1 = '\141\142\143\x61\x62\x63' +s2 = '\u9a86\u660a' +print(s1, s2) +``` + +如果不希望字符串中的`\`表示转义,我们可以通过在字符串的最前面加上字母`r`来加以说明,再看看下面的代码又会输出什么。 + +```Python +s1 = r'\'hello, world!\'' +s2 = r'\n\\hello, world!\\\n' +print(s1, s2, end='') +``` + +Python为字符串类型提供了非常丰富的运算符,我们可以使用`+`运算符来实现字符串的拼接,可以使用`*`运算符来重复一个字符串的内容,可以使用`in`和`not in`来判断一个字符串是否包含另外一个字符串(成员运算),我们也可以用`[]`和`[:]`运算符从字符串取出某个字符或某些字符(切片运算),代码如下所示。 + +```Python +s1 = 'hello ' * 3 +print(s1) # hello hello hello +s2 = 'world' +s1 += s2 +print(s1) # hello hello hello world +print('ll' in s1) # True +print('good' in s1) # False +str2 = 'abc123456' +# 从字符串中取出指定位置的字符(下标运算) +print(str2[2]) # c +# 字符串切片(从指定的开始索引到指定的结束索引) +print(str2[2:5]) # c12 +print(str2[2:]) # c123456 +print(str2[2::2]) # c246 +print(str2[::2]) # ac246 +print(str2[::-1]) # 654321cba +print(str2[-3:-1]) # 45 +``` + +在Python中,我们还可以通过一系列的方法来完成对字符串的处理,代码如下所示。 + +```Python +str1 = 'hello, world!' +# 通过内置函数len计算字符串的长度 +print(len(str1)) # 13 +# 获得字符串首字母大写的拷贝 +print(str1.capitalize()) # Hello, world! +# 获得字符串每个单词首字母大写的拷贝 +print(str1.title()) # Hello, World! +# 获得字符串变大写后的拷贝 +print(str1.upper()) # HELLO, WORLD! +# 从字符串中查找子串所在位置 +print(str1.find('or')) # 8 +print(str1.find('shit')) # -1 +# 与find类似但找不到子串时会引发异常 +# print(str1.index('or')) +# print(str1.index('shit')) +# 检查字符串是否以指定的字符串开头 +print(str1.startswith('He')) # False +print(str1.startswith('hel')) # True +# 检查字符串是否以指定的字符串结尾 +print(str1.endswith('!')) # True +# 将字符串以指定的宽度居中并在两侧填充指定的字符 +print(str1.center(50, '*')) +# 将字符串以指定的宽度靠右放置左侧填充指定的字符 +print(str1.rjust(50, ' ')) +str2 = 'abc123456' +# 检查字符串是否由数字构成 +print(str2.isdigit()) # False +# 检查字符串是否以字母构成 +print(str2.isalpha()) # False +# 检查字符串是否以数字和字母构成 +print(str2.isalnum()) # True +str3 = ' jackfrued@126.com ' +print(str3) +# 获得字符串修剪左右两侧空格之后的拷贝 +print(str3.strip()) +``` + +我们之前讲过,可以用下面的方式来格式化输出字符串。 + +```Python +a, b = 5, 10 +print('%d * %d = %d' % (a, b, a * b)) +``` + +当然,我们也可以用字符串提供的方法来完成字符串的格式,代码如下所示。 + +```Python +a, b = 5, 10 +print('{0} * {1} = {2}'.format(a, b, a * b)) +``` + +Python 3.6以后,格式化字符串还有更为简洁的书写方式,就是在字符串前加上字母`f`,我们可以使用下面的语法糖来简化上面的代码。 + +```Python +a, b = 5, 10 +print(f'{a} * {b} = {a * b}') +``` + +除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典。 + +### 使用列表 + +不知道大家是否注意到,刚才我们讲到的字符串类型(`str`)和之前我们讲到的数值类型(`int`和`float`)有一些区别。数值类型是标量类型,也就是说这种类型的对象没有可以访问的内部结构;而字符串类型是一种结构化的、非标量类型,所以才会有一系列的属性和方法。接下来我们要介绍的列表(`list`),也是一种结构化的、非标量类型,它是值的有序序列,每个值都可以通过索引进行标识,定义列表可以将列表的元素放在`[]`中,多个元素用`,`进行分隔,可以使用`for`循环对列表元素进行遍历,也可以使用`[]`或`[:]`运算符取出列表中的一个或多个元素。 + +下面的代码演示了如何定义列表、如何遍历列表以及列表的下标运算。 + +```Python +list1 = [1, 3, 5, 7, 100] +print(list1) # [1, 3, 5, 7, 100] +# 乘号表示列表元素的重复 +list2 = ['hello'] * 3 +print(list2) # ['hello', 'hello', 'hello'] +# 计算列表长度(元素个数) +print(len(list1)) # 5 +# 下标(索引)运算 +print(list1[0]) # 1 +print(list1[4]) # 100 +# print(list1[5]) # IndexError: list index out of range +print(list1[-1]) # 100 +print(list1[-3]) # 5 +list1[2] = 300 +print(list1) # [1, 3, 300, 7, 100] +# 通过循环用下标遍历列表元素 +for index in range(len(list1)): + print(list1[index]) +# 通过for循环遍历列表元素 +for elem in list1: + print(elem) +# 通过enumerate函数处理列表之后再遍历可以同时获得元素索引和值 +for index, elem in enumerate(list1): + print(index, elem) +``` + +下面的代码演示了如何向列表中添加元素以及如何从列表中移除元素。 + +```Python +list1 = [1, 3, 5, 7, 100] +# 添加元素 +list1.append(200) +list1.insert(1, 400) +# 合并两个列表 +# list1.extend([1000, 2000]) +list1 += [1000, 2000] +print(list1) # [1, 400, 3, 5, 7, 100, 200, 1000, 2000] +print(len(list1)) # 9 +# 先通过成员运算判断元素是否在列表中,如果存在就删除该元素 +if 3 in list1: + list1.remove(3) +if 1234 in list1: + list1.remove(1234) +print(list1) # [1, 400, 5, 7, 100, 200, 1000, 2000] +# 从指定的位置删除元素 +list1.pop(0) +list1.pop(len(list1) - 1) +print(list1) # [400, 5, 7, 100, 200, 1000] +# 清空列表元素 +list1.clear() +print(list1) # [] +``` + +和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。 + +```Python +fruits = ['grape', 'apple', 'strawberry', 'waxberry'] +fruits += ['pitaya', 'pear', 'mango'] +# 列表切片 +fruits2 = fruits[1:4] +print(fruits2) # apple strawberry waxberry +# 可以通过完整切片操作来复制列表 +fruits3 = fruits[:] +print(fruits3) # ['grape', 'apple', 'strawberry', 'waxberry', 'pitaya', 'pear', 'mango'] +fruits4 = fruits[-3:-1] +print(fruits4) # ['pitaya', 'pear'] +# 可以通过反向切片操作来获得倒转后的列表的拷贝 +fruits5 = fruits[::-1] +print(fruits5) # ['mango', 'pear', 'pitaya', 'waxberry', 'strawberry', 'apple', 'grape'] +``` + +下面的代码实现了对列表的排序操作。 + +```Python +list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry'] +list2 = sorted(list1) +# sorted函数返回列表排序后的拷贝不会修改传入的列表 +# 函数的设计就应该像sorted函数一样尽可能不产生副作用 +list3 = sorted(list1, reverse=True) +# 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序 +list4 = sorted(list1, key=len) +print(list1) +print(list2) +print(list3) +print(list4) +# 给列表对象发出排序消息直接在列表对象上进行排序 +list1.sort(reverse=True) +print(list1) +``` + +### 生成式和生成器 + +我们还可以使用列表的生成式语法来创建列表,代码如下所示。 + +```Python +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) +``` + +除了上面提到的生成器语法,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}$$](./res/formula_6.png) + +![$${\displaystyle F_{1}=1}$$](./res/formula_7.png) + +![$${\displaystyle F_{n}=F_{n-1}+F_{n-2}}({n}\geq{2})$$](./res/formula_8.png) + +![](./res/fibonacci-blocks.png) + +```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 +# 定义元组 +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) +``` + +这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢? + +1. 元组中的元素是无法修改的,事实上我们在项目中尤其是[多线程](https://zh.wikipedia.org/zh-hans/%E5%A4%9A%E7%BA%BF%E7%A8%8B)环境(后面会讲到)中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。 +2. 元组在创建时间和占用的空间上面都优于列表。我们可以使用sys模块的getsizeof函数来检查存储同样的元素的元组和列表各自占用了多少内存空间,这个很容易做到。我们也可以在ipython中使用魔法指令%timeit来分析创建同样内容的元组和列表所花费的时间,下图是我的macOS系统上测试的结果。 + +![](./res/ipython-timeit.png) + +### 使用集合 + +Python中的集合跟数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集等运算。 + +![](./res/python-set.png) + +可以按照下面代码所示的方式来创建和使用集合。 + +```Python +# 创建集合的字面量语法 +set1 = {1, 2, 3, 3, 3, 2} +print(set1) +print('Length =', len(set1)) +# 创建集合的构造器语法(面向对象部分会进行详细讲解) +set2 = set(range(1, 10)) +set3 = set((1, 2, 3, 3, 2, 1)) +print(set2, set3) +# 创建集合的推导式语法(推导式也可以用于推导集合) +set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0} +print(set4) +``` + +向集合添加元素和从集合删除元素。 + +```Python +set1.add(4) +set1.add(5) +set2.update([11, 12]) +set2.discard(5) +if 4 in set2: + set2.remove(4) +print(set1, set2) +print(set3.pop()) +print(set3) +``` + +集合的成员、交集、并集、差集等运算。 + +```Python +# 集合的交集、并集、差集、对称差运算 +print(set1 & set2) +# print(set1.intersection(set2)) +print(set1 | set2) +# print(set1.union(set2)) +print(set1 - set2) +# print(set1.difference(set2)) +print(set1 ^ set2) +# print(set1.symmetric_difference(set2)) +# 判断子集和超集 +print(set2 <= set1) +# print(set2.issubset(set1)) +print(set3 <= set1) +# print(set3.issubset(set1)) +print(set1 >= set2) +# print(set1.issuperset(set2)) +print(set1 >= set3) +# print(set1.issuperset(set3)) +``` + +> **说明:** Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符(后面的章节中会讲到),上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如`&`运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。 + +### 使用字典 + +字典是另一种可变容器模型,Python中的字典跟我们生活中使用的字典是一样一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。 + +```Python +# 创建字典的字面量语法 +scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82} +print(scores) +# 创建字典的构造器语法 +items1 = dict(one=1, two=2, three=3, four=4) +# 通过zip函数将两个序列压成字典 +items2 = dict(zip(['a', 'b', 'c'], '123')) +# 创建字典的推导式语法 +items3 = {num: num ** 2 for num in range(1, 10)} +print(items1, items2, items3) +# 通过键可以获取字典中对应的值 +print(scores['骆昊']) +print(scores['狄仁杰']) +# 对字典中所有键值对进行遍历 +for key in scores: + print(f'{key}: {scores[key]}') +# 更新字典中的元素 +scores['白元芳'] = 65 +scores['诸葛王朗'] = 71 +scores.update(冷面=67, 方启鹤=85) +print(scores) +if '武则天' in scores: + print(scores['武则天']) +print(scores.get('武则天')) +# get方法也是通过键获取对应的值但是可以设置默认值 +print(scores.get('武则天', 60)) +# 删除字典中的元素 +print(scores.popitem()) +print(scores.popitem()) +print(scores.pop('骆昊', 100)) +# 清空字典 +scores.clear() +print(scores) +``` + +### 练习 + +#### 练习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 = [] + 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/Day01-15/08.\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/Day01-15/08.\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" new file mode 100755 index 000000000..56a0fff6a --- /dev/null +++ "b/Day01-15/08.\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" @@ -0,0 +1,247 @@ +## 面向对象编程基础 + +活在当下的程序员应该都听过"面向对象编程"一词,也经常有人问能不能用一句话解释下什么是"面向对象编程",我们先来看看比较正式的说法。 + +"把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。" + +这样一说是不是更不明白了。所以我们还是看看更通俗易懂的说法,下面这段内容来自于[知乎](https://www.zhihu.com/)。 + +![](./res/oop-zhihu.png) + +> **说明:** 以上的内容来自于网络,不代表作者本人的观点和看法,与作者本人立场无关,相关责任不由作者承担。 + +之前我们说过"**程序是指令的集合**",我们在程序中书写的语句在执行时会变成一条或多条指令然后由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也不例外。 + +### 类和对象 + +简单的说,类是对象的蓝图和模板,而对象是类的实例。这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。 + +![](./res/object-feature.png) + +### 定义类 + +在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_movie(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_movie() + stu2 = Student('王大锤', 15) + stu2.study('思想品德') + stu2.watch_movie() + + +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 +from time import sleep + + +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 show(self): + """显示时间""" + return '%02d:%02d:%02d' % \ + (self._hour, self._minute, self._second) + + +def main(): + clock = Clock(23, 59, 58) + while True: + print(clock.show()) + 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/Day01-15/09.\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" "b/Day01-15/09.\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" new file mode 100755 index 000000000..bbf7006f0 --- /dev/null +++ "b/Day01-15/09.\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" @@ -0,0 +1,744 @@ +## 面向对象进阶 + +在前面的章节我们已经了解了面向对象的入门知识,知道了如何定义类,如何创建对象以及如何给对象发消息。为了能够更好的使用面向对象编程思想进行程序开发,我们还需要对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)一书。 + +![](./res/uml-components.png) + +![](./res/uml-example.png) + +利用类之间的这些关系,我们可以在已有类的基础上来完成某些操作,也可以在已有类的基础上创建新的类,这些都是实现代码复用的重要手段。复用现有的代码不仅可以减少开发的工作量,也有利于代码的管理和维护,这是我们在日常工作中都会使用到的技术手段。 + +### 继承和多态 + +刚才我们提到了,可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为[里氏替换原则](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 +import random + + +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): + 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' % (self._suite, face_str) + + def __repr__(self): + return self.__str__() + + +class Poker(object): + """一副牌""" + + def __init__(self): + self._cards = [Card(suite, face) + for suite in '♠♥♣♦' + for face in range(1, 14)] + self._current = 0 + + @property + def cards(self): + return self._cards + + def shuffle(self): + """洗牌(随机乱序)""" + self._current = 0 + random.shuffle(self._cards) + + @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) + print(player.cards_on_hand) + + +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/Day01-15/10.\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" "b/Day01-15/10.\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" new file mode 100755 index 000000000..ae6562452 --- /dev/null +++ "b/Day01-15/10.\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" @@ -0,0 +1,329 @@ +## 图形用户界面和游戏开发 + +### 基于tkinter模块的GUI + +GUI是图形用户界面的缩写,图形化的用户界面对使用过计算机的人来说应该都不陌生,在此也无需进行赘述。Python默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter),从这个名字就可以看出它是基于Tk的,Tk是一个工具包,最初是为Tcl设计的,后来被移植到很多其他的脚本语言中,它提供了跨平台的GUI控件。当然Tk并不是最新和最好的选择,也没有功能特别强大的GUI控件,事实上,开发GUI应用并不是Python最擅长的工作,如果真的需要使用Python开发GUI应用,wxPython、PyQt、PyGTK等模块都是不错的选择。 + +基本上使用tkinter来开发GUI应用需要以下5个步骤: + +1. 导入tkinter模块中我们需要的东西。 +2. 创建一个顶层窗口对象并用它来承载整个GUI应用。 +3. 在顶层窗口对象上添加GUI组件。 +4. 通过代码将这些GUI组件的功能组织起来。 +5. 进入主事件循环(main loop)。 + +下面的代码演示了如何使用tkinter做一个简单的GUI应用。 + +```Python +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) + # 创建按钮对象 指定添加到哪个容器中 通过command参数绑定事件回调函数 + 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() +``` + +需要说明的是,GUI应用通常是事件驱动式的,之所以要进入主事件循环就是要监听鼠标、键盘等各种事件的发生并执行对应的代码对事件进行处理,因为事件会持续的发生,所以需要这样的一个循环一直运行着等待下一个事件的发生。另一方面,Tk为控件的摆放提供了三种布局管理器,通过布局管理器可以对控件进行定位,这三种布局管理器分别是:Placer(开发者提供控件的大小和摆放位置)、Packer(自动将控件填充到合适的位置)和Grid(基于网格坐标来摆放控件),此处不进行赘述。 + +### 使用Pygame进行游戏开发 + +Pygame是一个开源的Python模块,专门用于多媒体应用(如电子游戏)的开发,其中包含对图像、声音、视频、事件、碰撞等的支持。Pygame建立在[SDL](https://zh.wikipedia.org/wiki/SDL)的基础上,SDL是一套跨平台的多媒体开发库,用C语言实现,被广泛的应用于游戏、模拟器、播放器等的开发。而Pygame让游戏开发者不再被底层语言束缚,可以更多的关注游戏的功能和逻辑。 + +下面我们来完成一个简单的小游戏,游戏的名字叫“大球吃小球”,当然完成这个游戏并不是重点,学会使用Pygame也不是重点,最重要的我们要在这个过程中体会如何使用前面讲解的面向对象程序设计,学会用这种编程思想去解决现实中的问题。 + +#### 制作游戏窗口 + +```Python +import pygame + + +def main(): + # 初始化导入的pygame中的模块 + pygame.init() + # 初始化用于显示的窗口并设置窗口尺寸 + screen = pygame.display.set_mode((800, 600)) + # 设置当前窗口的标题 + pygame.display.set_caption('大球吃小球') + running = True + # 开启一个事件循环处理发生的事件 + while running: + # 从消息队列中获取事件并对事件进行处理 + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + +if __name__ == '__main__': + main() +``` + +#### 在窗口中绘图 + +可以通过pygame中draw模块的函数在窗口上绘图,可以绘制的图形包括:线条、矩形、多边形、圆、椭圆、圆弧等。需要说明的是,屏幕坐标系是将屏幕左上角设置为坐标原点`(0, 0)`,向右是x轴的正向,向下是y轴的正向,在表示位置或者设置尺寸的时候,我们默认的单位都是[像素](https://zh.wikipedia.org/wiki/%E5%83%8F%E7%B4%A0)。所谓像素就是屏幕上的一个点,你可以用浏览图片的软件试着将一张图片放大若干倍,就可以看到这些点。pygame中表示颜色用的是色光[三原色](https://zh.wikipedia.org/wiki/%E5%8E%9F%E8%89%B2)表示法,即通过一个元组或列表来指定颜色的RGB值,每个值都在0~255之间,因为是每种原色都用一个8位(bit)的值来表示,三种颜色相当于一共由24位构成,这也就是常说的“24位颜色表示法”。 + +```Python +import pygame + + +def main(): + # 初始化导入的pygame中的模块 + pygame.init() + # 初始化用于显示的窗口并设置窗口尺寸 + screen = pygame.display.set_mode((800, 600)) + # 设置当前窗口的标题 + pygame.display.set_caption('大球吃小球') + # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组) + screen.fill((242, 242, 242)) + # 绘制一个圆(参数分别是: 屏幕, 颜色, 圆心位置, 半径, 0表示填充圆) + pygame.draw.circle(screen, (255, 0, 0,), (100, 100), 30, 0) + # 刷新当前窗口(渲染窗口将绘制的图像呈现出来) + pygame.display.flip() + running = True + # 开启一个事件循环处理发生的事件 + while running: + # 从消息队列中获取事件并对事件进行处理 + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + +if __name__ == '__main__': + main() +``` + +#### 加载图像 + +如果需要直接加载图像到窗口上,可以使用pygame中image模块的函数来加载图像,再通过之前获得的窗口对象的`blit`方法渲染图像,代码如下所示。 + +```Python +import pygame + + +def main(): + # 初始化导入的pygame中的模块 + pygame.init() + # 初始化用于显示的窗口并设置窗口尺寸 + screen = pygame.display.set_mode((800, 600)) + # 设置当前窗口的标题 + pygame.display.set_caption('大球吃小球') + # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组) + screen.fill((255, 255, 255)) + # 通过指定的文件名加载图像 + ball_image = pygame.image.load('./res/ball.png') + # 在窗口上渲染图像 + screen.blit(ball_image, (50, 50)) + # 刷新当前窗口(渲染窗口将绘制的图像呈现出来) + pygame.display.flip() + running = True + # 开启一个事件循环处理发生的事件 + while running: + # 从消息队列中获取事件并对事件进行处理 + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + +if __name__ == '__main__': + main() +``` + +#### 实现动画效果 + +说到[动画](https://zh.wikipedia.org/wiki/%E5%8A%A8%E7%94%BB)这个词大家都不会陌生,事实上要实现动画效果,本身的原理也非常简单,就是将不连续的图片连续的播放,只要每秒钟达到了一定的帧数,那么就可以做出比较流畅的动画效果。如果要让上面代码中的小球动起来,可以将小球的位置用变量来表示,并在循环中修改小球的位置再刷新整个窗口即可。 + +```Python +import pygame + + +def main(): + # 初始化导入的pygame中的模块 + pygame.init() + # 初始化用于显示的窗口并设置窗口尺寸 + screen = pygame.display.set_mode((800, 600)) + # 设置当前窗口的标题 + 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 + screen.fill((255, 255, 255)) + pygame.draw.circle(screen, (255, 0, 0,), (x, y), 30, 0) + pygame.display.flip() + # 每隔50毫秒就改变小球的位置再刷新窗口 + pygame.time.delay(50) + x, y = x + 5, y + 5 + + +if __name__ == '__main__': + main() +``` + +#### 碰撞检测 + +通常一个游戏中会有很多对象出现,而这些对象之间的“碰撞”在所难免,比如炮弹击中了飞机、箱子撞到了地面等。碰撞检测在绝大多数的游戏中都是一个必须得处理的至关重要的问题,pygame的sprite(动画精灵)模块就提供了对碰撞检测的支持,这里我们暂时不介绍sprite模块提供的功能,因为要检测两个小球有没有碰撞其实非常简单,只需要检查球心的距离有没有小于两个球的半径之和。为了制造出更多的小球,我们可以通过对鼠标事件的处理,在点击鼠标的位置创建颜色、大小和移动速度都随机的小球,当然要做到这一点,我们可以把之前学习到的面向对象的知识应用起来。 + +```Python +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) +``` + +#### 事件处理 + +可以在事件循环中对鼠标事件进行处理,通过事件对象的`type`属性可以判定事件类型,再通过`pos`属性就可以获得鼠标点击的位置。如果要处理键盘事件也是在这个地方,做法与处理鼠标事件类似。 + +```Python +def main(): + # 定义用来装所有球的容器 + balls = [] + # 初始化导入的pygame中的模块 + pygame.init() + # 初始化用于显示的窗口并设置窗口尺寸 + screen = pygame.display.set_mode((800, 600)) + # 设置当前窗口的标题 + pygame.display.set_caption('大球吃小球') + 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() +``` + +上面的两段代码合在一起,我们就完成了“大球吃小球”的游戏(如下图所示),准确的说它算不上一个游戏,但是做一个小游戏的基本知识我们已经通过这个例子告诉大家了,有了这些知识已经可以开始你的小游戏开发之旅了。其实上面的代码中还有很多值得改进的地方,比如刷新窗口以及让球移动起来的代码并不应该放在事件循环中,等学习了多线程的知识后,用一个后台线程来处理这些事可能是更好的选择。如果希望获得更好的用户体验,我们还可以在游戏中加入背景音乐以及在球与球发生碰撞时播放音效,利用pygame的mixer和music模块,我们可以很容易的做到这一点,大家可以自行了解这方面的知识。事实上,想了解更多的关于pygame的知识,最好的教程是[pygame的官方网站](https://www.pygame.org/news),如果英语没毛病就可以赶紧去看看啦。 如果想开发[3D游戏](https://zh.wikipedia.org/wiki/3D%E6%B8%B8%E6%88%8F),pygame就显得力不从心了,对3D游戏开发如果有兴趣的读者不妨看看[Panda3D](https://www.panda3d.org/)。 + diff --git "a/Day01-15/11.\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" "b/Day01-15/11.\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" new file mode 100755 index 000000000..6e47437e3 --- /dev/null +++ "b/Day01-15/11.\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" @@ -0,0 +1,266 @@ +## 文件和异常 + +实际开发中常常会遇到对数据进行[持久化](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96)操作的场景,而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词,可能需要先科普一下关于[文件系统](https://zh.wikipedia.org/wiki/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F)的知识,但是这里我们并不浪费笔墨介绍这个概念,请大家自行通过维基百科进行了解。 + +在Python中实现文件的读写操作其实非常简单,通过Python内置的`open`函数,我们可以指定文件名、操作模式、编码信息等来获得操作文件的对象,接下来就可以对文件进行读写操作了。这里所说的操作模式是指要打开什么样的文件(字符文件还是二进制文件)以及做什么样的操作(读、写还是追加),具体的如下表所示。 + +| 操作模式 | 具体含义 | +| -------- | -------------------------------- | +| `'r'` | 读取 (默认) | +| `'w'` | 写入(会先截断之前的内容) | +| `'x'` | 写入,如果文件已经存在会产生异常 | +| `'a'` | 追加,将内容写入到已有文件的末尾 | +| `'b'` | 二进制模式 | +| `'t'` | 文本模式(默认) | +| `'+'` | 更新(既可以读又可以写) | + +下面这张图来自于[菜鸟教程](http://www.runoob.com)网站,它展示了如果根据应用程序的需要来设置操作模式。 + +![](./res/file-open-mode.png) + +### 读写文本文件 + +读取文本文件时,需要在使用`open`函数时指定好带路径的文件名(可以使用相对路径或绝对路径)并将文件模式设置为`'r'`(如果不指定,默认值也是`'r'`),然后通过`encoding`参数指定编码(如果不指定,默认值是None,那么在读取文件时使用的是操作系统默认的编码),如果不能保证保存文件时使用的编码方式与encoding参数指定的编码方式是一致的,那么就可能因无法解码字符而导致读取失败。下面的例子演示了如何读取一个纯文本文件。 + +```Python +def main(): + f = open('致橡树.txt', 'r', encoding='utf-8') + print(f.read()) + f.close() + + +if __name__ == '__main__': + main() +``` + +请注意上面的代码,如果`open`函数指定的文件并不存在或者无法打开,那么将引发异常状况导致程序崩溃。为了让代码有一定的健壮性和容错性,我们可以使用Python的异常机制对可能在运行时发生状况的代码进行适当的处理,如下所示。 + +```Python +def main(): + f = None + try: + f = open('致橡树.txt', 'r', encoding='utf-8') + print(f.read()) + except FileNotFoundError: + print('无法打开指定的文件!') + except LookupError: + print('指定了未知的编码!') + except UnicodeDecodeError: + print('读取文件时解码错误!') + finally: + if f: + f.close() + + +if __name__ == '__main__': + main() +``` + +在Python中,我们可以将那些在运行时可能会出现状况的代码放在`try`代码块中,在`try`代码块的后面可以跟上一个或多个`except`来捕获可能出现的异常状况。例如在上面读取文件的过程中,文件找不到会引发`FileNotFoundError`,指定了未知的编码会引发`LookupError`,而如果读取文件时无法按指定方式解码会引发`UnicodeDecodeError`,我们在`try`后面跟上了三个`except`分别处理这三种不同的异常状况。最后我们使用`finally`代码块来关闭打开的文件,释放掉程序中获取的外部资源,由于`finally`块的代码不论程序正常还是异常都会执行到(甚至是调用了`sys`模块的`exit`函数退出Python环境,`finally`块都会被执行,因为`exit`函数实质上是引发了`SystemExit`异常),因此我们通常把`finally`块称为“总是执行代码块”,它最适合用来做释放外部资源的操作。如果不愿意在`finally`代码块中关闭文件对象释放资源,也可以使用上下文语法,通过`with`关键字指定文件对象的上下文环境并在离开上下文环境时自动释放文件资源,代码如下所示。 + +```Python +def main(): + try: + with open('致橡树.txt', 'r', encoding='utf-8') as f: + print(f.read()) + except FileNotFoundError: + print('无法打开指定的文件!') + except LookupError: + print('指定了未知的编码!') + except UnicodeDecodeError: + print('读取文件时解码错误!') + + +if __name__ == '__main__': + main() +``` + +除了使用文件对象的`read`方法读取文件之外,还可以使用`for-in`循环逐行读取或者用`readlines`方法将文件按行读取到一个列表容器中,代码如下所示。 + +```Python +import time + + +def main(): + # 一次性读取整个文件内容 + with open('致橡树.txt', 'r', encoding='utf-8') as f: + print(f.read()) + + # 通过for-in循环逐行读取 + with open('致橡树.txt', mode='r') as f: + for line in f: + print(line, end='') + time.sleep(0.5) + print() + + # 读取文件按行读取到列表中 + with open('致橡树.txt') as f: + lines = f.readlines() + print(lines) + + +if __name__ == '__main__': + main() +``` + +要将文本信息写入文件文件也非常简单,在使用`open`函数时指定好文件名并将文件模式设置为`'w'`即可。注意如果需要对文件内容进行追加式写入,应该将模式设置为`'a'`。如果要写入的文件不存在会自动创建文件而不是引发异常。下面的例子演示了如何将1-9999之间的素数分别写入三个文件中(1-99之间的素数保存在a.txt中,100-999之间的素数保存在b.txt中,1000-9999之间的素数保存在c.txt中)。 + +```Python +from math import sqrt + + +def is_prime(n): + """判断素数的函数""" + assert n > 0 + for factor in range(2, int(sqrt(n)) + 1): + if n % factor == 0: + return False + return True if n != 1 else False + + +def main(): + filenames = ('a.txt', 'b.txt', 'c.txt') + fs_list = [] + try: + for filename in filenames: + fs_list.append(open(filename, 'w', encoding='utf-8')) + for number in range(1, 10000): + if is_prime(number): + if number < 100: + fs_list[0].write(str(number) + '\n') + elif number < 1000: + fs_list[1].write(str(number) + '\n') + else: + fs_list[2].write(str(number) + '\n') + except IOError as ex: + print(ex) + print('写文件时发生错误!') + finally: + for fs in fs_list: + fs.close() + print('操作完成!') + + +if __name__ == '__main__': + main() +``` + +### 读写二进制文件 + +知道了如何读写文本文件要读写二进制文件也就很简单了,下面的代码实现了复制图片文件的功能。 + +```Python +def main(): + try: + with open('guido.jpg', 'rb') as fs1: + data = fs1.read() + print(type(data)) # + with open('吉多.jpg', 'wb') as fs2: + fs2.write(data) + except FileNotFoundError as e: + print('指定的文件无法打开.') + except IOError as e: + print('读写文件时出现错误.') + print('程序执行结束.') + + +if __name__ == '__main__': + main() +``` + +### 读写JSON文件 + +通过上面的讲解,我们已经知道如何将文本数据和二进制数据保存到文件中,那么这里还有一个问题,如果希望把一个列表或者一个字典中的数据保存到文件中又该怎么做呢?答案是将数据以JSON格式进行保存。JSON是“JavaScript Object Notation”的缩写,它本来是JavaScript语言中创建对象的一种字面量语法,现在已经被广泛的应用于跨平台跨语言的数据交换,原因很简单,因为JSON也是纯文本,任何系统任何编程语言处理纯文本都是没有问题的。目前JSON基本上已经取代了XML作为异构系统间交换数据的事实标准。关于JSON的知识,更多的可以参考[JSON的官方网站](http://json.org),从这个网站也可以了解到每种语言处理JSON数据格式可以使用的工具或三方库,下面是一个JSON的简单例子。 + +```JSON +{ + "name": "骆昊", + "age": 38, + "qq": 957658, + "friends": ["王大锤", "白元芳"], + "cars": [ + {"brand": "BYD", "max_speed": 180}, + {"brand": "Audi", "max_speed": 280}, + {"brand": "Benz", "max_speed": 320} + ] +} +``` + +可能大家已经注意到了,上面的JSON跟Python中的字典其实是一样一样的,事实上JSON的数据类型和Python的数据类型是很容易找到对应关系的,如下面两张表所示。 + +| JSON | Python | +| ------------------- | ------------ | +| object | dict | +| array | list | +| string | str | +| number (int / real) | int / float | +| true / false | True / False | +| null | None | + +| Python | JSON | +| -------------------------------------- | ------------ | +| dict | object | +| list, tuple | array | +| str | string | +| int, float, int- & float-derived Enums | number | +| True / False | true / false | +| None | null | + +我们使用Python中的json模块就可以将字典或列表以JSON格式保存到文件中,代码如下所示。 + +```Python +import json + + +def main(): + mydict = { + 'name': '骆昊', + 'age': 38, + 'qq': 957658, + 'friends': ['王大锤', '白元芳'], + 'cars': [ + {'brand': 'BYD', 'max_speed': 180}, + {'brand': 'Audi', 'max_speed': 280}, + {'brand': 'Benz', 'max_speed': 320} + ] + } + try: + with open('data.json', 'w', encoding='utf-8') as fs: + json.dump(mydict, fs) + except IOError as e: + print(e) + print('保存数据完成!') + + +if __name__ == '__main__': + main() +``` + +json模块主要有四个比较重要的函数,分别是: + +- `dump` - 将Python对象按照JSON格式序列化到文件中 +- `dumps` - 将Python对象处理成JSON格式的字符串 +- `load` - 将文件中的JSON数据反序列化成对象 +- `loads` - 将字符串的内容反序列化成Python对象 + +这里出现了两个概念,一个叫序列化,一个叫反序列化。自由的百科全书[维基百科](https://zh.wikipedia.org/)上对这两个概念是这样解释的:“序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换为可以存储或传输的形式,这样在需要的时候能够恢复到原先的状态,而且通过序列化的数据重新获取字节时,可以利用这些字节来产生原始对象的副本(拷贝)。与这个过程相反的动作,即从一系列字节中提取数据结构的操作,就是反序列化(deserialization)”。 + +目前绝大多数网络数据服务(或称之为网络API)都是基于[HTTP协议](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE)提供JSON格式的数据,关于HTTP协议的相关知识,可以看看阮一峰老师的[《HTTP协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html),如果想了解国内的网络数据服务,可以看看[聚合数据](https://www.juhe.cn/)和[阿凡达数据](http://www.avatardata.cn/)等网站,国外的可以看看[{API}Search](http://apis.io/)网站。下面的例子演示了如何使用[requests](http://docs.python-requests.org/zh_CN/latest/)模块(封装得足够好的第三方网络访问模块)访问网络API获取国内新闻,如何通过json模块解析JSON数据并显示新闻标题,这个例子使用了[天行数据](https://www.tianapi.com/)提供的国内新闻数据接口,其中的APIKey需要自己到该网站申请。 + +```Python +import requests +import json + + +def main(): + resp = requests.get('http://api.tianapi.com/guonei/?key=APIKey&num=10') + data_model = json.loads(resp.text) + for news in data_model['newslist']: + print(news['title']) + + +if __name__ == '__main__': + main() +``` + +在Python中要实现序列化和反序列化除了使用json模块之外,还可以使用pickle和shelve模块,但是这两个模块是使用特有的序列化协议来序列化数据,因此序列化后的数据只能被Python识别。关于这两个模块的相关知识可以自己看看网络上的资料。另外,如果要了解更多的关于Python异常机制的知识,可以看看segmentfault上面的文章[《总结:Python中的异常处理》](https://segmentfault.com/a/1190000007736783),这篇文章不仅介绍了Python中异常机制的使用,还总结了一系列的最佳实践,很值得一读。 \ No newline at end of file diff --git "a/Day01-15/12.\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" "b/Day01-15/12.\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" new file mode 100755 index 000000000..088cc584d --- /dev/null +++ "b/Day01-15/12.\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" @@ -0,0 +1,180 @@ +## 使用正则表达式 + +### 正则表达式相关知识 + +在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要,正则表达式就是用于描述这些规则的工具,换句话说正则表达式是一种工具,它定义了字符串的匹配模式(如何检查一个字符串是否有跟某种模式匹配的部分或者从一个字符串中将与模式匹配的部分提取出来或者替换掉)。如果你在Windows操作系统中使用过文件查找并且在指定文件名时使用过通配符(\*和?),那么正则表达式也是与之类似的用来进行文本匹配的工具,只不过比起通配符正则表达式更强大,它能更精确地描述你的需求(当然你付出的代价是书写一个正则表达式比打出一个通配符要复杂得多,要知道任何给你带来好处的东西都是有代价的,就如同学习一门编程语言一样),比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像028-12345678或0813-7654321),这不就是国内的座机号码吗。最初计算机是为了做数学运算而诞生的,处理的信息基本上都是数值,而今天我们在日常工作中处理的信息基本上都是文本数据,我们希望计算机能够识别和处理符合某些模式的文本,正则表达式就显得非常重要了。今天几乎所有的编程语言都提供了对正则表达式操作的支持,Python通过标准库中的re模块来支持正则表达式操作。 + +我们可以考虑下面一个问题:我们从某个地方(可能是一个文本文件,也可能是网络上的一则新闻)获得了一个字符串,希望在字符串中找出手机号和座机号。当然我们可以设定手机号是11位的数字(注意并不是随机的11位数字,因为你没有见过“25012345678”这样的手机号吧)而座机号跟上一段中描述的模式相同,如果不使用正则表达式要完成这个任务就会很麻烦。 + +关于正则表达式的相关知识,大家可以阅读一篇非常有名的博客叫[《正则表达式30分钟入门教程》](https://deerchao.net/tutorials/regex/regex.htm),读完这篇文章后你就可以看懂下面的表格,这是我们对正则表达式中的一些基本符号进行的扼要总结。 + +| 符号 | 解释 | 示例 | 说明 | +| ------------------ | ----------------------------------------- | ---------------- | -------------------------------------------------- | +| . | 匹配任意字符 | b.t | 可以匹配bat / but / b#t / b1t等 | +| \\w | 匹配字母/数字/下划线 | b\\wt | 可以匹配bat / b1t / b_t等
但不能匹配b#t | +| \\s | 匹配空白字符(包括\r、\n、\t等) | love\\syou | 可以匹配love you | +| \\d | 匹配数字 | \\d\\d | 可以匹配01 / 23 / 99等 | +| \\b | 匹配单词的边界 | \\bThe\\b | | +| ^ | 匹配字符串的开始 | ^The | 可以匹配The开头的字符串 | +| $ | 匹配字符串的结束 | .exe$ | 可以匹配.exe结尾的字符串 | +| \\W | 匹配非字母/数字/下划线 | b\\Wt | 可以匹配b#t / b@t等
但不能匹配but / b1t / b_t等 | +| \\S | 匹配非空白字符 | love\\Syou | 可以匹配love#you等
但不能匹配love you | +| \\D | 匹配非数字 | \\d\\D | 可以匹配9a / 3# / 0F等 | +| \\B | 匹配非单词边界 | \\Bio\\B | | +| [] | 匹配来自字符集的任意单一字符 | [aeiou] | 可以匹配任一元音字母字符 | +| [^] | 匹配不在字符集中的任意单一字符 | [^aeiou] | 可以匹配任一非元音字母字符 | +| * | 匹配0次或多次 | \\w* | | +| + | 匹配1次或多次 | \\w+ | | +| ? | 匹配0次或1次 | \\w? | | +| {N} | 匹配N次 | \\w{3} | | +| {M,} | 匹配至少M次 | \\w{3,} | | +| {M,N} | 匹配至少M次至多N次 | \\w{3,6} | | +| \| | 分支 | foo\|bar | 可以匹配foo或者bar | +| (?#) | 注释 | | | +| (exp) | 匹配exp并捕获到自动命名的组中 | | | +| (?<name>exp) | 匹配exp并捕获到名为name的组中 | | | +| (?:exp) | 匹配exp但是不捕获匹配的文本 | | | +| (?=exp) | 匹配exp前面的位置 | \\b\\w+(?=ing) | 可以匹配I'm dancing中的danc | +| (?<=exp) | 匹配exp后面的位置 | (?<=\\bdanc)\\w+\\b | 可以匹配I love dancing and reading中的第一个ing | +| (?!exp) | 匹配后面不是exp的位置 | | | +| (?a.\*?b | 将正则表达式应用于aabab,前者会匹配整个字符串aabab,后者会匹配aab和ab两个字符串 | +| +? | 重复1次或多次,但尽可能少重复 | | | +| ?? | 重复0次或1次,但尽可能少重复 | | | +| {M,N}? | 重复M到N次,但尽可能少重复 | | | +| {M,}? | 重复M次以上,但尽可能少重复 | | | + +> **说明:** 如果需要匹配的字符是正则表达式中的特殊字符,那么可以使用\\进行转义处理,例如想匹配小数点可以写成\\.就可以了,因为直接写.会匹配任意字符;同理,想匹配圆括号必须写成\\(和\\),否则圆括号被视为正则表达式中的分组。 + +### Python对正则表达式的支持 + +Python提供了re模块来支持正则表达式相关操作,下面是re模块中的核心函数。 + +| 函数 | 说明 | +| -------------------------------------------- | ------------------------------------------------------------ | +| compile(pattern, flags=0) | 编译正则表达式返回正则表达式对象 | +| match(pattern, string, flags=0) | 用正则表达式匹配字符串 成功返回匹配对象 否则返回None | +| search(pattern, string, flags=0) | 搜索字符串中第一次出现正则表达式的模式 成功返回匹配对象 否则返回None | +| split(pattern, string, maxsplit=0, flags=0) | 用正则表达式指定的模式分隔符拆分字符串 返回列表 | +| sub(pattern, repl, string, count=0, flags=0) | 用指定的字符串替换原字符串中与正则表达式匹配的模式 可以用count指定替换的次数 | +| fullmatch(pattern, string, flags=0) | match函数的完全匹配(从字符串开头到结尾)版本 | +| findall(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回字符串的列表 | +| finditer(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回一个迭代器 | +| purge() | 清除隐式编译的正则表达式的缓存 | +| re.I / re.IGNORECASE | 忽略大小写匹配标记 | +| re.M / re.MULTILINE | 多行匹配标记 | + +> **说明:** 上面提到的re模块中的这些函数,实际开发中也可以用正则表达式对象的方法替代对这些函数的使用,如果一个正则表达式需要重复的使用,那么先通过compile函数编译正则表达式并创建出正则表达式对象无疑是更为明智的选择。 + +下面我们通过一系列的例子来告诉大家在Python中如何使用正则表达式。 + +#### 例子1:验证输入用户名和QQ号是否有效并给出对应的提示信息。 + +```Python +""" +验证输入用户名和QQ号是否有效并给出对应的提示信息 + +要求:用户名必须由字母、数字或下划线构成且长度在6~20个字符之间,QQ号是5~12的数字且首位不能为0 +""" +import re + + +def main(): + username = input('请输入用户名: ') + qq = input('请输入QQ号: ') + # match函数的第一个参数是正则表达式字符串或正则表达式对象 + # 第二个参数是要跟正则表达式做匹配的字符串对象 + m1 = re.match(r'^[0-9a-zA-Z_]{6,20}$', username) + if not m1: + print('请输入有效的用户名.') + m2 = re.match(r'^[1-9]\d{4,11}$', qq) + if not m2: + print('请输入有效的QQ号.') + if m1 and m2: + print('你输入的信息是有效的!') + + +if __name__ == '__main__': + main() +``` + +> **提示:** 上面在书写正则表达式时使用了“原始字符串”的写法(在字符串前面加上了r),所谓“原始字符串”就是字符串中的每个字符都是它原始的意义,说得更直接一点就是字符串中没有所谓的转义字符啦。因为正则表达式中有很多元字符和需要进行转义的地方,如果不使用原始字符串就需要将反斜杠写作\\\\,例如表示数字的\\d得书写成\\\\d,这样不仅写起来不方便,阅读的时候也会很吃力。 + +#### 例子2:从一段文字中提取出国内手机号码。 + +下面这张图是截止到2017年底,国内三家运营商推出的手机号段。 + +![](./res/tel-start-number.png) + +```Python +import re + + +def main(): + # 创建正则表达式对象 使用了前瞻和回顾来保证手机号前后不应该出现数字 + pattern = re.compile(r'(?<=\D)1[34578]\d{9}(?=\D)') + sentence = ''' + 重要的事情说8130123456789遍,我的手机号是13512346789这个靓号, + 不是15600998765,也是110或119,王大锤的手机号才是15600998765。 + ''' + # 查找所有匹配并保存到一个列表中 + mylist = re.findall(pattern, sentence) + print(mylist) + print('--------华丽的分隔线--------') + # 通过迭代器取出匹配对象并获得匹配的内容 + for temp in pattern.finditer(sentence): + print(temp.group()) + print('--------华丽的分隔线--------') + # 通过search函数指定搜索位置找出所有匹配 + m = pattern.search(sentence) + while m: + print(m.group()) + m = pattern.search(sentence, m.end()) + + +if __name__ == '__main__': + main() +``` + +> **说明:** 上面匹配国内手机号的正则表达式并不够好,因为像14开头的号码只有145或147,而上面的正则表达式并没有考虑这种情况,要匹配国内手机号,更好的正则表达式的写法是:`(?<=\D)(1[38]\d{9}|14[57]\d{8}|15[0-35-9]\d{8}|17[678]\d{8})(?=\D)`,国内最近好像有19和16开头的手机号了,但是这个暂时不在我们考虑之列。 + +#### 例子3:替换字符串中的不良内容 + +```Python +import re + + +def main(): + sentence = '你丫是傻叉吗? 我操你大爷的. Fuck you.' + purified = re.sub('[操肏艹]|fuck|shit|傻[比屄逼叉缺吊屌]|煞笔', + '*', sentence, flags=re.IGNORECASE) + print(purified) # 你丫是*吗? 我*你大爷的. * you. + + +if __name__ == '__main__': + main() +``` + +> **说明:** re模块的正则表达式相关函数中都有一个flags参数,它代表了正则表达式的匹配标记,可以通过该标记来指定匹配时是否忽略大小写、是否进行多行匹配、是否显示调试信息等。如果需要为flags参数指定多个值,可以使用[按位或运算符](http://www.runoob.com/python/python-operators.html#ysf5)进行叠加,如`flags=re.I | re.M`。 + +#### 例子4:拆分长字符串 + +```Python +import re + + +def main(): + poem = '窗前明月光,疑是地上霜。举头望明月,低头思故乡。' + sentence_list = re.split(r'[,。, .]', poem) + while '' in sentence_list: + sentence_list.remove('') + print(sentence_list) # ['窗前明月光', '疑是地上霜', '举头望明月', '低头思故乡'] + + +if __name__ == '__main__': + main() +``` + +### 后话 + +如果要从事爬虫类应用的开发,那么正则表达式一定是一个非常好的助手,因为它可以帮助我们迅速的从网页代码中发现某种我们指定的模式并提取出我们需要的信息,当然对于初学者来收,要编写一个正确的适当的正则表达式可能并不是一件容易的事情(当然有些常用的正则表达式可以直接在网上找找),所以实际开发爬虫应用的时候,有很多人会选择[Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/)或[Lxml](http://lxml.de/)来进行匹配和信息的提取,前者简单方便但是性能较差,后者既好用性能也好,但是安装稍嫌麻烦,这些内容我们会在后期的爬虫专题中为大家介绍。 \ No newline at end of file diff --git "a/Day01-15/13.\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" "b/Day01-15/13.\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" new file mode 100755 index 000000000..2c93c14fe --- /dev/null +++ "b/Day01-15/13.\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" @@ -0,0 +1,489 @@ +## 进程和线程 + +今天我们使用的计算机早已进入多CPU或多核时代,而我们使用的操作系统都是支持“多任务”的操作系统,这使得我们可以同时运行多个程序,也可以将一个程序分解为若干个相对独立的子任务,让多个子任务并发的执行,从而缩短程序的执行时间,同时也让用户获得更好的体验。因此在当下不管是用什么编程语言进行开发,实现让程序同时执行多个任务也就是常说的“并发编程”,应该是程序员必备技能之一。为此,我们需要先讨论两个概念,一个叫进程,一个叫线程。 + +### 概念 + +进程就是操作系统中执行的一个程序,操作系统以进程为单位分配存储空间,每个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据,操作系统管理所有进程的执行,为它们合理的分配资源。进程可以通过fork或spawn的方式来创建新的进程来执行其他的任务,不过新的进程也有自己独立的内存空间,因此必须通过进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,具体的方式包括管道、信号、套接字、共享内存区等。 + +一个进程还可以拥有多个并发的执行线索,简单的说就是拥有多个可以获得CPU调度的执行单元,这就是所谓的线程。由于线程在同一个进程下,它们可以共享相同的上下文,因此相对于进程而言,线程间的信息共享和通信更加容易。当然在单核CPU系统中,真正的并发是不可能的,因为在某个时刻能够获得CPU的只有唯一的一个线程,多个线程共享了CPU的执行时间。使用多线程实现并发编程为程序带来的好处是不言而喻的,最主要的体现在提升程序的性能和改善用户体验,今天我们使用的软件几乎都用到了多线程技术,这一点可以利用系统自带的进程监控工具(如macOS中的“活动监视器”、Windows中的“任务管理器”)来证实,如下图所示。 + +![](./res/macos-monitor.png) + +当然多线程也并不是没有坏处,站在其他进程的角度,多线程的程序对其他程序并不友好,因为它占用了更多的CPU执行时间,导致其他程序无法获得足够的CPU执行时间;另一方面,站在开发者的角度,编写和调试多线程的程序都对开发者有较高的要求,对于初学者来说更加困难。 + +Python既支持多进程又支持多线程,因此使用Python实现并发编程主要有3种方式:多进程、多线程、多进程+多线程。 + +### Python中的多进程 + +Unix和Linux操作系统上提供了`fork()`系统调用来创建进程,调用`fork()`函数的是父进程,创建出的是子进程,子进程是父进程的一个拷贝,但是子进程拥有自己的PID。`fork()`函数非常特殊它会返回两次,父进程中可以通过`fork()`函数的返回值得到子进程的PID,而子进程中的返回值永远都是0。Python的os模块提供了`fork()`函数。由于Windows系统没有`fork()`调用,因此要实现跨平台的多进程编程,可以使用multiprocessing模块的`Process`类来创建子进程,而且该模块还提供了更高级的封装,例如批量启动进程的进程池(`Pool`)、用于进程间通信的队列(`Queue`)和管道(`Pipe`)等。 + +下面用一个下载文件的例子来说明使用多进程和不使用多进程到底有什么差别,先看看下面的代码。 + +```Python +from random import randint +from time import time, sleep + + +def download_task(filename): + print('开始下载%s...' % filename) + time_to_download = randint(5, 10) + sleep(time_to_download) + print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) + + +def main(): + start = time() + download_task('Python从入门到住院.pdf') + download_task('Peking Hot.avi') + end = time() + print('总共耗费了%.2f秒.' % (end - start)) + + +if __name__ == '__main__': + main() +``` + +下面是运行程序得到的一次运行结果。 + +```Shell +开始下载Python从入门到住院.pdf... +Python从入门到住院.pdf下载完成! 耗费了6秒 +开始下载Peking Hot.avi... +Peking Hot.avi下载完成! 耗费了7秒 +总共耗费了13.01秒. +``` + +从上面的例子可以看出,如果程序中的代码只能按顺序一点点的往下执行,那么即使执行两个毫不相关的下载任务,也需要先等待一个文件下载完成后才能开始下一个下载任务,很显然这并不合理也没有效率。接下来我们使用多进程的方式将两个下载任务放到不同的进程中,代码如下所示。 + +```Python +from multiprocessing import Process +from os import getpid +from random import randint +from time import time, sleep + + +def download_task(filename): + print('启动下载进程,进程号[%d].' % getpid()) + print('开始下载%s...' % filename) + time_to_download = randint(5, 10) + sleep(time_to_download) + print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) + + +def main(): + start = time() + p1 = Process(target=download_task, args=('Python从入门到住院.pdf', )) + p1.start() + p2 = Process(target=download_task, args=('Peking Hot.avi', )) + p2.start() + p1.join() + p2.join() + end = time() + print('总共耗费了%.2f秒.' % (end - start)) + + +if __name__ == '__main__': + main() +``` + +在上面的代码中,我们通过`Process`类创建了进程对象,通过`target`参数我们传入一个函数来表示进程启动后要执行的代码,后面的`args`是一个元组,它代表了传递给函数的参数。`Process`对象的`start`方法用来启动进程,而`join`方法表示等待进程执行结束。运行上面的代码可以明显发现两个下载任务“同时”启动了,而且程序的执行时间将大大缩短,不再是两个任务的时间总和。下面是程序的一次执行结果。 + +```Shell +启动下载进程,进程号[1530]. +开始下载Python从入门到住院.pdf... +启动下载进程,进程号[1531]. +开始下载Peking Hot.avi... +Peking Hot.avi下载完成! 耗费了7秒 +Python从入门到住院.pdf下载完成! 耗费了10秒 +总共耗费了10.01秒. +``` + +我们也可以使用subprocess模块中的类和函数来创建和启动子进程,然后通过管道来和子进程通信,这些内容我们不在此进行讲解,有兴趣的读者可以自己了解这些知识。接下来我们将重点放在如何实现两个进程间的通信。我们启动两个进程,一个输出Ping,一个输出Pong,两个进程输出的Ping和Pong加起来一共10个。听起来很简单吧,但是如果这样写可是错的哦。 + +```Python +from multiprocessing import Process +from time import sleep + +counter = 0 + + +def sub_task(string): + global counter + while counter < 10: + print(string, end='', flush=True) + counter += 1 + sleep(0.01) + + +def main(): + Process(target=sub_task, args=('Ping', )).start() + Process(target=sub_task, args=('Pong', )).start() + + +if __name__ == '__main__': + main() +``` + +看起来没毛病,但是最后的结果是Ping和Pong各输出了10个,Why?当我们在程序中创建进程的时候,子进程复制了父进程及其所有的数据结构,每个子进程有自己独立的内存空间,这也就意味着两个子进程中各有一个`counter`变量,所以结果也就可想而知了。要解决这个问题比较简单的办法是使用multiprocessing模块中的`Queue`类,它是可以被多个进程共享的队列,底层是通过管道和[信号量(semaphore)]()机制来实现的,有兴趣的读者可以自己尝试一下。 + + +### Python中的多线程 + +在Python早期的版本中就引入了thread模块(现在名为_thread)来实现多线程编程,然而该模块过于底层,而且很多功能都没有提供,因此目前的多线程开发我们推荐使用threading模块,该模块对多线程编程提供了更好的面向对象的封装。我们把刚才下载文件的例子用多线程的方式来实现一遍。 + +```Python +from random import randint +from threading import Thread +from time import time, sleep + + +def download(filename): + print('开始下载%s...' % filename) + time_to_download = randint(5, 10) + sleep(time_to_download) + print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) + + +def main(): + start = time() + t1 = Thread(target=download, args=('Python从入门到住院.pdf',)) + t1.start() + t2 = Thread(target=download, args=('Peking Hot.avi',)) + t2.start() + t1.join() + t2.join() + end = time() + print('总共耗费了%.3f秒' % (end - start)) + + +if __name__ == '__main__': + main() +``` + +我们可以直接使用threading模块的`Thread`类来创建线程,但是我们之前讲过一个非常重要的概念叫“继承”,我们可以从已有的类创建新类,因此也可以通过继承`Thread`类的方式来创建自定义的线程类,然后再创建线程对象并启动线程。代码如下所示。 + +```Python +from random import randint +from threading import Thread +from time import time, sleep + + +class DownloadTask(Thread): + + def __init__(self, filename): + super().__init__() + self._filename = filename + + def run(self): + print('开始下载%s...' % self._filename) + time_to_download = randint(5, 10) + sleep(time_to_download) + print('%s下载完成! 耗费了%d秒' % (self._filename, time_to_download)) + + +def main(): + start = time() + t1 = DownloadTask('Python从入门到住院.pdf') + t1.start() + t2 = DownloadTask('Peking Hot.avi') + t2.start() + t1.join() + t2.join() + end = time() + print('总共耗费了%.2f秒.' % (end - start)) + + +if __name__ == '__main__': + main() +``` + +因为多个线程可以共享进程的内存空间,因此要实现多个线程间的通信相对简单,大家能想到的最直接的办法就是设置一个全局变量,多个线程共享这个全局变量即可。但是当多个线程共享同一个变量(我们通常称之为“资源”)的时候,很有可能产生不可控的结果从而导致程序失效甚至崩溃。如果一个资源被多个线程竞争使用,那么我们通常称之为“临界资源”,对“临界资源”的访问需要加上保护,否则资源会处于“混乱”的状态。下面的例子演示了100个线程向同一个银行账户转账(转入1元钱)的场景,在这个例子中,银行账户就是一个临界资源,在没有保护的情况下我们很有可能会得到错误的结果。 + +```Python +from time import sleep +from threading import Thread + + +class Account(object): + + def __init__(self): + self._balance = 0 + + def deposit(self, money): + # 计算存款后的余额 + new_balance = self._balance + money + # 模拟受理存款业务需要0.01秒的时间 + sleep(0.01) + # 修改账户余额 + self._balance = new_balance + + @property + def balance(self): + return self._balance + + +class AddMoneyThread(Thread): + + def __init__(self, account, money): + super().__init__() + self._account = account + self._money = money + + def run(self): + self._account.deposit(self._money) + + +def main(): + account = Account() + threads = [] + # 创建100个存款的线程向同一个账户中存钱 + for _ in range(100): + t = AddMoneyThread(account, 1) + threads.append(t) + t.start() + # 等所有存款的线程都执行完毕 + for t in threads: + t.join() + print('账户余额为: ¥%d元' % account.balance) + + +if __name__ == '__main__': + main() +``` + +运行上面的程序,结果让人大跌眼镜,100个线程分别向账户中转入1元钱,结果居然远远小于100元。之所以出现这种情况是因为我们没有对银行账户这个“临界资源”加以保护,多个线程同时向账户中存钱时,会一起执行到`new_balance = self._balance + money`这行代码,多个线程得到的账户余额都是初始状态下的`0`,所以都是`0`上面做了+1的操作,因此得到了错误的结果。在这种情况下,“锁”就可以派上用场了。我们可以通过“锁”来保护“临界资源”,只有获得“锁”的线程才能访问“临界资源”,而其他没有得到“锁”的线程只能被阻塞起来,直到获得“锁”的线程释放了“锁”,其他线程才有机会获得“锁”,进而访问被保护的“临界资源”。下面的代码演示了如何使用“锁”来保护对银行账户的操作,从而获得正确的结果。 + +```Python +from time import sleep +from threading import Thread, Lock + + +class Account(object): + + def __init__(self): + self._balance = 0 + self._lock = Lock() + + def deposit(self, money): + # 先获取锁才能执行后续的代码 + self._lock.acquire() + try: + new_balance = self._balance + money + sleep(0.01) + self._balance = new_balance + finally: + # 在finally中执行释放锁的操作保证正常异常锁都能释放 + self._lock.release() + + @property + def balance(self): + return self._balance + + +class AddMoneyThread(Thread): + + def __init__(self, account, money): + super().__init__() + self._account = account + self._money = money + + def run(self): + self._account.deposit(self._money) + + +def main(): + account = Account() + threads = [] + for _ in range(100): + t = AddMoneyThread(account, 1) + threads.append(t) + t.start() + for t in threads: + t.join() + print('账户余额为: ¥%d元' % account.balance) + + +if __name__ == '__main__': + main() +``` + +比较遗憾的一件事情是Python的多线程并不能发挥CPU的多核特性,这一点只要启动几个执行死循环的线程就可以得到证实了。之所以如此,是因为Python的解释器有一个“全局解释器锁”(GIL)的东西,任何线程执行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行,这是一个历史遗留问题,但是即便如此,就如我们之前举的例子,使用多线程在提升执行效率和改善用户体验方面仍然是有积极意义的。 + +### 多进程还是多线程 + +无论是多进程还是多线程,只要数量一多,效率肯定上不去,为什么呢?我们打个比方,假设你不幸正在准备中考,每天晚上需要做语文、数学、英语、物理、化学这5科的作业,每项作业耗时1小时。如果你先花1小时做语文作业,做完了,再花1小时做数学作业,这样,依次全部做完,一共花5小时,这种方式称为单任务模型。如果你打算切换到多任务模型,可以先做1分钟语文,再切换到数学作业,做1分钟,再切换到英语,以此类推,只要切换速度足够快,这种方式就和单核CPU执行多任务是一样的了,以旁观者的角度来看,你就正在同时写5科作业。 + +但是,切换作业是有代价的,比如从语文切到数学,要先收拾桌子上的语文书本、钢笔(这叫保存现场),然后,打开数学课本、找出圆规直尺(这叫准备新环境),才能开始做数学作业。操作系统在切换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。这个切换过程虽然很快,但是也需要耗费时间。如果有几千个任务同时进行,操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了,这种情况最常见的就是硬盘狂响,点窗口无反应,系统处于假死状态。所以,多任务一旦多到一个限度,反而会使得系统性能急剧下降,最终导致所有任务都做不好。 + +是否采用多任务的第二个考虑是任务的类型,可以把任务分为计算密集型和I/O密集型。计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如对视频进行编码解码或者格式转换等等,这种任务全靠CPU的运算能力,虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低。计算密集型任务由于主要消耗CPU资源,这类任务用Python这样的脚本语言去执行效率通常很低,最能胜任这类任务的是C语言,我们之前提到过Python中有嵌入C/C++代码的机制。 + +除了计算密集型任务,其他的涉及到网络、存储介质I/O的任务都可以视为I/O密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待I/O操作完成(因为I/O的速度远远低于CPU和内存的速度)。对于I/O密集型任务,如果启动多任务,就可以减少I/O等待时间从而让CPU高效率的运转。有一大类的任务都属于I/O密集型任务,这其中包括了我们很快会涉及到的网络应用和Web应用。 + +> **说明:** 上面的内容和例子来自于[廖雪峰官方网站的《Python教程》](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000),因为对作者文中的某些观点持有不同的看法,对原文的文字描述做了适当的调整。 + +### 单线程+异步I/O + +现代操作系统对I/O操作的改进中最为重要的就是支持异步I/O。如果充分利用操作系统提供的异步I/O支持,就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型。Nginx就是支持异步I/O的Web服务器,它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上,可以运行多个进程(数量与CPU核心数相同),充分利用多核CPU。用Node.js开发的服务器端程序也使用了这种工作模式,这也是当下并发编程的一种流行方案。 + +在Python语言中,单线程+异步I/O的编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。协程最大的优势就是极高的执行效率,因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销。协程的第二个优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不用加锁,只需要判断状态就好了,所以执行效率比多线程高很多。如果想要充分利用CPU的多核特性,最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。关于这方面的内容,在后续的课程中会进行讲解。 + +### 应用案例 + +#### 例子1:将耗时间的任务放到线程中以获得更好的用户体验。 + +如下所示的界面中,有“下载”和“关于”两个按钮,用休眠的方式模拟点击“下载”按钮会联网下载文件需要耗费10秒的时间,如果不使用“多线程”,我们会发现,当点击“下载”按钮后整个程序的其他部分都被这个耗时间的任务阻塞而无法执行了,这显然是非常糟糕的用户体验,代码如下所示。 + +```Python +import time +import tkinter +import tkinter.messagebox + + +def download(): + # 模拟下载任务需要花费10秒钟时间 + time.sleep(10) + tkinter.messagebox.showinfo('提示', '下载完成!') + + +def show_about(): + tkinter.messagebox.showinfo('关于', '作者: 骆昊(v1.0)') + + +def main(): + top = tkinter.Tk() + top.title('单线程') + top.geometry('200x150') + top.wm_attributes('-topmost', True) + + panel = tkinter.Frame(top) + button1 = tkinter.Button(panel, text='下载', command=download) + button1.pack(side='left') + button2 = tkinter.Button(panel, text='关于', command=show_about) + button2.pack(side='right') + panel.pack(side='bottom') + + tkinter.mainloop() + + +if __name__ == '__main__': + main() +``` + +如果使用多线程将耗时间的任务放到一个独立的线程中执行,这样就不会因为执行耗时间的任务而阻塞了主线程,修改后的代码如下所示。 + +```Python +import time +import tkinter +import tkinter.messagebox +from threading import Thread + + +def main(): + + class DownloadTaskHandler(Thread): + + def run(self): + time.sleep(10) + tkinter.messagebox.showinfo('提示', '下载完成!') + # 启用下载按钮 + button1.config(state=tkinter.NORMAL) + + def download(): + # 禁用下载按钮 + button1.config(state=tkinter.DISABLED) + # 通过daemon参数将线程设置为守护线程(主程序退出就不再保留执行) + # 在线程中处理耗时间的下载任务 + DownloadTaskHandler(daemon=True).start() + + def show_about(): + tkinter.messagebox.showinfo('关于', '作者: 骆昊(v1.0)') + + top = tkinter.Tk() + top.title('单线程') + top.geometry('200x150') + top.wm_attributes('-topmost', 1) + + panel = tkinter.Frame(top) + button1 = tkinter.Button(panel, text='下载', command=download) + button1.pack(side='left') + button2 = tkinter.Button(panel, text='关于', command=show_about) + button2.pack(side='right') + panel.pack(side='bottom') + + tkinter.mainloop() + + +if __name__ == '__main__': + main() +``` + +#### 例子2:使用多进程对复杂任务进行“分而治之”。 + +我们来完成1~100000000求和的计算密集型任务,这个问题本身非常简单,有点循环的知识就能解决,代码如下所示。 + +```Python +from time import time + + +def main(): + total = 0 + number_list = [x for x in range(1, 100000001)] + start = time() + for number in number_list: + total += number + print(total) + end = time() + print('Execution time: %.3fs' % (end - start)) + + +if __name__ == '__main__': + main() +``` + +在上面的代码中,我故意先去创建了一个列表容器然后填入了100000000个数,这一步其实是比较耗时间的,所以为了公平起见,当我们将这个任务分解到8个进程中去执行的时候,我们暂时也不考虑列表切片操作花费的时间,只是把做运算和合并运算结果的时间统计出来,代码如下所示。 + +```Python +from multiprocessing import Process, Queue +from random import randint +from time import time + + +def task_handler(curr_list, result_queue): + total = 0 + for number in curr_list: + total += number + result_queue.put(total) + + +def main(): + processes = [] + number_list = [x for x in range(1, 100000001)] + result_queue = Queue() + index = 0 + # 启动8个进程将数据切片后进行运算 + for _ in range(8): + p = Process(target=task_handler, + args=(number_list[index:index + 12500000], result_queue)) + index += 12500000 + processes.append(p) + p.start() + # 开始记录所有进程执行完成花费的时间 + start = time() + for p in processes: + p.join() + # 合并执行结果 + total = 0 + while not result_queue.empty(): + total += result_queue.get() + print(total) + end = time() + print('Execution time: ', (end - start), 's', sep='') + + +if __name__ == '__main__': + main() +``` + +比较两段代码的执行结果(在我目前使用的MacBook上,上面的代码需要大概6秒左右的时间,而下面的代码只需要不到1秒的时间,再强调一次我们只是比较了运算的时间,不考虑列表创建及切片操作花费的时间),使用多进程后由于获得了更多的CPU执行时间以及更好的利用了CPU的多核特性,明显的减少了程序的执行时间,而且计算量越大效果越明显。当然,如果愿意还可以将多个进程部署在不同的计算机上,做成分布式进程,具体的做法就是通过`multiprocessing.managers`模块中提供的管理器将`Queue`对象通过网络共享出来(注册到网络上让其他计算机可以访问),这部分内容也留到爬虫的专题再进行讲解。 + diff --git "a/Day01-15/14.\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250\345\222\214\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" "b/Day01-15/14.\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250\345\222\214\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" new file mode 100755 index 000000000..02f2b4a72 --- /dev/null +++ "b/Day01-15/14.\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250\345\222\214\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" @@ -0,0 +1,417 @@ +## 网络编程入门 + +### 计算机网络基础 + +计算机网络是独立自主的计算机互联而成的系统的总称,组建计算机网络最主要的目的是实现多台计算机之间的通信和资源共享。今天计算机网络中的设备和计算机网络的用户已经多得不可计数,而计算机网络也可以称得上是一个“复杂巨系统”,对于这样的系统,我们不可能用一两篇文章把它讲清楚,有兴趣的读者可以自行阅读Andrew S.Tanenbaum老师的经典之作《计算机网络》或Kurose和Ross老师合著的《计算机网络:自顶向下方法》来了解计算机网络的相关知识。 + +#### 计算机网络发展史 + +1. 1960s - 美国国防部ARPANET项目问世,奠定了分组交换网络的基础。 + + ![](./res/arpanet.png) + +2. 1980s - 国际标准化组织(ISO)发布OSI/RM,奠定了网络技术标准化的基础。 + + ![](./res/osimodel.png) + +3. 1990s - 英国人[蒂姆·伯纳斯-李](https://zh.wikipedia.org/wiki/%E6%8F%90%E5%A7%86%C2%B7%E6%9F%8F%E5%85%A7%E8%8C%B2-%E6%9D%8E)发明了图形化的浏览器,浏览器的简单易用性使得计算机网络迅速被普及。 + + 在没有浏览器的年代,上网是这样的。 + + ![](./res/before-browser.jpg) + + 有了浏览器以后,上网是这样的。 + + ![](./res/after-browser.jpg) + +#### TCP/IP模型 + +实现网络通信的基础是网络通信协议,这些协议通常是由[互联网工程任务组](https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%94%E7%BD%91%E5%B7%A5%E7%A8%8B%E4%BB%BB%E5%8A%A1%E7%BB%84) (IETF)制定的。所谓“协议”就是通信计算机双方必须共同遵从的一组约定,例如怎样建立连接、怎样互相识别等,网络协议的三要素是:语法、语义和时序。构成我们今天使用的Internet的基础的是TCP/IP协议族,所谓协议族就是一系列的协议及其构成的通信模型,我们通常也把这套东西称为TCP/IP模型。与国际标准化组织发布的OSI/RM这个七层模型不同,TCP/IP是一个四层模型,也就是说,该模型将我们使用的网络从逻辑上分解为四个层次,自底向上依次是:网络接口层、网络层、传输层和应用层,如下图所示。 + +![](./res/TCP-IP-model.png) + +IP通常被翻译为网际协议,它服务于网络层,主要实现了寻址和路由的功能。接入网络的每一台主机都需要有自己的IP地址,IP地址就是主机在计算机网络上的身份标识。当然由于IPv4地址的匮乏,我们平常在家里、办公室以及其他可以接入网络的公共区域上网时获得的IP地址并不是全球唯一的IP地址,而是一个[局域网(LAN)](https://zh.wikipedia.org/zh-hans/%E5%B1%80%E5%9F%9F%E7%BD%91)中的内部IP地址,通过[网络地址转换(NAT)服务](https://zh.wikipedia.org/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2)我们也可以实现对网络的访问。计算机网络上有大量的被我们称为“[路由器](https://zh.wikipedia.org/wiki/%E8%B7%AF%E7%94%B1%E5%99%A8)”的网络中继设备,它们会存储转发我们发送到网络上的数据分组,让从源头发出的数据最终能够找到传送到目的地通路,这项功能就是所谓的路由。 + +TCP全称传输控制协议,它是基于IP提供的寻址和路由服务而建立起来的负责实现端到端可靠传输的协议,之所以将TCP称为可靠的传输协议是因为TCP向调用者承诺了三件事情: + +1. 数据不传丢不传错(利用握手、校验和重传机制可以实现)。 +2. 流量控制(通过滑动窗口匹配数据发送者和接收者之间的传输速度)。 +3. 拥塞控制(通过RTT时间以及对滑动窗口的控制缓解网络拥堵)。 + +#### 网络应用模式 + +1. C/S模式和B/S模式。这里的C指的是Client(客户端),通常是一个需要安装到某个宿主操作系统上的应用程序;而B指的是Browser(浏览器),它几乎是所有图形化操作系统都默认安装了的一个应用软件;通过C或B都可以实现对S(服务器)的访问。关于二者的比较和讨论在网络上有一大堆的文章,在此我们就不再浪费笔墨了。 +2. 去中心化的网络应用模式。不管是B/S还是C/S都需要服务器的存在,服务器就是整个应用模式的中心,而去中心化的网络应用通常没有固定的服务器或者固定的客户端,所有应用的使用者既可以作为资源的提供者也可以作为资源的访问者。 + +### 基于HTTP协议的网络资源访问 + +#### HTTP(超文本传输协议) + +HTTP是超文本传输协议(Hyper-Text Transfer Proctol)的简称,维基百科上对HTTP的解释是:超文本传输协议是一种用于分布式、协作式和超媒体信息系统的应用层协议,它是[万维网](https://zh.wikipedia.org/wiki/%E5%85%A8%E7%90%83%E8%B3%87%E8%A8%8A%E7%B6%B2)数据通信的基础,设计HTTP最初的目的是为了提供一种发布和接收[HTML](https://zh.wikipedia.org/wiki/HTML)页面的方法,通过HTTP或者[HTTPS](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8%E5%8D%8F%E8%AE%AE)(超文本传输安全协议)请求的资源由URI([统一资源标识符](https://zh.wikipedia.org/wiki/%E7%B5%B1%E4%B8%80%E8%B3%87%E6%BA%90%E6%A8%99%E8%AD%98%E7%AC%A6))来标识。关于HTTP的更多内容,我们推荐阅读阮一峰老师的[《HTTP 协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html),简单的说,通过HTTP我们可以获取网络上的(基于字符的)资源,开发中经常会用到的网络API(有的地方也称之为网络数据接口)就是基于HTTP来实现数据传输的。 + +#### JSON格式 + +**JSON**(**J**ava**S**cript **O**bject **N**otation)是一种轻量级的数据交换语言,该语言以易于让人阅读的文字(纯文本)为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是最初只是Javascript中一种创建对象的字面量语法,但它在当下更是一种独立于语言的数据格式,很多编程语言都支持JSON格式数据的生成和解析,Python内置的json模块也提供了这方面的功能。由于JSON是纯文本,它和[XML](https://zh.wikipedia.org/wiki/XML)一样都适用于异构系统之间的数据交换,而相较于XML,JSON显得更加的轻便和优雅。下面是表达同样信息的XML和JSON,而JSON的优势是相当直观的。 + +XML的例子: + +```XML + + + Alice + Bob + Will you marry me? + +``` + +JSON的例子: + +```JSON +{ + "from": "Alice", + "to": "Bob", + "content": "Will you marry me?" +} +``` + +#### requests库 + +requests是一个基于HTTP协议来使用网络的第三库,其[官方网站](http://cn.python-requests.org/zh_CN/latest/)有这样的一句介绍它的话:“Requests是唯一的一个**非转基因**的Python HTTP库,人类可以安全享用。”简单的说,使用requests库可以非常方便的使用HTTP,避免安全缺陷、冗余代码以及“重复发明轮子”(行业黑话,通常用在软件工程领域表示重新创造一个已有的或是早已被优化過的基本方法)。前面的文章中我们已经使用过这个库,下面我们还是通过requests来实现一个访问网络数据接口并从中获取美女图片下载链接然后下载美女图片到本地的例子程序,程序中使用了[天行数据](https://www.tianapi.com/)提供的网络API。 + +我们可以先通过pip安装requests及其依赖库。 + +```Shell +pip install requests +``` + +如果使用PyCharm作为开发工具,可以直接在代码中书写`import requests`,然后通过代码修复功能来自动下载安装requests。 + +```Python +from time import time +from threading import Thread + +import requests + + +# 继承Thread类创建自定义的线程类 +class DownloadHanlder(Thread): + + def __init__(self, url): + super().__init__() + self.url = url + + def run(self): + filename = self.url[self.url.rfind('/') + 1:] + resp = requests.get(self.url) + with open('/Users/Hao/' + filename, 'wb') as f: + f.write(resp.content) + + +def main(): + # 通过requests模块的get函数获取网络资源 + # 下面的代码中使用了天行数据接口提供的网络API + # 要使用该数据接口需要在天行数据的网站上注册 + # 然后用自己的Key替换掉下面代码的中APIKey即可 + resp = requests.get( + 'http://api.tianapi.com/meinv/?key=APIKey&num=10') + # 将服务器返回的JSON格式的数据解析为字典 + data_model = resp.json() + for mm_dict in data_model['newslist']: + url = mm_dict['picUrl'] + # 通过多线程的方式实现图片下载 + DownloadHanlder(url).start() + + +if __name__ == '__main__': + main() +``` + +### 基于传输层协议的套接字编程 + +套接字这个词对很多不了解网络编程的人来说显得非常晦涩和陌生,其实说得通俗点,套接字就是一套用[C语言](https://zh.wikipedia.org/wiki/C%E8%AF%AD%E8%A8%80)写成的应用程序开发库,主要用于实现进程间通信和网络编程,在网络应用开发中被广泛使用。在Python中也可以基于套接字来使用传输层提供的传输服务,并基于此开发自己的网络应用。实际开发中使用的套接字可以分为三类:流套接字(TCP套接字)、数据报套接字和原始套接字。 + +#### TCP套接字 + +所谓TCP套接字就是使用TCP协议提供的传输服务来实现网络通信的编程接口。在Python中可以通过创建socket对象并指定type属性为SOCK_STREAM来使用TCP套接字。由于一台主机可能拥有多个IP地址,而且很有可能会配置多个不同的服务,所以作为服务器端的程序,需要在创建套接字对象后将其绑定到指定的IP地址和端口上。这里的端口并不是物理设备而是对IP地址的扩展,用于区分不同的服务,例如我们通常将HTTP服务跟80端口绑定,而MySQL数据库服务默认绑定在3306端口,这样当服务器收到用户请求时就可以根据端口号来确定到底用户请求的是HTTP服务器还是数据库服务器提供的服务。端口的取值范围是0~65535,而1024以下的端口我们通常称之为“著名端口”(留给像FTP、HTTP、SMTP等“著名服务”使用的端口,有的地方也称之为“周知端口”),自定义的服务通常不使用这些端口,除非自定义的是HTTP或FTP这样的著名服务。 + +下面的代码实现了一个提供时间日期的服务器。 + +```Python +from socket import socket, SOCK_STREAM, AF_INET +from datetime import datetime + + +def main(): + # 1.创建套接字对象并指定使用哪种传输服务 + # family=AF_INET - IPv4地址 + # family=AF_INET6 - IPv6地址 + # type=SOCK_STREAM - TCP套接字 + # type=SOCK_DGRAM - UDP套接字 + # type=SOCK_RAW - 原始套接字 + server = socket(family=AF_INET, type=SOCK_STREAM) + # 2.绑定IP地址和端口(端口用于区分不同的服务) + # 同一时间在同一个端口上只能绑定一个服务否则报错 + server.bind(('192.168.1.2', 6789)) + # 3.开启监听 - 监听客户端连接到服务器 + # 参数512可以理解为连接队列的大小 + server.listen(512) + print('服务器启动开始监听...') + while True: + # 4.通过循环接收客户端的连接并作出相应的处理(提供服务) + # accept方法是一个阻塞方法如果没有客户端连接到服务器代码不会向下执行 + # accept方法返回一个元组其中的第一个元素是客户端对象 + # 第二个元素是连接到服务器的客户端的地址(由IP和端口两部分构成) + client, addr = server.accept() + print(str(addr) + '连接到了服务器.') + # 5.发送数据 + client.send(str(datetime.now()).encode('utf-8')) + # 6.断开连接 + client.close() + + +if __name__ == '__main__': + main() +``` + +运行服务器程序后我们可以通过Windows系统的telnet来访问该服务器,结果如下图所示。 + +```Shell +telnet 192.168.1.2 6789 +``` + +![](./res/telnet.png) + +当然我们也可以通过Python的程序来实现TCP客户端的功能,相较于实现服务器程序,实现客户端程序就简单多了,代码如下所示。 + +```Python +from socket import socket + + +def main(): + # 1.创建套接字对象默认使用IPv4和TCP协议 + client = socket() + # 2.连接到服务器(需要指定IP地址和端口) + client.connect(('192.168.1.2', 6789)) + # 3.从服务器接收数据 + print(client.recv(1024).decode('utf-8')) + client.close() + + +if __name__ == '__main__': + main() +``` + +需要注意的是,上面的服务器并没有使用多线程或者异步I/O的处理方式,这也就意味着当服务器与一个客户端处于通信状态时,其他的客户端只能排队等待。很显然,这样的服务器并不能满足我们的需求,我们需要的服务器是能够同时接纳和处理多个用户请求的。下面我们来设计一个使用多线程技术处理多个用户请求的服务器,该服务器会向连接到服务器的客户端发送一张图片。 + +服务器端代码: + +```Python +from socket import socket, SOCK_STREAM, AF_INET +from base64 import b64encode +from json import dumps +from threading import Thread + + +def main(): + + # 自定义线程类 + class FileTransferHandler(Thread): + + def __init__(self, cclient): + super().__init__() + self.cclient = cclient + + def run(self): + my_dict = {} + my_dict['filename'] = 'guido.jpg' + # JSON是纯文本不能携带二进制数据 + # 所以图片的二进制数据要处理成base64编码 + my_dict['filedata'] = data + # 通过dumps函数将字典处理成JSON字符串 + json_str = dumps(my_dict) + # 发送JSON字符串 + self.cclient.send(json_str.encode('utf-8')) + self.cclient.close() + + # 1.创建套接字对象并指定使用哪种传输服务 + server = socket() + # 2.绑定IP地址和端口(区分不同的服务) + server.bind(('192.168.1.2', 5566)) + # 3.开启监听 - 监听客户端连接到服务器 + server.listen(512) + print('服务器启动开始监听...') + with open('guido.jpg', 'rb') as f: + # 将二进制数据处理成base64再解码成字符串 + data = b64encode(f.read()).decode('utf-8') + while True: + client, addr = server.accept() + # 启动一个线程来处理客户端的请求 + FileTransferHandler(client).start() + + +if __name__ == '__main__': + main() +``` + +客户端代码: + +```Python +from socket import socket +from json import loads +from base64 import b64decode + + +def main(): + client = socket() + client.connect(('192.168.1.2', 5566)) + # 定义一个保存二进制数据的对象 + in_data = bytes() + # 由于不知道服务器发送的数据有多大每次接收1024字节 + data = client.recv(1024) + while data: + # 将收到的数据拼接起来 + in_data += data + data = client.recv(1024) + # 将收到的二进制数据解码成JSON字符串并转换成字典 + # loads函数的作用就是将JSON字符串转成字典对象 + my_dict = loads(in_data.decode('utf-8')) + filename = my_dict['filename'] + filedata = my_dict['filedata'].encode('utf-8') + with open('/Users/Hao/' + filename, 'wb') as f: + # 将base64格式的数据解码成二进制数据并写入文件 + f.write(b64decode(filedata)) + print('图片已保存.') + + +if __name__ == '__main__': + main() +``` + +在这个案例中,我们使用了JSON作为数据传输的格式(通过JSON格式对传输的数据进行了序列化和反序列化的操作),但是JSON并不能携带二进制数据,因此对图片的二进制数据进行了Base64编码的处理。Base64是一种用64个字符表示所有二进制数据的编码方式,通过将二进制数据每6位一组的方式重新组织,刚好可以使用0~9的数字、大小写字母以及“+”和“/”总共64个字符表示从`000000`到`111111`的64种状态。[维基百科](https://zh.wikipedia.org/wiki/Base64)上有关于Base64编码的详细讲解,不熟悉Base64的读者可以自行阅读。 + +> **说明:** 上面的代码主要为了讲解网络编程的相关内容因此并没有对异常状况进行处理,请读者自行添加异常处理代码来增强程序的健壮性。 + +#### UDP套接字 + +传输层除了有可靠的传输协议TCP之外,还有一种非常轻便的传输协议叫做用户数据报协议,简称UDP。TCP和UDP都是提供端到端传输服务的协议,二者的差别就如同打电话和发短信的区别,后者不对传输的可靠性和可达性做出任何承诺从而避免了TCP中握手和重传的开销,所以在强调性能和而不是数据完整性的场景中(例如传输网络音视频数据),UDP可能是更好的选择。可能大家会注意到一个现象,就是在观看网络视频时,有时会出现卡顿,有时会出现花屏,这无非就是部分数据传丢或传错造成的。在Python中也可以使用UDP套接字来创建网络应用,对此我们不进行赘述,有兴趣的读者可以自行研究。 + +### 网络应用开发 + +#### 发送电子邮件 + +在即时通信软件如此发达的今天,电子邮件仍然是互联网上使用最为广泛的应用之一,公司向应聘者发出录用通知、网站向用户发送一个激活账号的链接、银行向客户推广它们的理财产品等几乎都是通过电子邮件来完成的,而这些任务应该都是由程序自动完成的。 + +就像我们可以用HTTP(超文本传输协议)来访问一个网站一样,发送邮件要使用SMTP(简单邮件传输协议),SMTP也是一个建立在TCP(传输控制协议)提供的可靠数据传输服务的基础上的应用级协议,它规定了邮件的发送者如何跟发送邮件的服务器进行通信的细节,而Python中的smtplib模块将这些操作简化成了几个简单的函数。 + +下面的代码演示了如何在Python发送邮件。 + +```Python +from smtplib import SMTP +from email.header import Header +from email.mime.text import MIMEText + + +def main(): + # 请自行修改下面的邮件发送者和接收者 + sender = 'abcdefg@126.com' + receivers = ['uvwxyz@qq.com', 'uvwxyz@126.com'] + message = MIMEText('用Python发送邮件的示例代码.', 'plain', 'utf-8') + message['From'] = Header('王大锤', 'utf-8') + message['To'] = Header('骆昊', 'utf-8') + message['Subject'] = Header('示例代码实验邮件', 'utf-8') + smtper = SMTP('smtp.126.com') + # 请自行修改下面的登录口令 + smtper.login(sender, 'secretpass') + smtper.sendmail(sender, receivers, message.as_string()) + print('邮件发送完成!') + + +if __name__ == '__main__': + main() +``` + +如果要发送带有附件的邮件,那么可以按照下面的方式进行操作。 + +```Python +from smtplib import SMTP +from email.header import Header +from email.mime.text import MIMEText +from email.mime.image import MIMEImage +from email.mime.multipart import MIMEMultipart + +import urllib + + +def main(): + # 创建一个带附件的邮件消息对象 + message = MIMEMultipart() + + # 创建文本内容 + text_content = MIMEText('附件中有本月数据请查收', 'plain', 'utf-8') + message['Subject'] = Header('本月数据', 'utf-8') + # 将文本内容添加到邮件消息对象中 + message.attach(text_content) + + # 读取文件并将文件作为附件添加到邮件消息对象中 + with open('/Users/Hao/Desktop/hello.txt', 'rb') as f: + txt = MIMEText(f.read(), 'base64', 'utf-8') + txt['Content-Type'] = 'text/plain' + txt['Content-Disposition'] = 'attachment; filename=hello.txt' + message.attach(txt) + # 读取文件并将文件作为附件添加到邮件消息对象中 + with open('/Users/Hao/Desktop/汇总数据.xlsx', 'rb') as f: + xls = MIMEText(f.read(), 'base64', 'utf-8') + xls['Content-Type'] = 'application/vnd.ms-excel' + xls['Content-Disposition'] = 'attachment; filename=month-data.xlsx' + message.attach(xls) + + # 创建SMTP对象 + smtper = SMTP('smtp.126.com') + # 开启安全连接 + # smtper.starttls() + sender = 'abcdefg@126.com' + receivers = ['uvwxyz@qq.com'] + # 登录到SMTP服务器 + # 请注意此处不是使用密码而是邮件客户端授权码进行登录 + # 对此有疑问的读者可以联系自己使用的邮件服务器客服 + smtper.login(sender, 'secretpass') + # 发送邮件 + smtper.sendmail(sender, receivers, message.as_string()) + # 与邮件服务器断开连接 + smtper.quit() + print('发送完成!') + + +if __name__ == '__main__': + main() +``` + +#### 发送短信 + +发送短信也是项目中常见的功能,网站的注册码、验证码、营销信息基本上都是通过短信来发送给用户的。在下面的代码中我们使用了[互亿无线](http://www.ihuyi.com/)短信平台(该平台为注册用户提供了50条免费短信以及常用开发语言发送短信的demo,可以登录该网站并在用户自服务页面中对短信进行配置)提供的API接口实现了发送短信的服务,当然国内的短信平台很多,读者可以根据自己的需要进行选择(通常会考虑费用预算、短信达到率、使用的难易程度等指标),如果需要在商业项目中使用短信服务建议购买短信平台提供的套餐服务。 + +```Python +import urllib.parse +import http.client +import json + + +def main(): + host = "106.ihuyi.com" + sms_send_uri = "/webservice/sms.php?method=Submit" + # 下面的参数需要填入自己注册的账号和对应的密码 + params = urllib.parse.urlencode({'account': '你自己的账号', 'password' : '你自己的密码', 'content': '您的验证码是:147258。请不要把验证码泄露给其他人。', 'mobile': '接收者的手机号', 'format':'json' }) + print(params) + headers = {'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'} + conn = http.client.HTTPConnection(host, port=80, timeout=30) + conn.request('POST', sms_send_uri, params, headers) + response = conn.getresponse() + response_str = response.read() + jsonstr = response_str.decode('utf-8') + print(json.loads(jsonstr)) + conn.close() + + +if __name__ == '__main__': + main() +``` \ No newline at end of file diff --git "a/Day01-15/15.\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243\345\244\204\347\220\206.md" "b/Day01-15/15.\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243\345\244\204\347\220\206.md" new file mode 100755 index 000000000..7ab5a80a9 --- /dev/null +++ "b/Day01-15/15.\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243\345\244\204\347\220\206.md" @@ -0,0 +1,179 @@ +## 图像和办公文档处理 + +用程序来处理图像和办公文档经常出现在实际开发中,Python的标准库中虽然没有直接支持这些操作的模块,但我们可以通过Python生态圈中的第三方模块来完成这些操作。 + +### 操作图像 + +#### 计算机图像相关知识 + +1. 颜色。如果你有使用颜料画画的经历,那么一定知道混合红、黄、蓝三种颜料可以得到其他的颜色,事实上这三种颜色就是被我们称为美术三原色的东西,它们是不能再分解的基本颜色。在计算机中,我们可以将红、绿、蓝三种色光以不同的比例叠加来组合成其他的颜色,因此这三种颜色就是色光三原色,所以我们通常会将一个颜色表示为一个RGB值或RGBA值(其中的A表示Alpha通道,它决定了透过这个图像的像素,也就是透明度)。 + + | 名称 | RGBA值 | 名称 | RGBA值 | + | :---: | :------------------: | :----: | :----------------: | + | White | (255, 255, 255, 255) | Red | (255, 0, 0, 255) | + | Green | (0, 255, 0, 255) | Blue | (0, 0, 255, 255) | + | Gray | (128, 128, 128, 255) | Yellow | (255, 255, 0, 255) | + | Black | (0, 0, 0, 255) | Purple | (128, 0, 128, 255) | + +2. 像素。对于一个由数字序列表示的图像来说,最小的单位就是图像上单一颜色的小方格,这些小方块都有一个明确的位置和被分配的色彩数值,而这些一小方格的颜色和位置决定了该图像最终呈现出来的样子,它们是不可分割的单位,我们通常称之为像素(pixel)。每一个图像都包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。 + +#### 用Pillow操作图像 + +Pillow是由从著名的Python图像处理库PIL发展出来的一个分支,通过Pillow可以实现图像压缩和图像处理等各种操作。可以使用下面的命令来安装Pillow。 + +```Shell +pip install pillow +``` + +Pillow中最为重要的是Image类,读取和处理图像都要通过这个类来完成。 + +```Python +>>> from PIL import Image +>>> +>>> image = Image.open('./res/guido.jpg') +>>> image.format, image.size, image.mode +('JPEG', (500, 750), 'RGB') +>>> image.show() +``` + +![](./res/image-show.png) + +1. 剪裁图像 + + ```Python + >>> image = Image.open('./res/guido.jpg') + >>> rect = 80, 20, 310, 360 + >>> image.crop(rect).show() + ``` + + ![](./res/image-crop.png) + +2. 生成缩略图 + + ```Python + >>> image = Image.open('./res/guido.jpg') + >>> size = 128, 128 + >>> image.thumbnail(size) + >>> image.show() + ``` + + ![](./res/image-thumbnail.png) + +3. 缩放和黏贴图像 + + ```Python + >>> image1 = Image.open('./res/luohao.png') + >>> image2 = Image.open('./res/guido.jpg') + >>> rect = 80, 20, 310, 360 + >>> guido_head = image2.crop(rect) + >>> width, height = guido_head.size + >>> image1.paste(guido_head.resize((int(width / 1.5), int(height / 1.5))), (172, 40)) + ``` + + ![](./res/image-paste.png) + +4. 旋转和翻转 + + ```Python + >>> image = Image.open('./res/guido.png') + >>> image.rotate(180).show() + >>> image.transpose(Image.FLIP_LEFT_RIGHT).show() + ``` + + ![](./res/image-rotate.png) + + ![](./res/image-transpose.png) + +5. 操作像素 + + ```Python + >>> image = Image.open('./res/guido.jpg') + >>> for x in range(80, 310): + ... for y in range(20, 360): + ... image.putpixel((x, y), (128, 128, 128)) + ... + >>> image.show() + ``` + + ![](./res/image-putpixel.png) + +6. 滤镜效果 + + ```Python + >>> from PIL import Image, ImageFilter + >>> + >>> image = Image.open('./res/guido.jpg') + >>> image.filter(ImageFilter.CONTOUR).show() + ``` + + ![](./res/image-filter.png) + +### 处理Excel电子表格 + +Python的openpyxl模块让我们可以在Python程序中读取和修改Excel电子表格,由于微软从Office 2007开始使用了新的文件格式,这使得Office Excel和LibreOffice Calc、OpenOffice Calc是完全兼容的,这就意味着openpyxl模块也能处理来自这些软件生成的电子表格。 + +```Python +import datetime + +from openpyxl import Workbook + +wb = Workbook() +ws = wb.active + +ws['A1'] = 42 +ws.append([1, 2, 3]) +ws['A2'] = datetime.datetime.now() + +wb.save("sample.xlsx") +``` + +### 处理Word文档 + +利用python-docx模块,Python可以创建和修改Word文档,当然这里的Word文档不仅仅是指通过微软的Office软件创建的扩展名为docx的文档,LibreOffice Writer和OpenOffice Writer都是免费的字处理软件。 + +```Python +from docx import Document +from docx.shared import Inches + +document = Document() + +document.add_heading('Document Title', 0) + +p = document.add_paragraph('A plain paragraph having some ') +p.add_run('bold').bold = True +p.add_run(' and some ') +p.add_run('italic.').italic = True + +document.add_heading('Heading, level 1', level=1) +document.add_paragraph('Intense quote', style='Intense Quote') + +document.add_paragraph( + 'first item in unordered list', style='List Bullet' +) +document.add_paragraph( + 'first item in ordered list', style='List Number' +) + +document.add_picture('monty-truth.png', width=Inches(1.25)) + +records = ( + (3, '101', 'Spam'), + (7, '422', 'Eggs'), + (4, '631', 'Spam, spam, eggs, and spam') +) + +table = document.add_table(rows=1, cols=3) +hdr_cells = table.rows[0].cells +hdr_cells[0].text = 'Qty' +hdr_cells[1].text = 'Id' +hdr_cells[2].text = 'Desc' +for qty, id, desc in records: + row_cells = table.add_row().cells + row_cells[0].text = str(qty) + row_cells[1].text = id + row_cells[2].text = desc + +document.add_page_break() + +document.save('demo.docx') +``` diff --git a/Day01-15/Day01/hello.py b/Day01-15/Day01/hello.py deleted file mode 100644 index b91e9d6fd..000000000 --- a/Day01-15/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-15/Day01/peppa_pig.py b/Day01-15/Day01/peppa_pig.py deleted file mode 100644 index d33858a22..000000000 --- a/Day01-15/Day01/peppa_pig.py +++ /dev/null @@ -1,193 +0,0 @@ -from turtle import* - - -def nose(x,y):#鼻子 - penup()#提起笔 - goto(x,y)#定位 - pendown()#落笔,开始画 - setheading(-30)#将乌龟的方向设置为to_angle/为数字(0-东、90-北、180-西、270-南) - begin_fill()#准备开始填充图形 - a=0.4 - for i in range(120): - if 0<=i<30 or 60<=i<90: - a=a+0.08 - left(3) #向左转3度 - forward(a) #向前走a的步长 - else: - a=a-0.08 - left(3) - forward(a) - end_fill()#填充完成 - - penup() - setheading(90) - forward(25) - setheading(0) - forward(10) - pendown() - pencolor(255,155,192)#画笔颜色 - setheading(10) - begin_fill() - circle(5) - color(160,82,45)#返回或设置pencolor和fillcolor - end_fill() - - penup() - setheading(0) - forward(20) - pendown() - pencolor(255,155,192) - setheading(10) - begin_fill() - circle(5) - color(160,82,45) - end_fill() - - -def head(x,y):#头 - color((255,155,192),"pink") - penup() - goto(x,y) - setheading(0) - pendown() - begin_fill() - setheading(180) - circle(300,-30) - circle(100,-60) - circle(80,-100) - circle(150,-20) - circle(60,-95) - setheading(161) - circle(-300,15) - penup() - goto(-100,100) - pendown() - setheading(-30) - a=0.4 - for i in range(60): - if 0<=i<30 or 60<=i<90: - a=a+0.08 - lt(3) #向左转3度 - fd(a) #向前走a的步长 - else: - a=a-0.08 - lt(3) - fd(a) - end_fill() - - -def ears(x,y): #耳朵 - color((255,155,192),"pink") - penup() - goto(x,y) - pendown() - begin_fill() - setheading(100) - circle(-50,50) - circle(-10,120) - circle(-50,54) - end_fill() - - penup() - setheading(90) - forward(-12) - setheading(0) - forward(30) - pendown() - begin_fill() - setheading(100) - circle(-50,50) - circle(-10,120) - circle(-50,56) - end_fill() - - -def eyes(x,y):#眼睛 - color((255,155,192),"white") - penup() - setheading(90) - forward(-20) - setheading(0) - forward(-95) - pendown() - begin_fill() - circle(15) - end_fill() - - color("black") - penup() - setheading(90) - forward(12) - setheading(0) - forward(-3) - pendown() - begin_fill() - circle(3) - end_fill() - - color((255,155,192),"white") - penup() - seth(90) - forward(-25) - seth(0) - forward(40) - pendown() - begin_fill() - circle(15) - end_fill() - - color("black") - penup() - setheading(90) - forward(12) - setheading(0) - forward(-3) - pendown() - begin_fill() - circle(3) - end_fill() - - -def cheek(x,y):#腮 - color((255,155,192)) - penup() - goto(x,y) - pendown() - setheading(0) - begin_fill() - circle(30) - end_fill() - - -def mouth(x,y): #嘴 - color(239,69,19) - penup() - goto(x,y) - pendown() - setheading(-80) - circle(30,40) - circle(40,80) - - -def setting(): #参数设置 - pensize(4) - hideturtle() #使乌龟无形(隐藏) - colormode(255) #将其设置为1.0或255.随后 颜色三元组的r,g,b值必须在0 .. cmode范围内 - color((255,155,192),"pink") - setup(840,500) - speed(10) - - -def main(): - setting() #画布、画笔设置 - nose(-100,100) #鼻子 - head(-69,167) #头 - ears(0,160) #耳朵 - eyes(0,140) #眼睛 - cheek(80,10) #腮 - mouth(-20,30) #嘴 - done() - - -if __name__ == '__main__': - main() diff --git a/Day01-15/Day01/res/python-idle.png b/Day01-15/Day01/res/python-idle.png deleted file mode 100644 index 1e90598fc..000000000 Binary files a/Day01-15/Day01/res/python-idle.png and /dev/null differ diff --git a/Day01-15/Day01/res/python-ipython.png b/Day01-15/Day01/res/python-ipython.png deleted file mode 100644 index c3a9054fb..000000000 Binary files a/Day01-15/Day01/res/python-ipython.png and /dev/null differ diff --git a/Day01-15/Day01/res/python-jupyter-1.png b/Day01-15/Day01/res/python-jupyter-1.png deleted file mode 100644 index 58cedc50e..000000000 Binary files a/Day01-15/Day01/res/python-jupyter-1.png and /dev/null differ diff --git a/Day01-15/Day01/res/python-jupyter-2.png b/Day01-15/Day01/res/python-jupyter-2.png deleted file mode 100644 index 27249889e..000000000 Binary files a/Day01-15/Day01/res/python-jupyter-2.png and /dev/null differ diff --git a/Day01-15/Day01/res/python-pycharm.png b/Day01-15/Day01/res/python-pycharm.png deleted file mode 100644 index ffae8c3f4..000000000 Binary files a/Day01-15/Day01/res/python-pycharm.png and /dev/null differ diff --git a/Day01-15/Day01/res/python-sublime.png b/Day01-15/Day01/res/python-sublime.png deleted file mode 100644 index c786d13c1..000000000 Binary files a/Day01-15/Day01/res/python-sublime.png and /dev/null differ diff --git "a/Day01-15/Day01/\345\210\235\350\257\206Python.md" "b/Day01-15/Day01/\345\210\235\350\257\206Python.md" deleted file mode 100644 index 44f048b8f..000000000 --- "a/Day01-15/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的用户体验并不是那么好所以很少在实际开发中被采用。 - -![](./res/python-idle.png) - -#### IPython - 更好的交互式编程工具 - -IPython是一种基于Python的交互式解释器。相较于原生的Python Shell,IPython提供了更为强大的编辑和交互功能。可以通过Python的包管理工具pip安装IPython和Jupyter,具体的操作如下所示。 - -```Shell -pip install ipython jupyter -``` - -或者 - -```Shell -python -m pip install ipython jupyter -``` - -安装成功后,可以通过下面的ipython命令启动IPython,如下图所示。 - -![](./res/python-ipython.png) - -当然我们也可以通过Jupyter运行名为notebook的项目在浏览器窗口中进行交互式操作。 - -```Shell -jupyter notebook -``` - -![](./res/python-jupyter-1.png) - -![](./res/python-jupyter-2.png) - -#### Sublime - 文本编辑神器 - -![](./res/python-sublime.png) - -- 首先可以通过[官方网站](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的安装、配置和使用我们在后面会进行介绍。 - -![](./res/python-pycharm.png) - -### 练习 - -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/Day01-15/Day02/circle.py b/Day01-15/Day02/circle.py deleted file mode 100644 index 020bf65e8..000000000 --- a/Day01-15/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/Day01-15/Day02/string.py b/Day01-15/Day02/string.py deleted file mode 100644 index bc60550c2..000000000 --- a/Day01-15/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/Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" "b/Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" deleted file mode 100644 index 87a3af640..000000000 --- "a/Day01-15/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/Day01-15/Day03/.py b/Day01-15/Day03/.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/Day01-15/Day03/convert.py b/Day01-15/Day03/convert.py deleted file mode 100644 index 2e2f587f9..000000000 --- a/Day01-15/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/Day01-15/Day03/grade.py b/Day01-15/Day03/grade.py deleted file mode 100644 index a48ecedba..000000000 --- a/Day01-15/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/Day01-15/Day03/piecewise.py b/Day01-15/Day03/piecewise.py deleted file mode 100644 index fc6bfbd9b..000000000 --- a/Day01-15/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/Day01-15/Day03/rolldice.py b/Day01-15/Day03/rolldice.py deleted file mode 100644 index 76373d0e8..000000000 --- a/Day01-15/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/Day01-15/Day03/tax.py b/Day01-15/Day03/tax.py deleted file mode 100644 index d404bff64..000000000 --- a/Day01-15/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/Day01-15/Day03/triangle.py b/Day01-15/Day03/triangle.py deleted file mode 100644 index 4c4c7a437..000000000 --- a/Day01-15/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/Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" "b/Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" deleted file mode 100644 index 23da1e84d..000000000 --- "a/Day01-15/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/Day01-15/Day04/for4.py b/Day01-15/Day04/for4.py deleted file mode 100644 index 0cd4e548f..000000000 --- a/Day01-15/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/Day01-15/Day04/for5.py b/Day01-15/Day04/for5.py deleted file mode 100644 index b4683fa09..000000000 --- a/Day01-15/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/Day01-15/Day04/while2.py b/Day01-15/Day04/while2.py deleted file mode 100644 index 1e7066cc8..000000000 --- a/Day01-15/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/Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" "b/Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" deleted file mode 100644 index af5ab166d..000000000 --- "a/Day01-15/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/Day01-15/Day05/chicken.py b/Day01-15/Day05/chicken.py deleted file mode 100644 index 639cb0078..000000000 --- a/Day01-15/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/Day01-15/Day05/craps.py b/Day01-15/Day05/craps.py deleted file mode 100644 index d0b66b897..000000000 --- a/Day01-15/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/Day01-15/Day05/fibonacci.py b/Day01-15/Day05/fibonacci.py deleted file mode 100644 index c6a72b9b6..000000000 --- a/Day01-15/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/Day01-15/Day05/guess.py b/Day01-15/Day05/guess.py deleted file mode 100644 index cd8880b60..000000000 --- a/Day01-15/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/Day01-15/Day05/lily.py b/Day01-15/Day05/lily.py deleted file mode 100644 index a33361919..000000000 --- a/Day01-15/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/Day01-15/Day05/palindrome.py b/Day01-15/Day05/palindrome.py deleted file mode 100644 index 02768f580..000000000 --- a/Day01-15/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/Day01-15/Day05/perfect.py b/Day01-15/Day05/perfect.py deleted file mode 100644 index b778e94eb..000000000 --- a/Day01-15/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/Day01-15/Day05/prime.py b/Day01-15/Day05/prime.py deleted file mode 100644 index b6d49a6ef..000000000 --- a/Day01-15/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/Day01-15/Day05/table.py b/Day01-15/Day05/table.py deleted file mode 100644 index 470caf1ed..000000000 --- a/Day01-15/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/Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" "b/Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" deleted file mode 100644 index e8f7ba89c..000000000 --- "a/Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" +++ /dev/null @@ -1 +0,0 @@ -## 总结和练习 diff --git a/Day01-15/Day06/function1.py b/Day01-15/Day06/function1.py deleted file mode 100644 index d1c92f4cb..000000000 --- a/Day01-15/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/Day01-15/Day06/function2.py b/Day01-15/Day06/function2.py deleted file mode 100644 index b2ed2838c..000000000 --- a/Day01-15/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/Day01-15/Day06/function3.py b/Day01-15/Day06/function3.py deleted file mode 100644 index 16a980fee..000000000 --- a/Day01-15/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/Day01-15/Day06/function4.py b/Day01-15/Day06/function4.py deleted file mode 100644 index 22d076fbb..000000000 --- a/Day01-15/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/Day01-15/Day06/function5.py b/Day01-15/Day06/function5.py deleted file mode 100644 index 90b618747..000000000 --- a/Day01-15/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/Day01-15/Day06/function6.py b/Day01-15/Day06/function6.py deleted file mode 100644 index 9ad898771..000000000 --- a/Day01-15/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/Day01-15/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/Day01-15/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/Day01-15/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/Day01-15/Day07/avgscore.py b/Day01-15/Day07/avgscore.py deleted file mode 100644 index 7c525ccc6..000000000 --- a/Day01-15/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/Day01-15/Day07/dict1.py b/Day01-15/Day07/dict1.py deleted file mode 100644 index 6d7b62a3b..000000000 --- a/Day01-15/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/Day01-15/Day07/dict2.py b/Day01-15/Day07/dict2.py deleted file mode 100644 index 9b3dea9fe..000000000 --- a/Day01-15/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/Day01-15/Day07/fibonacci.py b/Day01-15/Day07/fibonacci.py deleted file mode 100644 index 9dba36a53..000000000 --- a/Day01-15/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/Day01-15/Day07/findmax.py b/Day01-15/Day07/findmax.py deleted file mode 100644 index 30e59b0cd..000000000 --- a/Day01-15/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/Day01-15/Day07/list1.py b/Day01-15/Day07/list1.py deleted file mode 100644 index be7cdbc37..000000000 --- a/Day01-15/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/Day01-15/Day07/list2.py b/Day01-15/Day07/list2.py deleted file mode 100644 index 049afa488..000000000 --- a/Day01-15/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/Day01-15/Day07/list3.py b/Day01-15/Day07/list3.py deleted file mode 100644 index 74713486f..000000000 --- a/Day01-15/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/Day01-15/Day07/lottery.py b/Day01-15/Day07/lottery.py deleted file mode 100644 index 192051c55..000000000 --- a/Day01-15/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/Day01-15/Day07/marquee.py b/Day01-15/Day07/marquee.py deleted file mode 100644 index dc9750dd2..000000000 --- a/Day01-15/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/Day01-15/Day07/res/fibonacci-blocks.png b/Day01-15/Day07/res/fibonacci-blocks.png deleted file mode 100644 index a638bf123..000000000 Binary files a/Day01-15/Day07/res/fibonacci-blocks.png and /dev/null differ diff --git a/Day01-15/Day07/res/ipython-timeit.png b/Day01-15/Day07/res/ipython-timeit.png deleted file mode 100644 index 5b5238225..000000000 Binary files a/Day01-15/Day07/res/ipython-timeit.png and /dev/null differ diff --git a/Day01-15/Day07/res/python-set.png b/Day01-15/Day07/res/python-set.png deleted file mode 100644 index 2110598f3..000000000 Binary files a/Day01-15/Day07/res/python-set.png and /dev/null differ diff --git a/Day01-15/Day07/scoretable.py b/Day01-15/Day07/scoretable.py deleted file mode 100644 index 63fded25b..000000000 --- a/Day01-15/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/Day01-15/Day07/set2.py b/Day01-15/Day07/set2.py deleted file mode 100644 index 506588765..000000000 --- a/Day01-15/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/Day01-15/Day07/tic-tac-toe.py b/Day01-15/Day07/tic-tac-toe.py deleted file mode 100644 index 0db6620ed..000000000 --- a/Day01-15/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/Day01-15/Day07/tuple.py b/Day01-15/Day07/tuple.py deleted file mode 100644 index dcfaf2ffb..000000000 --- a/Day01-15/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/Day01-15/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/Day01-15/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/Day01-15/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})$$ - -![](./res/fibonacci-blocks.png) - -```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系统上测试的结果。 - -![](./res/ipython-timeit.png) - -### 使用集合 - -Python中的集合跟数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集等运算。 - -![](./res/python-set.png) - -```Python -def main(): - set1 = {1, 2, 3, 3, 3, 2} - print(set1) - print('Length =', len(set1)) - set2 = set(range(1, 10)) - print(set2) - set1.add(4) - set1.add(5) - set2.update([11, 12]) - print(set1) - print(set2) - set2.discard(5) - # remove的元素如果不存在会引发KeyError - if 4 in set2: - 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/Day01-15/Day08/access.py b/Day01-15/Day08/access.py deleted file mode 100644 index 8ed1558a9..000000000 --- a/Day01-15/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/Day01-15/Day08/circle.py b/Day01-15/Day08/circle.py deleted file mode 100644 index 7e0ddc02c..000000000 --- a/Day01-15/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/Day01-15/Day08/clock.py b/Day01-15/Day08/clock.py deleted file mode 100644 index ffdb7923b..000000000 --- a/Day01-15/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/Day01-15/Day08/guess.py b/Day01-15/Day08/guess.py deleted file mode 100644 index 095c52824..000000000 --- a/Day01-15/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/Day01-15/Day08/hack.py b/Day01-15/Day08/hack.py deleted file mode 100644 index 701f3a034..000000000 --- a/Day01-15/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/Day01-15/Day08/rect.py b/Day01-15/Day08/rect.py deleted file mode 100644 index 3698b2589..000000000 --- a/Day01-15/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/Day01-15/Day08/res/object-feature.png b/Day01-15/Day08/res/object-feature.png deleted file mode 100644 index 62c605762..000000000 Binary files a/Day01-15/Day08/res/object-feature.png and /dev/null differ diff --git a/Day01-15/Day08/res/oop-zhihu.png b/Day01-15/Day08/res/oop-zhihu.png deleted file mode 100644 index c473984a0..000000000 Binary files a/Day01-15/Day08/res/oop-zhihu.png and /dev/null differ diff --git a/Day01-15/Day08/student.py b/Day01-15/Day08/student.py deleted file mode 100644 index f5ac8552f..000000000 --- a/Day01-15/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/Day01-15/Day08/test.py b/Day01-15/Day08/test.py deleted file mode 100644 index e69de29bb..000000000 diff --git "a/Day01-15/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/Day01-15/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/Day01-15/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/)。 - -![](./res/oop-zhihu.png) - -> **说明**:以上的内容来自于网络,不代表作者本人的观点和看法,与作者本人立场无关,相关责任不由作者承担。(终于有机会享受一下把这段话反过来说的乐趣了,乐得牙都快碎了。) - -之前我们说过“程序是指令的集合”,我们在程序中书写的语句在执行时会变成一条或多条指令然后由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也不例外。 - -### 类和对象 - -简单的说,类是对象的蓝图和模板,而对象是类的实例。这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。 - -![](./res/object-feature.png) - -### 定义类 - -在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/Day01-15/Day09/association.py b/Day01-15/Day09/association.py deleted file mode 100644 index d614abe9d..000000000 --- a/Day01-15/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/Day01-15/Day09/car1.py b/Day01-15/Day09/car1.py deleted file mode 100644 index 813b4a89b..000000000 --- a/Day01-15/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/Day01-15/Day09/car2.py b/Day01-15/Day09/car2.py deleted file mode 100644 index c8d2b4b37..000000000 --- a/Day01-15/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/Day01-15/Day09/dependency.py b/Day01-15/Day09/dependency.py deleted file mode 100644 index b315fb296..000000000 --- a/Day01-15/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/Day01-15/Day09/diamond.py b/Day01-15/Day09/diamond.py deleted file mode 100644 index 97479203f..000000000 --- a/Day01-15/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/Day01-15/Day09/employee.py b/Day01-15/Day09/employee.py deleted file mode 100644 index 8e8628b85..000000000 --- a/Day01-15/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/Day01-15/Day09/multi.py b/Day01-15/Day09/multi.py deleted file mode 100644 index d24eb9ab8..000000000 --- a/Day01-15/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/Day01-15/Day09/rational.py b/Day01-15/Day09/rational.py deleted file mode 100644 index 4ff859638..000000000 --- a/Day01-15/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/Day01-15/Day09/res/uml-components.png b/Day01-15/Day09/res/uml-components.png deleted file mode 100644 index 0394cd7f2..000000000 Binary files a/Day01-15/Day09/res/uml-components.png and /dev/null differ diff --git a/Day01-15/Day09/res/uml-example.gliffy b/Day01-15/Day09/res/uml-example.gliffy deleted file mode 100644 index 015163a2f..000000000 --- a/Day01-15/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/Day01-15/Day09/res/uml-example.png b/Day01-15/Day09/res/uml-example.png deleted file mode 100644 index 14e8e9c37..000000000 Binary files a/Day01-15/Day09/res/uml-example.png and /dev/null differ diff --git a/Day01-15/Day09/shape.py b/Day01-15/Day09/shape.py deleted file mode 100644 index 8bd292c26..000000000 --- a/Day01-15/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/Day01-15/Day09/triangle.py b/Day01-15/Day09/triangle.py deleted file mode 100644 index d6feb762d..000000000 --- a/Day01-15/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/Day01-15/Day09/\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" "b/Day01-15/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/Day01-15/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)一书。 - -![](./res/uml-components.png) - -![](./res/uml-example.png) - -利用类之间的这些关系,我们可以在已有类的基础上来完成某些操作,也可以在已有类的基础上创建新的类,这些都是实现代码复用的重要手段。复用现有的代码不仅可以减少开发的工作量,也有利于代码的管理和维护,这是我们在日常工作中都会使用到的技术手段。 - -### 继承和多态 - -刚才我们提到了,可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为[里氏替换原则](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/Day01-15/Day10/gui1.py b/Day01-15/Day10/gui1.py deleted file mode 100644 index 6eb747bd8..000000000 --- a/Day01-15/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/Day01-15/Day10/gui2.py b/Day01-15/Day10/gui2.py deleted file mode 100644 index 69c81d0da..000000000 --- a/Day01-15/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('', mouse_evt_handler) -canvas.create_rectangle(0, 0, 600, 600, fill='yellow', outline='white') -for index in range(15): - canvas.create_line(20, 20 + 40 * index, 580, 20 + 40 * index, fill='black') - canvas.create_line(20 + 40 * index, 20, 20 + 40 * index, 580, fill='black') -canvas.create_rectangle(15, 15, 585, 585, outline='black', width=4) -canvas.pack() -tkinter.mainloop() - -# 请思考如何用面向对象的编程思想对上面的代码进行封装 diff --git a/Day01-15/Day10/res/ball-game.png b/Day01-15/Day10/res/ball-game.png deleted file mode 100644 index 37b9f890d..000000000 Binary files a/Day01-15/Day10/res/ball-game.png and /dev/null differ diff --git a/Day01-15/Day10/res/ball.png b/Day01-15/Day10/res/ball.png deleted file mode 100644 index 0a63bdcb4..000000000 Binary files a/Day01-15/Day10/res/ball.png and /dev/null differ diff --git "a/Day01-15/Day10/\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" "b/Day01-15/Day10/\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" deleted file mode 100644 index 729753331..000000000 --- "a/Day01-15/Day10/\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" +++ /dev/null @@ -1,338 +0,0 @@ -## 图形用户界面和游戏开发 - -### 基于tkinter模块的GUI - -GUI是图形用户界面的缩写,图形化的用户界面对使用过计算机的人来说应该都不陌生,在此也无需进行赘述。Python默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter),从这个名字就可以看出它是基于Tk的,Tk是一个工具包,最初是为Tcl设计的,后来被移植到很多其他的脚本语言中,它提供了跨平台的GUI控件。当然Tk并不是最新和最好的选择,也没有功能特别强大的GUI控件,事实上,开发GUI应用并不是Python最擅长的工作,如果真的需要使用Python开发GUI应用,wxPython、PyQt、PyGTK等模块都是不错的选择。 - -基本上使用tkinter来开发GUI应用需要以下5个步骤: - -1. 导入tkinter模块中我们需要的东西。 -2. 创建一个顶层窗口对象并用它来承载整个GUI应用。 -3. 在顶层窗口对象上添加GUI组件。 -4. 通过代码将这些GUI组件的功能组织起来。 -5. 进入主事件循环(main loop)。 - -下面的代码演示了如何使用tkinter做一个简单的GUI应用。 - -```Python -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) - # 创建按钮对象 指定添加到哪个容器中 通过command参数绑定事件回调函数 - 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() - -``` - -需要说明的是,GUI应用通常是事件驱动式的,之所以要进入主事件循环就是要监听鼠标、键盘等各种事件的发生并执行对应的代码对事件进行处理,因为事件会持续的发生,所以需要这样的一个循环一直运行着等待下一个事件的发生。另一方面,Tk为控件的摆放提供了三种布局管理器,通过布局管理器可以对控件进行定位,这三种布局管理器分别是:Placer(开发者提供控件的大小和摆放位置)、Packer(自动将控件填充到合适的位置)和Grid(基于网格坐标来摆放控件),此处不进行赘述。 - -### 使用Pygame进行游戏开发 - -Pygame是一个开源的Python模块,专门用于多媒体应用(如电子游戏)的开发,其中包含对图像、声音、视频、事件、碰撞等的支持。Pygame建立在[SDL](https://zh.wikipedia.org/wiki/SDL)的基础上,SDL是一套跨平台的多媒体开发库,用C语言实现,被广泛的应用于游戏、模拟器、播放器等的开发。而Pygame让游戏开发者不再被底层语言束缚,可以更多的关注游戏的功能和逻辑。 - -下面我们来完成一个简单的小游戏,游戏的名字叫“大球吃小球”,当然完成这个游戏并不是重点,学会使用Pygame也不是重点,最重要的我们要在这个过程中体会如何使用前面讲解的面向对象程序设计,学会用这种编程思想去解决现实中的问题。 - -#### 制作游戏窗口 - -```Python -import pygame - - -def main(): - # 初始化导入的pygame中的模块 - pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 - screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 - pygame.display.set_caption('大球吃小球') - running = True - # 开启一个事件循环处理发生的事件 - while running: - # 从消息队列中获取事件并对事件进行处理 - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - -if __name__ == '__main__': - main() - -``` - -#### 在窗口中绘图 - -可以通过pygame中draw模块的函数在窗口上绘图,可以绘制的图形包括:线条、矩形、多边形、圆、椭圆、圆弧等。需要说明的是,屏幕坐标系是将屏幕左上角设置为坐标原点`(0, 0)`,向右是x轴的正向,向下是y轴的正向,在表示位置或者设置尺寸的时候,我们默认的单位都是[像素](https://zh.wikipedia.org/wiki/%E5%83%8F%E7%B4%A0)。所谓像素就是屏幕上的一个点,你可以用浏览图片的软件试着将一张图片放大若干倍,就可以看到这些点。pygame中表示颜色用的是色光[三原色](https://zh.wikipedia.org/wiki/%E5%8E%9F%E8%89%B2)表示法,即通过一个元组或列表来指定颜色的RGB值,每个值都在0~255之间,因为是每种原色都用一个8位(bit)的值来表示,三种颜色相当于一共由24位构成,这也就是常说的“24位颜色表示法”。 - -```Python -import pygame - - -def main(): - # 初始化导入的pygame中的模块 - pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 - screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 - pygame.display.set_caption('大球吃小球') - # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组) - screen.fill((242, 242, 242)) - # 绘制一个圆(参数分别是: 屏幕, 颜色, 圆心位置, 半径, 0表示填充圆) - pygame.draw.circle(screen, (255, 0, 0,), (100, 100), 30, 0) - # 刷新当前窗口(渲染窗口将绘制的图像呈现出来) - pygame.display.flip() - running = True - # 开启一个事件循环处理发生的事件 - while running: - # 从消息队列中获取事件并对事件进行处理 - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - -if __name__ == '__main__': - main() - -``` - -####加载图像 - -如果需要直接加载图像到窗口上,可以使用pygame中image模块的函数来加载图像,再通过之前获得的窗口对象的`blit`方法渲染图像,代码如下所示。 - -```Python -import pygame - - -def main(): - # 初始化导入的pygame中的模块 - pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 - screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 - pygame.display.set_caption('大球吃小球') - # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组) - screen.fill((255, 255, 255)) - # 通过指定的文件名加载图像 - ball_image = pygame.image.load('./res/ball.png') - # 在窗口上渲染图像 - screen.blit(ball_image, (50, 50)) - # 刷新当前窗口(渲染窗口将绘制的图像呈现出来) - pygame.display.flip() - running = True - # 开启一个事件循环处理发生的事件 - while running: - # 从消息队列中获取事件并对事件进行处理 - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - -if __name__ == '__main__': - main() - -``` - -####实现动画效果 - -说到[动画](https://zh.wikipedia.org/wiki/%E5%8A%A8%E7%94%BB)这个词大家都不会陌生,事实上要实现动画效果,本身的原理也非常简单,就是将不连续的图片连续的播放,只要每秒钟达到了一定的帧数,那么就可以做出比较流畅的动画效果。如果要让上面代码中的小球动起来,可以将小球的位置用变量来表示,并在循环中修改小球的位置再刷新整个窗口即可。 - -```Python -import pygame - - -def main(): - # 初始化导入的pygame中的模块 - pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 - screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 - 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 - screen.fill((255, 255, 255)) - pygame.draw.circle(screen, (255, 0, 0,), (x, y), 30, 0) - pygame.display.flip() - # 每隔50毫秒就改变小球的位置再刷新窗口 - pygame.time.delay(50) - x, y = x + 5, y + 5 - - -if __name__ == '__main__': - main() - -``` - -#### 碰撞检测 - -通常一个游戏中会有很多对象出现,而这些对象之间的“碰撞”在所难免,比如炮弹击中了飞机、箱子撞到了地面等。碰撞检测在绝大多数的游戏中都是一个必须得处理的至关重要的问题,pygame的sprite(动画精灵)模块就提供了对碰撞检测的支持,这里我们暂时不介绍sprite模块提供的功能,因为要检测两个小球有没有碰撞其实非常简单,只需要检查球心的距离有没有小于两个球的半径之和。为了制造出更多的小球,我们可以通过对鼠标事件的处理,在点击鼠标的位置创建颜色、大小和移动速度都随机的小球,当然要做到这一点,我们可以把之前学习到的面向对象的知识应用起来。 - -```Python -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 - a 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) - -``` - -#### 事件处理 - -可以在事件循环中对鼠标事件进行处理,通过事件对象的`type`属性可以判定事件类型,再通过`pos`属性就可以获得鼠标点击的位置。如果要处理键盘事件也是在这个地方,做法与处理鼠标事件类似。 - -```Python -def main(): - # 定义用来装所有球的容器 - balls = [] - # 初始化导入的pygame中的模块 - pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 - screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 - pygame.display.set_caption('大球吃小球') - 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() - -``` - -上面的两段代码合在一起,我们就完成了“大球吃小球”的游戏(如下图所示),准确的说它算不上一个游戏,但是做一个小游戏的基本知识我们已经通过这个例子告诉大家了,有了这些知识已经可以开始你的小游戏开发之旅了。其实上面的代码中还有很多值得改进的地方,比如刷新窗口以及让球移动起来的代码并不应该放在事件循环中,等学习了多线程的知识后,用一个后台线程来处理这些事可能是更好的选择。如果希望获得更好的用户体验,我们还可以在游戏中加入背景音乐以及在球与球发生碰撞时播放音效,利用pygame的mixer和music模块,我们可以很容易的做到这一点,大家可以自行了解这方面的知识。事实上,想了解更多的关于pygame的知识,最好的教程是[pygame的官方网站](https://www.pygame.org/news),如果英语没毛病就可以赶紧去看看啦。 如果想开发[3D游戏](https://zh.wikipedia.org/wiki/3D%E6%B8%B8%E6%88%8F),pygame就显得力不从心了,对3D游戏开发如果有兴趣的读者不妨看看[Panda3D](https://www.panda3d.org/)。 - - - diff --git a/Day01-15/Day11/.py b/Day01-15/Day11/.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/Day01-15/Day11/csv1.py b/Day01-15/Day11/csv1.py deleted file mode 100644 index 585e42477..000000000 --- a/Day01-15/Day11/csv1.py +++ /dev/null @@ -1,23 +0,0 @@ -""" - -读取CSV文件 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -import csv - -filename = 'example.csv' - -try: - with open(filename) as f: - reader = csv.reader(f) - data = list(reader) -except FileNotFoundError: - print('无法打开文件:', filename) -else: - for item in data: - print('%-30s%-20s%-10s' % (item[0], item[1], item[2])) diff --git a/Day01-15/Day11/csv2.py b/Day01-15/Day11/csv2.py deleted file mode 100644 index 7a5a55568..000000000 --- a/Day01-15/Day11/csv2.py +++ /dev/null @@ -1,46 +0,0 @@ -""" - -写入CSV文件 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -import csv - - -class Teacher(object): - - def __init__(self, name, age, title): - self.__name = name - self.__age = age - self.__title = title - self.__index = -1 - - @property - def name(self): - return self.__name - - @property - def age(self): - return self.__age - - @property - def title(self): - return self.__title - - -filename = 'teacher.csv' -teachers = [Teacher('骆昊', 38, '叫兽'), Teacher('狄仁杰', 25, '砖家')] - -try: - with open(filename, 'w') as f: - writer = csv.writer(f) - for teacher in teachers: - writer.writerow([teacher.name, teacher.age, teacher.title]) -except BaseException as e: - print('无法写入文件:', filename) -else: - print('保存数据完成!') diff --git a/Day01-15/Day11/ex1.py b/Day01-15/Day11/ex1.py deleted file mode 100644 index 71c1cb58e..000000000 --- a/Day01-15/Day11/ex1.py +++ /dev/null @@ -1,23 +0,0 @@ -""" - -异常机制 - 处理程序在运行时可能发生的状态 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -input_again = True -while input_again: - try: - a = int(input('a = ')) - b = int(input('b = ')) - print('%d / %d = %f' % (a, b, a / b)) - input_again = False - except ValueError: - print('请输入整数') - except ZeroDivisionError: - print('除数不能为0') -# 处理异常让代码不因异常而崩溃是一方面 -# 更重要的是可以通过对异常的处理让代码从异常中恢复过来 diff --git a/Day01-15/Day11/ex2.py b/Day01-15/Day11/ex2.py deleted file mode 100644 index 6f1e9841d..000000000 --- a/Day01-15/Day11/ex2.py +++ /dev/null @@ -1,19 +0,0 @@ -""" - -异常机制 - 处理程序在运行时可能发生的状态 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -input_again = True -while input_again: - try: - a = int(input('a = ')) - b = int(input('b = ')) - print('%d / %d = %f' % (a, b, a / b)) - input_again = False - except (ValueError, ZeroDivisionError) as msg: - print(msg) diff --git a/Day01-15/Day11/ex3.py b/Day01-15/Day11/ex3.py deleted file mode 100644 index 34b3f609b..000000000 --- a/Day01-15/Day11/ex3.py +++ /dev/null @@ -1,30 +0,0 @@ -""" - -异常机制 - 处理程序在运行时可能发生的状态 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -import time -import sys - -filename = input('请输入文件名: ') -try: - with open(filename) as f: - lines = f.readlines() -except FileNotFoundError as msg: - print('无法打开文件:', filename) - print(msg) -except UnicodeDecodeError as msg: - print('非文本文件无法解码') - sys.exit() -else: - for line in lines: - print(line.rstrip()) - time.sleep(0.5) -finally: - # 此处最适合做善后工作 - print('不管发生什么我都会执行') diff --git a/Day01-15/Day11/ex4.py b/Day01-15/Day11/ex4.py deleted file mode 100644 index 532626971..000000000 --- a/Day01-15/Day11/ex4.py +++ /dev/null @@ -1,24 +0,0 @@ -""" - -引发异常和异常栈 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - - -def f1(): - raise AssertionError('发生异常') - - -def f2(): - f1() - - -def f3(): - f2() - - -f3() diff --git a/Day01-15/Day11/file1.py b/Day01-15/Day11/file1.py deleted file mode 100644 index 3e57a6398..000000000 --- a/Day01-15/Day11/file1.py +++ /dev/null @@ -1,33 +0,0 @@ -""" - -从文本文件中读取数据 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -import time - - -def main(): - # 一次性读取整个文件内容 - with open('致橡树.txt', 'r', encoding='utf-8') as f: - print(f.read()) - - # 通过for-in循环逐行读取 - with open('致橡树.txt', mode='r') as f: - for line in f: - print(line, end='') - time.sleep(0.5) - print() - - # 读取文件按行读取到列表中 - with open('致橡树.txt') as f: - lines = f.readlines() - print(lines) - - -if __name__ == '__main__': - main() diff --git a/Day01-15/Day11/file2.py b/Day01-15/Day11/file2.py deleted file mode 100644 index e3a2d0ab1..000000000 --- a/Day01-15/Day11/file2.py +++ /dev/null @@ -1,18 +0,0 @@ -""" - -读取圆周率文件判断其中是否包含自己的生日 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -birth = input('请输入你的生日: ') -with open('pi_million_digits.txt') as f: - lines = f.readlines() - pi_string = '' - for line in lines: - pi_string += line.strip() - if birth in pi_string: - print('Bingo!!!') diff --git a/Day01-15/Day11/file3.py b/Day01-15/Day11/file3.py deleted file mode 100644 index 0c1cb03d2..000000000 --- a/Day01-15/Day11/file3.py +++ /dev/null @@ -1,28 +0,0 @@ -""" - -写文本文件 -将100以内的素数写入到文件中 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -from math import sqrt - - -def is_prime(n): - for factor in range(2, int(sqrt(n)) + 1): - if n % factor == 0: - return False - return True - - -# 试一试有什么不一样 -# with open('prime.txt', 'a') as f: -with open('prime.txt', 'w') as f: - for num in range(2, 100): - if is_prime(num): - f.write(str(num) + '\n') -print('写入完成!') diff --git a/Day01-15/Day11/file4.py b/Day01-15/Day11/file4.py deleted file mode 100644 index dc7096ca9..000000000 --- a/Day01-15/Day11/file4.py +++ /dev/null @@ -1,23 +0,0 @@ -""" - -读写二进制文件 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -import base64 - -with open('mm.jpg', 'rb') as f: - data = f.read() - # print(type(data)) - # print(data) - print('字节数:', len(data)) - # 将图片处理成BASE-64编码 - print(base64.b64encode(data)) - -with open('girl.jpg', 'wb') as f: - f.write(data) -print('写入完成!') diff --git a/Day01-15/Day11/json1.py b/Day01-15/Day11/json1.py deleted file mode 100644 index 6223bc0ed..000000000 --- a/Day01-15/Day11/json1.py +++ /dev/null @@ -1,87 +0,0 @@ -""" - -读取JSON数据 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-13 - -""" - -import json -import csv2 - -json_str = '{"name": "骆昊", "age": 38, "title": "叫兽"}' -result = json.loads(json_str) -print(result) -print(type(result)) -print(result['name']) -print(result['age']) - -# 把转换得到的字典作为关键字参数传入Teacher的构造器 -teacher = csv2.Teacher(**result) -print(teacher) -print(teacher.name) -print(teacher.age) -print(teacher.title) - -# 请思考如何将下面JSON格式的天气数据转换成对象并获取我们需要的信息 -# 稍后我们会讲解如何通过网络API获取我们需要的JSON格式的数据 -""" - { - "wendu": "29", - "ganmao": "各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。", - "forecast": [ - { - "fengxiang": "南风", - "fengli": "3-4级", - "high": "高温 32℃", - "type": "多云", - "low": "低温 17℃", - "date": "16日星期二" - }, - { - "fengxiang": "南风", - "fengli": "微风级", - "high": "高温 34℃", - "type": "晴", - "low": "低温 19℃", - "date": "17日星期三" - }, - { - "fengxiang": "南风", - "fengli": "微风级", - "high": "高温 35℃", - "type": "晴", - "low": "低温 22℃", - "date": "18日星期四" - }, - { - "fengxiang": "南风", - "fengli": "微风级", - "high": "高温 35℃", - "type": "多云", - "low": "低温 22℃", - "date": "19日星期五" - }, - { - "fengxiang": "南风", - "fengli": "3-4级", - "high": "高温 34℃", - "type": "晴", - "low": "低温 21℃", - "date": "20日星期六" - } - ], - "yesterday": { - "fl": "微风", - "fx": "南风", - "high": "高温 28℃", - "type": "晴", - "low": "低温 15℃", - "date": "15日星期一" - }, - "aqi": "72", - "city": "北京" - } -""" diff --git a/Day01-15/Day11/mm.jpg b/Day01-15/Day11/mm.jpg deleted file mode 100644 index 882abf2f0..000000000 Binary files a/Day01-15/Day11/mm.jpg and /dev/null differ diff --git a/Day01-15/Day11/res/file-open-mode.png b/Day01-15/Day11/res/file-open-mode.png deleted file mode 100644 index 2d01ad486..000000000 Binary files a/Day01-15/Day11/res/file-open-mode.png and /dev/null differ diff --git "a/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" "b/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" deleted file mode 100644 index 3fa4e96af..000000000 --- "a/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" +++ /dev/null @@ -1,274 +0,0 @@ -## 文件和异常 - -在实际开发中,常常需要对程序中的数据进行[持久化](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96)操作,而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词,可能需要先科普一下关于[文件系统](https://zh.wikipedia.org/wiki/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F)的知识,对于这个概念,维基百科上给出了很好的诠释,这里不再浪费笔墨。 - -在Python中实现文件的读写操作其实非常简单,通过Python内置的`open`函数,我们可以指定文件名、操作模式、编码信息等来获得操作文件的对象,接下来就可以对文件进行读写操作了。这里所说的操作模式是指要打开什么样的文件(字符文件还是二进制文件)以及做什么样的操作(读、写还是追加),具体的如下表所示。 - -| 操作模式 | 具体含义 | -| -------- | -------------------------------- | -| `'r'` | 读取 (默认) | -| `'w'` | 写入(会先截断之前的内容) | -| `'x'` | 写入,如果文件已经存在会产生异常 | -| `'a'` | 追加,将内容写入到已有文件的末尾 | -| `'b'` | 二进制模式 | -| `'t'` | 文本模式(默认) | -| `'+'` | 更新(既可以读又可以写) | - -下面这张图来自于[菜鸟教程](http://www.runoob.com)网站,它展示了如果根据应用程序的需要来设置操作模式。 - -![](./res/file-open-mode.png) - -### 读写文本文件 - -读取文本文件时,需要在使用`open`函数时指定好带路径的文件名(可以使用相对路径或绝对路径)并将文件模式设置为`'r'`(如果不指定,默认值也是`'r'`),然后通过`encoding`参数指定编码(如果不指定,默认值是None,那么在读取文件时使用的是操作系统默认的编码),如果不能保证保存文件时使用的编码方式与encoding参数指定的编码方式是一致的,那么就可能因无法解码字符而导致读取失败。下面的例子演示了如何读取一个纯文本文件。 - -```Python -def main(): - f = open('致橡树.txt', 'r', encoding='utf-8') - print(f.read()) - f.close() - - -if __name__ == '__main__': - main() - -``` - -请注意上面的代码,如果`open`函数指定的文件并不存在或者无法打开,那么将引发异常状况导致程序崩溃。为了让代码有一定的健壮性和容错性,我们可以使用Python的异常机制对可能在运行时发生状况的代码进行适当的处理,如下所示。 - -```Python -def main(): - f = None - try: - f = open('致橡树.txt', 'r', encoding='utf-8') - print(f.read()) - except FileNotFoundError: - print('无法打开指定的文件!') - except LookupError: - print('指定了未知的编码!') - except UnicodeDecodeError: - print('读取文件时解码错误!') - finally: - if f: - f.close() - - -if __name__ == '__main__': - main() - -``` - -在Python中,我们可以将那些在运行时可能会出现状况的代码放在`try`代码块中,在`try`代码块的后面可以跟上一个或多个`except`来捕获可能出现的异常状况。例如在上面读取文件的过程中,文件找不到会引发`FileNotFoundError`,指定了未知的编码会引发`LookupError`,而如果读取文件时无法按指定方式解码会引发`UnicodeDecodeError`,我们在`try`后面跟上了三个`except`分别处理这三种不同的异常状况。最后我们使用`finally`代码块来关闭打开的文件,释放掉程序中获取的外部资源,由于`finally`块的代码不论程序正常还是异常都会执行到(甚至是调用了`sys`模块的`exit`函数退出Python环境,`finally`块都会被执行,因为`exit`函数实质上是引发了`SystemExit`异常),因此我们通常把`finally`块称为“总是执行代码块”,它最适合用来做释放外部资源的操作。如果不愿意在`finally`代码块中关闭文件对象释放资源,也可以使用上下文语法,通过`with`关键字指定文件对象的上下文环境并在离开上下文环境时自动释放文件资源,代码如下所示。 - -```Python -def main(): - try: - with open('致橡树.txt', 'r', encoding='utf-8') as f: - print(f.read()) - except FileNotFoundError: - print('无法打开指定的文件!') - except LookupError: - print('指定了未知的编码!') - except UnicodeDecodeError: - print('读取文件时解码错误!') - - -if __name__ == '__main__': - main() - -``` - -除了使用文件对象的`read`方法读取文件之外,还可以使用`for-in`循环逐行读取或者用`readlines`方法将文件按行读取到一个列表容器中,代码如下所示。 - -```Python -import time - - -def main(): - # 一次性读取整个文件内容 - with open('致橡树.txt', 'r', encoding='utf-8') as f: - print(f.read()) - - # 通过for-in循环逐行读取 - with open('致橡树.txt', mode='r') as f: - for line in f: - print(line, end='') - time.sleep(0.5) - print() - - # 读取文件按行读取到列表中 - with open('致橡树.txt') as f: - lines = f.readlines() - print(lines) - - -if __name__ == '__main__': - main() - -``` - -要将文本信息写入文件文件也非常简单,在使用`open`函数时指定好文件名并将文件模式设置为`'w'`即可。注意如果需要对文件内容进行追加式写入,应该将模式设置为`'a'`。如果要写入的文件不存在会自动创建文件而不是引发异常。下面的例子演示了如何将1~9999直接的素数分别写入三个文件中(1~99之间的素数保存在a.txt中,100~999之间的素数保存在b.txt中,1000~9999之间的素数保存在c.txt中)。 - -```Python -from math import sqrt - - -def is_prime(n): - """判断素数的函数""" - assert n > 0 - for factor in range(2, int(sqrt(n)) + 1): - if n % factor == 0: - return False - return True if n != 1 else False - - -def main(): - filenames = ('a.txt', 'b.txt', 'c.txt') - fs_list = [] - try: - for filename in filenames: - fs_list.append(open(filename, 'w', encoding='utf-8')) - for number in range(1, 10000): - if is_prime(number): - if number < 100: - fs_list[0].write(str(number) + '\n') - elif number < 1000: - fs_list[1].write(str(number) + '\n') - else: - fs_list[2].write(str(number) + '\n') - except IOError as ex: - print(ex) - print('写文件时发生错误!') - finally: - for fs in fs_list: - fs.close() - print('操作完成!') - - -if __name__ == '__main__': - main() - -``` - -### 读写二进制文件 - -知道了如何读写文本文件要读写二进制文件也就很简单了,下面的代码实现了复制图片文件的功能。 - -```Python -def main(): - try: - with open('guido.jpg', 'rb') as fs1: - data = fs1.read() - print(type(data)) # - with open('吉多.jpg', 'wb') as fs2: - fs2.write(data) - except FileNotFoundError as e: - print('指定的文件无法打开.') - except IOError as e: - print('读写文件时出现错误.') - print('程序执行结束.') - - -if __name__ == '__main__': - main() - -``` - -### 读写JSON文件 - -通过上面的讲解,我们已经知道如何将文本数据和二进制数据保存到文件中,那么这里还有一个问题,如果希望把一个列表或者一个字典中的数据保存到文件中又该怎么做呢?答案是将数据以JSON格式进行保存。JSON是“JavaScript Object Notation”的缩写,它本来是JavaScript语言中创建对象的一种字面量语法,现在已经被广泛的应用于跨平台跨语言的数据交换,原因很简单,因为JSON也是纯文本,任何系统任何编程语言处理纯文本都是没有问题的。目前JSON基本上已经取代了XML作为异构系统间交换数据的事实标准。关于JSON的知识,更多的可以参考[JSON的官方网站](http://json.org),从这个网站也可以了解到每种语言处理JSON数据格式可以使用的工具或三方库,下面是一个JSON的简单例子。 - -```JSON -{ - 'name': '骆昊', - 'age': 38, - 'qq': 957658, - 'friends': ['王大锤', '白元芳'], - 'cars': [ - {'brand': 'BYD', 'max_speed': 180}, - {'brand': 'Audi', 'max_speed': 280}, - {'brand': 'Benz', 'max_speed': 320} - ] -} -``` - -可能大家已经注意到了,上面的JSON跟Python中的字典其实是一样一样的,事实上JSON的数据类型和Python的数据类型是很容易找到对应关系的,如下面两张表所示。 - -| JSON | Python | -| ------------------- | ------------ | -| object | dict | -| array | list | -| string | str | -| number (int / real) | int / float | -| true / false | True / False | -| null | None | - -| Python | JSON | -| -------------------------------------- | ------------ | -| dict | object | -| list, tuple | array | -| str | string | -| int, float, int- & float-derived Enums | number | -| True / False | true / false | -| None | null | - -我们使用Python中的json模块就可以将字典或列表以JSON格式保存到文件中,代码如下所示。 - -```Python -import json - - -def main(): - mydict = { - 'name': '骆昊', - 'age': 38, - 'qq': 957658, - 'friends': ['王大锤', '白元芳'], - 'cars': [ - {'brand': 'BYD', 'max_speed': 180}, - {'brand': 'Audi', 'max_speed': 280}, - {'brand': 'Benz', 'max_speed': 320} - ] - } - try: - with open('data.json', 'w', encoding='utf-8') as fs: - json.dump(mydict, fs) - except IOError as e: - print(e) - print('保存数据完成!') - - -if __name__ == '__main__': - main() - -``` - -json模块主要有四个比较重要的函数,分别是: - -- dump - 将Python对象按照JSON格式序列化到文件中 -- dumps - 将Python对象处理成JSON格式的字符串 -- load - 将文件中的JSON数据反序列化成对象 -- loads - 将字符串的内容反序列化成Python对象 - -这里出现了两个概念,一个叫序列化,一个叫反序列化。自由的百科全书[维基百科](https://zh.wikipedia.org/)上对这两个概念是这样解释的:“序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换为可以存储或传输的形式,这样在需要的时候能够恢复到原先的状态,而且通过序列化的数据重新获取字节时,可以利用这些字节来产生原始对象的副本(拷贝)。与这个过程相反的动作,即从一系列字节中提取数据结构的操作,就是反序列化(deserialization)”。 - -目前绝大多数网络数据服务(或称之为网络API)都是基于[HTTP协议](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE)提供JSON格式的数据,关于HTTP协议的相关知识,可以看看阮一峰老师的[《HTTP协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html),如果想了解国内的网络数据服务,可以看看[聚合数据](https://www.juhe.cn/)和[阿凡达数据](http://www.avatardata.cn/)等网站,国外的可以看看[{API}Search](http://apis.io/)网站。下面的例子演示了如何使用requests模块(封装得足够好的第三方网络访问模块)访问网络API获取国内新闻,如何通过json模块解析JSON数据并显示新闻标题,这个例子使用了[天行数据](https://www.tianapi.com/)提供的国内新闻数据接口,其中的APIKey需要自己到该网站申请。 - -```Python -import requests -import json - - -def main(): - resp = requests.get('http://api.tianapi.com/guonei/?key=APIKey&num=10') - data_model = json.loads(resp.text) - for news in data_model['newslist']: - print(news['title']) - - -if __name__ == '__main__': - main() - -``` - -在Python中要实现序列化和反序列化除了使用json模块之外,还可以使用pickle和shelve模块,但是这两个模块是使用特有的序列化协议来序列化数据,因此序列化后的数据只能被Python识别。关于这两个模块的相关知识可以自己看看网络上的资料。另外,如果要了解更多的关于Python异常机制的知识,可以看看segmentfault上面的文章[《总结:Python中的异常处理》](https://segmentfault.com/a/1190000007736783),这篇文章不仅介绍了Python中异常机制的使用,还总结了一系列的最佳实践,很值得一读。 \ No newline at end of file diff --git "a/Day01-15/Day11/\350\207\264\346\251\241\346\240\221.txt" "b/Day01-15/Day11/\350\207\264\346\251\241\346\240\221.txt" deleted file mode 100644 index 8380e7867..000000000 --- "a/Day01-15/Day11/\350\207\264\346\251\241\346\240\221.txt" +++ /dev/null @@ -1,7 +0,0 @@ -我如果爱你 -绝不学攀援的凌霄花 -借你的高枝炫耀自己 - -我如果爱你 -绝不学痴情的鸟儿 -为绿荫重复单调的歌曲 diff --git a/Day01-15/Day12/res/tel-start-number.png b/Day01-15/Day12/res/tel-start-number.png deleted file mode 100644 index b1be7a90a..000000000 Binary files a/Day01-15/Day12/res/tel-start-number.png and /dev/null differ diff --git a/Day01-15/Day12/str2.py b/Day01-15/Day12/str2.py deleted file mode 100644 index 23b171216..000000000 --- a/Day01-15/Day12/str2.py +++ /dev/null @@ -1,62 +0,0 @@ -""" - -字符串常用操作 - 实现字符串倒转的方法 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-19 - -""" - -from io import StringIO - - -def reverse_str1(str): - return str[::-1] - - -def reverse_str2(str): - if len(str) <= 1: - return str - return reverse_str2(str[1:]) + str[0:1] - - -def reverse_str3(str): - # StringIO对象是Python中的可变字符串 - # 不应该使用不变字符串做字符串连接操作 因为会产生很多无用字符串对象 - rstr = StringIO() - str_len = len(str) - for index in range(str_len - 1, -1, -1): - rstr.write(str[index]) - return rstr.getvalue() - - -def reverse_str4(str): - return ''.join(str[index] for index in range(len(str) - 1, -1, -1)) - - -def reverse_str5(str): - # 将字符串处理成列表 - str_list = list(str) - str_len = len(str) - # 使用zip函数将两个序列合并成一个产生元组的迭代器 - # 每次正好可以取到一前一后两个下标来实现元素的交换 - for i, j in zip(range(str_len // 2), range(str_len - 1, str_len // 2, -1)): - str_list[i], str_list[j] = str_list[j], str_list[i] - # 将列表元素连接成字符串 - return ''.join(str_list) - - -if __name__ == '__main__': - str = 'I love Python' - print(reverse_str1(str)) - print(str) - print(reverse_str2(str)) - print(str) - print(reverse_str3(str)) - print(str) - print(reverse_str4(str)) - print(str) - print(reverse_str5(str)) - print(str) -# 提醒学生注意这是一个面试题: 写出你能想到的实现字符串倒转的代码 diff --git a/Day01-15/Day12/test5.py b/Day01-15/Day12/test5.py deleted file mode 100644 index a62173ae5..000000000 --- a/Day01-15/Day12/test5.py +++ /dev/null @@ -1,12 +0,0 @@ -import re - - -def main(): - sentence = '你丫是傻叉吗? 我操你大爷的. Fuck you.' - purified = re.sub('[操肏艹草曹]|fuck|shit|傻[比屄逼叉缺吊屌]|煞笔', - '*', sentence, flags=re.IGNORECASE) - print(purified) - - -if __name__ == '__main__': - main() diff --git "a/Day01-15/Day12/\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" "b/Day01-15/Day12/\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" deleted file mode 100644 index d0075ce92..000000000 --- "a/Day01-15/Day12/\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" +++ /dev/null @@ -1,189 +0,0 @@ -## 使用正则表达式 - -### 正则表达式相关知识 - -在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要,正则表达式就是用于描述这些规则的工具,换句话说正则表达式是一种工具,它定义了字符串的匹配模式(如何检查一个字符串是否有跟某种模式匹配的部分或者从一个字符串中将与模式匹配的部分提取出来或者替换掉)。如果你在Windows操作系统中使用过文件查找并且在指定文件名时使用过通配符(\*和?),那么正则表达式也是与之类似的用来进行文本匹配的工具,只不过比起通配符正则表达式更强大,它能更精确地描述你的需求(当然你付出的代价是书写一个正则表达式比打出一个通配符要复杂得多,要知道任何给你带来好处的东西都是有代价的,就如同学习一门编程语言一样),比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像028-12345678或0813-7654321),这不就是国内的座机号码吗。最初计算机是为了做数学运算而诞生的,处理的信息基本上都是数值,而今天我们在日常工作中处理的信息基本上都是文本数据,我们希望计算机能够识别和处理符合某些模式的文本,正则表达式就显得非常重要了。今天几乎所有的编程语言都提供了对正则表达式操作的支持,Python通过标准库中的re模块来支持正则表达式操作。 - -我们可以考虑下面一个问题:我们从某个地方(可能是一个文本文件,也可能是网络上的一则新闻)获得了一个字符串,希望在字符串中找出手机号和座机号。当然我们可以设定手机号是11位的数字(注意并不是随机的11位数字,因为你没有见过“25012345678”这样的手机号吧)而座机号跟上一段中描述的模式相同,如果不使用正则表达式要完成这个任务就会很麻烦。 - -关于正则表达式的相关知识,大家可以阅读一篇非常有名的博客叫[《正则表达式30分钟入门教程》](https://deerchao.net/tutorials/regex/regex.htm),读完这篇文章后你就可以看懂下面的表格,这是我们对正则表达式中的一些基本符号进行的扼要总结。 - -| 符号 | 解释 | 示例 | 说明 | -| ------------------ | ----------------------------------------- | ---------------- | -------------------------------------------------- | -| . | 匹配任意字符 | b.t | 可以匹配bat / but / b#t / b1t等 | -| \\w | 匹配字母/数字/下划线 | b\\wt | 可以匹配bat / b1t / b_t等
但不能匹配b#t | -| \\s | 匹配空白字符(包括\r、\n、\t等) | love\\syou | 可以匹配love you | -| \\d | 匹配数字 | \\d\\d | 可以匹配01 / 23 / 99等 | -| \\b | 匹配单词的边界 | \\bThe\\b | | -| ^ | 匹配字符串的开始 | ^The | 可以匹配The开头的字符串 | -| $ | 匹配字符串的结束 | .exe$ | 可以匹配.exe结尾的字符串 | -| \\W | 匹配非字母/数字/下划线 | b\\Wt | 可以匹配b#t / b@t等
但不能匹配but / b1t / b_t等 | -| \\S | 匹配非空白字符 | love\\Syou | 可以匹配love#you等
但不能匹配love you | -| \\D | 匹配非数字 | \\d\\D | 可以匹配9a / 3# / 0F等 | -| \\B | 匹配非单词边界 | \\Bio\\B | | -| [] | 匹配来自字符集的任意单一字符 | [aeiou] | 可以匹配任一元音字母字符 | -| [^] | 匹配不在字符集中的任意单一字符 | [^aeiou] | 可以匹配任一非元音字母字符 | -| * | 匹配0次或多次 | \\w* | | -| + | 匹配1次或多次 | \\w+ | | -| ? | 匹配0次或1次 | \\w? | | -| {N} | 匹配N次 | \\w{3} | | -| {M,} | 匹配至少M次 | \\w{3,} | | -| {M,N} | 匹配至少M次至多N次 | \\w{3,6} | | -| \| | 分支 | foo\|bar | 可以匹配foo或者bar | -| (?#) | 注释 | | | -| (exp) | 匹配exp并捕获到自动命名的组中 | | | -| (?<name>exp) | 匹配exp并捕获到名为name的组中 | | | -| (?:exp) | 匹配exp但是不捕获匹配的文本 | | | -| (?=exp) | 匹配exp前面的位置 | \\b\\w+(?=ing) | 可以匹配I'm dancing中的danc | -| (?<=exp) | 匹配exp后面的位置 | (?<=\\bdanc)\\w+\\b | 可以匹配I love dancing and reading中的第一个ing | -| (?!exp) | 匹配后面不是exp的位置 | | | -| (?a.\*?b | 将正则表达式应用于aabab,前者会匹配整个字符串aabab,后者会匹配aab和ab两个字符串 | -| +? | 重复1次或多次,但尽可能少重复 | | | -| ?? | 重复0次或1次,但尽可能少重复 | | | -| {M,N}? | 重复M到N次,但尽可能少重复 | | | -| {M,}? | 重复M次以上,但尽可能少重复 | | | - -> **说明:**如果需要匹配的字符是正则表达式中的特殊字符,那么可以使用\\进行转义处理,例如想匹配小数点可以写成\\.就可以了,因为直接写.会匹配任意字符;同理,想匹配圆括号必须写成\\(和\\),否则圆括号被视为正则表达式中的分组。 - -### Python对正则表达式的支持 - -Python提供了re模块来支持正则表达式相关操作,下面是re模块中的核心函数。 - -| 函数 | 说明 | -| -------------------------------------------- | ------------------------------------------------------------ | -| compile(pattern, flags=0) | 编译正则表达式返回正则表达式对象 | -| match(pattern, string, flags=0) | 用正则表达式匹配字符串 成功返回匹配对象 否则返回None | -| search(pattern, string, flags=0) | 搜索字符串中第一次出现正则表达式的模式 成功返回匹配对象 否则返回None | -| split(pattern, string, maxsplit=0, flags=0) | 用正则表达式指定的模式分隔符拆分字符串 返回列表 | -| sub(pattern, repl, string, count=0, flags=0) | 用指定的字符串替换原字符串中与正则表达式匹配的模式 可以用count指定替换的次数 | -| fullmatch(pattern, string, flags=0) | match函数的完全匹配(从字符串开头到结尾)版本 | -| findall(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回字符串的列表 | -| finditer(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回一个迭代器 | -| purge() | 清除隐式编译的正则表达式的缓存 | -| re.I / re.IGNORECASE | 忽略大小写匹配标记 | -| re.M / re.MULTILINE | 多行匹配标记 | - -> **说明:**上面提到的re模块中的这些函数,实际开发中也可以用正则表达式对象的方法替代对这些函数的使用,如果一个正则表达式需要重复的使用,那么先通过compile函数编译正则表达式并创建出正则表达式对象无疑是更为明智的选择。 - -下面我们通过一系列的例子来告诉大家在Python中如何使用正则表达式。 - -#### 例子1:验证输入用户名和QQ号是否有效并给出对应的提示信息。 - -```Python -""" - -验证输入用户名和QQ号是否有效并给出对应的提示信息 - -要求: -用户名必须由字母、数字或下划线构成且长度在6~20个字符之间 -QQ号是5~12的数字且首位不能为0 - -""" - -import re - - -def main(): - username = input('请输入用户名: ') - qq = input('请输入QQ号: ') - # match函数的第一个参数是正则表达式字符串或正则表达式对象 - # 第二个参数是要跟正则表达式做匹配的字符串对象 - m1 = re.match(r'^[0-9a-zA-Z_]{6,20}$', username) - if not m1: - print('请输入有效的用户名.') - m2 = re.match(r'^[1-9]\d{4,11}$', qq) - if not m2: - print('请输入有效的QQ号.') - if m1 and m2: - print('你输入的信息是有效的!') - - -if __name__ == '__main__': - main() - -``` - -> **提示**:上面在书写正则表达式时使用了“原始字符串”的写法(在字符串前面加上了r),所谓“原始字符串”就是字符串中的每个字符都是它原始的意义,说得更直接一点就是字符串中没有所谓的转义字符啦。因为正则表达式中有很多元字符和需要进行转义的地方,如果不使用原始字符串就需要将反斜杠写作\\\\,例如表示数字的\\d得书写成\\\\d,这样不仅写起来不方便,阅读的时候也会很吃力。 - -#### 例子2:从一段文字中提取出国内手机号码。 - -下面这张图是截止到2017年底,国内三家运营商推出的手机号段。 - -![](./res/tel-start-number.png) - -```Python -import re - - -def main(): - # 创建正则表达式对象 使用了前瞻和回顾来保证手机号前后不应该出现数字 - pattern = re.compile(r'(?<=\D)1[34578]\d{9}(?=\D)') - sentence = ''' - 重要的事情说8130123456789遍,我的手机号是13512346789这个靓号, - 不是15600998765,也是110或119,王大锤的手机号才是15600998765。 - ''' - # 查找所有匹配并保存到一个列表中 - mylist = re.findall(pattern, sentence) - print(mylist) - print('--------华丽的分隔线--------') - # 通过迭代器取出匹配对象并获得匹配的内容 - for temp in pattern.finditer(sentence): - print(temp.group()) - print('--------华丽的分隔线--------') - # 通过search函数指定搜索位置找出所有匹配 - m = pattern.search(sentence) - while m: - print(m.group()) - m = pattern.search(sentence, m.end()) - - -if __name__ == '__main__': - main() - -``` - -> **说明**:上面匹配国内手机号的正则表达式并不够好,因为像14开头的号码只有145或147,而上面的正则表达式并没有考虑这种情况,要匹配国内手机号,更好的正则表达式的写法是:`(?<=\D)(1[38]\d{9}|14[57]\d{8}|15[0-35-9]\d{8}|17[678]\d{8})(?=\D)`,国内最近好像有19和16开头的手机号了,但是这个暂时不在我们考虑之列。 - -#### 例子3:替换字符串中的不良内容 - -```Python -import re - - -def main(): - sentence = '你丫是傻叉吗? 我操你大爷的. Fuck you.' - purified = re.sub('[操肏艹草曹]|fuck|shit|傻[比屄逼叉缺吊屌]|煞笔', - '*', sentence, flags=re.IGNORECASE) - print(purified) # 你丫是*吗? 我*你大爷的. * you. - - -if __name__ == '__main__': - main() - -``` - -> **说明**:re模块的正则表达式相关函数中都有一个flags参数,它代表了正则表达式的匹配标记,可以通过该标记来指定匹配时是否忽略大小写、是否进行多行匹配、是否显示调试信息等。如果需要为flags参数指定多个值,可以使用[按位或运算符](http://www.runoob.com/python/python-operators.html#ysf5)进行叠加,如`flags=re.I | re.M`。 - -#### 例子4:拆分长字符串 - -```Python -import re - - -def main(): - poem = '窗前明月光,疑是地上霜。举头望明月,低头思故乡。' - sentence_list = re.split(r'[,。, .]', poem) - while '' in sentence_list: - sentence_list.remove('') - print(sentence_list) # ['窗前明月光', '疑是地上霜', '举头望明月', '低头思故乡'] - - -if __name__ == '__main__': - main() - -``` - -### 后话 - -如果要从事爬虫类应用的开发,那么正则表达式一定是一个非常好的助手,因为它可以帮助我们迅速的从网页代码中发现某种我们指定的模式并提取出我们需要的信息,当然对于初学者来收,要编写一个正确的适当的正则表达式可能并不是一件容易的事情(当然有些常用的正则表达式可以直接在网上找找),所以实际开发爬虫应用的时候,有很多人会选择[Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/)或[Lxml](http://lxml.de/)来进行匹配和信息的提取,前者简单方便但是性能较差,后者既好用性能也好,但是安装稍嫌麻烦,这些内容我们会在后期的爬虫专题中为大家介绍。 \ No newline at end of file diff --git a/Day01-15/Day13/asyncio1.py b/Day01-15/Day13/asyncio1.py deleted file mode 100644 index c0763a68d..000000000 --- a/Day01-15/Day13/asyncio1.py +++ /dev/null @@ -1,32 +0,0 @@ -""" - -异步I/O操作 - asyncio模块 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-21 - -""" - -import asyncio -import threading -# import time - - -@asyncio.coroutine -def hello(): - print('%s: hello, world!' % threading.current_thread()) - # 休眠不会阻塞主线程因为使用了异步I/O操作 - # 注意有yield from才会等待休眠操作执行完成 - yield from asyncio.sleep(2) - # asyncio.sleep(1) - # time.sleep(1) - print('%s: goodbye, world!' % threading.current_thread()) - - -loop = asyncio.get_event_loop() -tasks = [hello(), hello()] -# 等待两个异步I/O操作执行结束 -loop.run_until_complete(asyncio.wait(tasks)) -print('game over!') -loop.close() diff --git a/Day01-15/Day13/asyncio2.py b/Day01-15/Day13/asyncio2.py deleted file mode 100644 index b23b1d257..000000000 --- a/Day01-15/Day13/asyncio2.py +++ /dev/null @@ -1,27 +0,0 @@ -""" - -异步I/O操作 - async和await - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-21 - -""" - -import asyncio -import threading - - -# 通过async修饰的函数不再是普通函数而是一个协程 -# 注意async和await将在Python 3.7中作为关键字出现 -async def hello(): - print('%s: hello, world!' % threading.current_thread()) - await asyncio.sleep(2) - print('%s: goodbye, world!' % threading.current_thread()) - - -loop = asyncio.get_event_loop() -tasks = [hello(), hello()] -# 等待两个异步I/O操作执行结束 -loop.run_until_complete(asyncio.wait(tasks)) -loop.close() diff --git a/Day01-15/Day13/asyncio3.py b/Day01-15/Day13/asyncio3.py deleted file mode 100644 index 514ab4fed..000000000 --- a/Day01-15/Day13/asyncio3.py +++ /dev/null @@ -1,38 +0,0 @@ -""" - -异步I/O操作 - asyncio模块 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-21 - -""" - -import asyncio - - -async def wget(host): - print('wget %s...' % host) - connect = asyncio.open_connection(host, 80) - # 异步方式等待连接结果 - reader, writer = await connect - header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host - writer.write(header.encode('utf-8')) - # 异步I/O方式执行写操作 - await writer.drain() - while True: - # 异步I/O方式执行读操作 - line = await reader.readline() - if line == b'\r\n': - break - print('%s header > %s' % (host, line.decode('utf-8').rstrip())) - writer.close() - - -loop = asyncio.get_event_loop() -# 通过生成式语法创建一个装了三个协程的列表 -hosts_list = ['www.sina.com.cn', 'www.sohu.com', 'www.163.com'] -tasks = [wget(host) for host in hosts_list] -# 下面的方法将异步I/O操作放入EventLoop直到执行完毕 -loop.run_until_complete(asyncio.wait(tasks)) -loop.close() diff --git a/Day01-15/Day13/coroutine1.py b/Day01-15/Day13/coroutine1.py deleted file mode 100644 index 3d4cfefec..000000000 --- a/Day01-15/Day13/coroutine1.py +++ /dev/null @@ -1,42 +0,0 @@ -""" - -使用协程 - 模拟快递中心派发快递 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-21 - -""" - -from time import sleep -from random import random - - -def build_deliver_man(man_id): - total = 0 - while True: - total += 1 - print('%d号快递员准备接今天的第%d单.' % (man_id, total)) - pkg = yield - print('%d号快递员收到编号为%s的包裹.' % (man_id, pkg)) - sleep(random() * 3) - - -def package_center(deliver_man, max_per_day): - num = 1 - deliver_man.send(None) - # next(deliver_man) - while num <= max_per_day: - package_id = 'PKG-%d' % num - deliver_man.send(package_id) - num += 1 - sleep(0.1) - deliver_man.close() - print('今天的包裹派送完毕!') - - -dm = build_deliver_man(1) -package_center(dm, 10) - -# 两个函数虽然没有调用关系但是创建快递员的函数作为一个协程协助了快递中心函数完成任务 -# 想一想如果有多个快递员的时候应该如何处理 diff --git a/Day01-15/Day13/coroutine2.py b/Day01-15/Day13/coroutine2.py deleted file mode 100644 index 61333ae4d..000000000 --- a/Day01-15/Day13/coroutine2.py +++ /dev/null @@ -1,44 +0,0 @@ -""" - -使用协程 - 查看协程的状态 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-21 - -""" - -from time import sleep -from inspect import getgeneratorstate - - -def build_deliver_man(man_id): - total = 0 - while True: - total += 1 - print('%d号快递员准备接今天的第%d单.' % (man_id, total)) - pkg = yield - print('%d号快递员收到编号为%s的包裹.' % (man_id, pkg)) - sleep(0.5) - - -def package_center(deliver_man, max_per_day): - num = 1 - # 创建状态(GEN_CREATED) - 等待开始执行 - print(getgeneratorstate(deliver_man)) - deliver_man.send(None) - # 挂起状态(GEN_SUSPENDED) - 在yield表达式处暂停 - print(getgeneratorstate(deliver_man)) - # next(deliver_man) - while num <= max_per_day: - package_id = 'PKG-%d' % num - deliver_man.send(package_id) - num += 1 - deliver_man.close() - # 结束状态(GEN_CLOSED) - 执行完毕 - print(getgeneratorstate(deliver_man)) - print('今天的包裹派送完毕!') - - -dm = build_deliver_man(1) -package_center(dm, 10) diff --git a/Day01-15/Day13/generator2.py b/Day01-15/Day13/generator2.py deleted file mode 100644 index 60c3a4df1..000000000 --- a/Day01-15/Day13/generator2.py +++ /dev/null @@ -1,21 +0,0 @@ -""" - -生成器 - 使用yield关键字 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-21 - -""" - - -def fib(num): - n, a, b = 0, 0, 1 - while n < num: - yield b - a, b = b, a + b - n += 1 - - -for x in fib(20): - print(x) diff --git a/Day01-15/Day13/multiprocess1.py b/Day01-15/Day13/multiprocess1.py deleted file mode 100644 index 7309b96e6..000000000 --- a/Day01-15/Day13/multiprocess1.py +++ /dev/null @@ -1,36 +0,0 @@ -""" - -使用Process类创建多个进程 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-20 - -""" - -# 通过下面程序的执行结果可以证实 父进程在创建子进程时复制了进程及其数据结构 -# 每个进程都有自己独立的内存空间 所以进程之间共享数据只能通过IPC的方式 - - -from multiprocessing import Process, Queue -from time import sleep - - -def sub_task(string, q): - number = q.get() - while number: - print('%d: %s' % (number, string)) - sleep(0.001) - number = q.get() - - -def main(): - q = Queue(10) - for number in range(1, 11): - q.put(number) - Process(target=sub_task, args=('Ping', q)).start() - Process(target=sub_task, args=('Pong', q)).start() - - -if __name__ == '__main__': - main() diff --git a/Day01-15/Day13/multiprocess2.py b/Day01-15/Day13/multiprocess2.py deleted file mode 100644 index 6fdeb2074..000000000 --- a/Day01-15/Day13/multiprocess2.py +++ /dev/null @@ -1,35 +0,0 @@ -""" - -实现进程间的通信 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-20 - -""" - -import multiprocessing -import os - - -def sub_task(queue): - print('子进程进程号:', os.getpid()) - counter = 0 - while counter < 1000: - queue.put('Pong') - counter += 1 - - -if __name__ == '__main__': - print('当前进程号:', os.getpid()) - queue = multiprocessing.Queue() - p = multiprocessing.Process(target=sub_task, args=(queue,)) - p.start() - counter = 0 - while counter < 1000: - queue.put('Ping') - counter += 1 - p.join() - print('子任务已经完成.') - for _ in range(2000): - print(queue.get(), end='') diff --git a/Day01-15/Day13/multiprocess3.py b/Day01-15/Day13/multiprocess3.py deleted file mode 100644 index b82ab7637..000000000 --- a/Day01-15/Day13/multiprocess3.py +++ /dev/null @@ -1,29 +0,0 @@ -""" - -创建进程调用其他程序 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-20 - -""" - -import subprocess -import sys - -def main(): - # 通过sys.argv获取命令行参数 - if len(sys.argv) > 1: - # 第一个命令行参数是程序本身所以从第二个开始取 - for index in range(1, len(sys.argv)): - try: - # 通过subprocess模块的call函数启动子进程 - status = subprocess.call(sys.argv[index]) - except FileNotFoundError: - print('不能执行%s命令' % sys.argv[index]) - else: - print('请使用命令行参数指定要执行的进程') - - -if __name__ == '__main__': - main() diff --git a/Day01-15/Day13/multithread1.py b/Day01-15/Day13/multithread1.py deleted file mode 100644 index 3b678101d..000000000 --- a/Day01-15/Day13/multithread1.py +++ /dev/null @@ -1,44 +0,0 @@ -""" - -使用多线程的情况 - 模拟多个下载任务 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-20 - -""" - -from random import randint -from time import time, sleep -import atexit -import _thread - - -def download_task(filename): - print('开始下载%s...' % filename) - time_to_download = randint(5, 10) - print('剩余时间%d秒.' % time_to_download) - sleep(time_to_download) - print('%s下载完成!' % filename) - - -def shutdown_hook(start): - end = time() - print('总共耗费了%.3f秒.' % (end - start)) - - -def main(): - start = time() - # 将多个下载任务放到多个线程中执行 - thread1 = _thread.start_new_thread(download_task, ('Python从入门到住院.pdf',)) - thread2 = _thread.start_new_thread(download_task, ('Peking Hot.avi',)) - # 注册关机钩子在程序执行结束前计算执行时间 - atexit.register(shutdown_hook, start) - - -if __name__ == '__main__': - main() - -# 执行这里的代码会引发致命错误(不要被这个词吓到) 因为主线程结束后下载线程再想执行就会出问题 -# 需要说明一下 由于_thread模块属于比较底层的线程操作而且不支持守护线程的概念 -# 在实际开发中会有诸多不便 因此我们推荐使用threading模块提供的高级操作进行多线程编程 diff --git a/Day01-15/Day13/multithread2.py b/Day01-15/Day13/multithread2.py deleted file mode 100644 index 5d17915cf..000000000 --- a/Day01-15/Day13/multithread2.py +++ /dev/null @@ -1,36 +0,0 @@ -""" - -使用多线程的情况 - 模拟多个下载任务 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-20 - -""" - -from random import randint -from threading import Thread -from time import time, sleep - - -def download_task(filename): - print('开始下载%s...' % filename) - time_to_download = randint(5, 10) - sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) - - -def main(): - start = time() - thread1 = Thread(target=download_task, args=('Python从入门到住院.pdf',)) - thread1.start() - thread2 = Thread(target=download_task, args=('Peking Hot.avi',)) - thread2.start() - thread1.join() - thread2.join() - end = time() - print('总共耗费了%.3f秒' % (end - start)) - - -if __name__ == '__main__': - main() diff --git a/Day01-15/Day13/multithread3.py b/Day01-15/Day13/multithread3.py deleted file mode 100644 index 5baeb8586..000000000 --- a/Day01-15/Day13/multithread3.py +++ /dev/null @@ -1,47 +0,0 @@ -""" - -使用多线程的情况 - 模拟多个下载任务 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-20 - -""" - -from random import randint -from time import time, sleep -import threading - - -class DownloadTask(threading.Thread): - - def __init__(self, filename): - super().__init__() - self._filename = filename - - def run(self): - print('开始下载%s...' % self._filename) - time_to_download = randint(5, 10) - print('剩余时间%d秒.' % time_to_download) - sleep(time_to_download) - print('%s下载完成!' % self._filename) - - -def main(): - start = time() - # 将多个下载任务放到多个线程中执行 - # 通过自定义的线程类创建线程对象 线程启动后会回调执行run方法 - thread1 = DownloadTask('Python从入门到住院.pdf') - thread1.start() - thread2 = DownloadTask('Peking Hot.avi') - thread2.start() - thread1.join() - thread2.join() - end = time() - print('总共耗费了%.3f秒' % (end - start)) - - -if __name__ == '__main__': - main() - -# 请注意通过threading.Thread创建的线程默认是非守护线程 diff --git a/Day01-15/Day13/multithread6.py b/Day01-15/Day13/multithread6.py deleted file mode 100644 index ad9ba5982..000000000 --- a/Day01-15/Day13/multithread6.py +++ /dev/null @@ -1,46 +0,0 @@ -""" - -多个线程共享数据 - 有锁的情况 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-20 - -""" - -import time -import threading - - -class Account(object): - - def __init__(self): - self._balance = 0 - self._lock = threading.Lock() - - def deposit(self, money): - # 获得锁后代码才能继续执行 - self._lock.acquire() - try: - new_balance = self._balance + money - time.sleep(0.01) - self._balance = new_balance - finally: - # 操作完成后一定要记着释放锁 - self._lock.release() - - @property - def balance(self): - return self._balance - - -if __name__ == '__main__': - account = Account() - # 创建100个存款的线程向同一个账户中存钱 - for _ in range(100): - threading.Thread(target=account.deposit, args=(1,)).start() - # 等所有存款的线程都执行完毕 - time.sleep(2) - print('账户余额为: ¥%d元' % account.balance) - -# 想一想结果为什么不是我们期望的100元 diff --git a/Day01-15/Day13/res/macos-monitor.png b/Day01-15/Day13/res/macos-monitor.png deleted file mode 100644 index 3d1b88b0f..000000000 Binary files a/Day01-15/Day13/res/macos-monitor.png and /dev/null differ diff --git "a/Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" "b/Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" deleted file mode 100644 index 89d6ad665..000000000 --- "a/Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" +++ /dev/null @@ -1,499 +0,0 @@ -## 进程和线程 - -今天我们使用的计算机早已进入多CPU或多核时代,而我们使用的操作系统都是支持“多任务”的操作系统,这使得我们可以同时运行多个程序,也可以将一个程序分解为若干个相对独立的子任务,让多个子任务并发的执行,从而缩短程序的执行时间,同时也让用户获得更好的体验。因此在当下不管是用什么编程语言进行开发,实现让程序同时执行多个任务也就是常说的“并发编程”,应该是程序员必备技能之一。为此,我们需要先讨论两个概念,一个叫进程,一个叫线程。 - -### 概念 - -进程就是操作系统中执行的一个程序,操作系统以进程为单位分配存储空间,每个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据,操作系统管理所有进程的执行,为它们合理的分配资源。进程可以通过fork或spawn的方式来创建新的进程来执行其他的任务,不过新的进程也有自己独立的内存空间,因此必须通过进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,具体的方式包括管道、信号、套接字、共享内存区等。 - -一个进程还可以拥有多个并发的执行线索,简单的说就是拥有多个可以获得CPU调度的执行单元,这就是所谓的线程。由于线程在同一个进程下,它们可以共享相同的上下文,因此相对于进程而言,线程间的信息共享和通信更加容易。当然在单核CPU系统中,真正的并发是不可能的,因为在某个时刻能够获得CPU的只有唯一的一个线程,多个线程共享了CPU的执行时间。使用多线程实现并发编程为程序带来的好处是不言而喻的,最主要的体现在提升程序的性能和改善用户体验,今天我们使用的软件几乎都用到了多线程技术,这一点可以利用系统自带的进程监控工具(如macOS中的“活动监视器”、Windows中的“任务管理器”)来证实,如下图所示。 - -![](./res/macos-monitor.png) - -当然多线程也并不是没有坏处,站在其他进程的角度,多线程的程序对其他程序并不友好,因为它占用了更多的CPU执行时间,导致其他程序无法获得足够的CPU执行时间;另一方面,站在开发者的角度,编写和调试多线程的程序都对开发者有较高的要求,对于初学者来说更加困难。 - -Python既支持多进程又支持多线程,因此使用Python实现并发编程主要有3种方式:多进程、多线程、多进程+多线程。 - -### Python中的多进程 - -Unix和Linux操作系统上提供了`fork()`系统调用来创建进程,调用`fork()`函数的是父进程,创建出的是子进程,子进程是父进程的一个拷贝,但是子进程拥有自己的PID。`fork()`函数非常特殊它会返回两次,父进程中可以通过`fork()`函数的返回值得到子进程的PID,而子进程中的返回值永远都是0。Python的os模块提供了`fork()`函数。由于Windows系统没有`fork()`调用,因此要实现跨平台的多进程编程,可以使用multiprocessing模块的`Process`类来创建子进程,而且该模块还提供了更高级的封装,例如批量启动进程的进程池(`Pool`)、用于进程间通信的队列(`Queue`)和管道(`Pipe`)等。 - -下面用一个下载文件的例子来说明使用多进程和不使用多进程到底有什么差别,先看看下面的代码。 - -```Python -from random import randint -from time import time, sleep - - -def download_task(filename): - print('开始下载%s...' % filename) - time_to_download = randint(5, 10) - sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) - - -def main(): - start = time() - download_task('Python从入门到住院.pdf') - download_task('Peking Hot.avi') - end = time() - print('总共耗费了%.2f秒.' % (end - start)) - - -if __name__ == '__main__': - main() - -``` - -下面是运行程序得到的一次运行结果。 - -```Shell -开始下载Python从入门到住院.pdf... -Python从入门到住院.pdf下载完成! 耗费了6秒 -开始下载Peking Hot.avi... -Peking Hot.avi下载完成! 耗费了7秒 -总共耗费了13.01秒. -``` - -从上面的例子可以看出,如果程序中的代码只能按顺序一点点的往下执行,那么即使执行两个毫不相关的下载任务,也需要先等待一个文件下载完成后才能开始下一个下载任务,很显然这并不合理也没有效率。接下来我们使用多进程的方式将两个下载任务放到不同的进程中,代码如下所示。 - -```Python -from multiprocessing import Process -from os import getpid -from random import randint -from time import time, sleep - - -def download_task(filename): - print('启动下载进程,进程号[%d].' % getpid()) - print('开始下载%s...' % filename) - time_to_download = randint(5, 10) - sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) - - -def main(): - start = time() - p1 = Process(target=download_task, args=('Python从入门到住院.pdf', )) - p1.start() - p2 = Process(target=download_task, args=('Peking Hot.avi', )) - p2.start() - p1.join() - p2.join() - end = time() - print('总共耗费了%.2f秒.' % (end - start)) - - -if __name__ == '__main__': - main() - -``` - -在上面的代码中,我们通过`Process`类创建了进程对象,通过`target`参数我们传入一个函数来表示进程启动后要执行的代码,后面的`args`是一个元组,它代表了传递给函数的参数。`Process`对象的`start`方法用来启动进程,而`join`方法表示等待进程执行结束。运行上面的代码可以明显发现两个下载任务“同时”启动了,而且程序的执行时间将大大缩短,不再是两个任务的时间总和。下面是程序的一次执行结果。 - -```Shell -启动下载进程,进程号[1530]. -开始下载Python从入门到住院.pdf... -启动下载进程,进程号[1531]. -开始下载Peking Hot.avi... -Peking Hot.avi下载完成! 耗费了7秒 -Python从入门到住院.pdf下载完成! 耗费了10秒 -总共耗费了10.01秒. -``` - -我们也可以使用subprocess模块中的类和函数来创建和启动子进程,然后通过管道来和子进程通信,这些内容我们不在此进行讲解,有兴趣的读者可以自己了解这些知识。接下来我们将重点放在如何实现两个进程间的通信。我们启动两个进程,一个输出Ping,一个输出Pong,两个进程输出的Ping和Pong加起来一共10个。听起来很简单吧,但是如果这样写可是错的哦。 - -```Python -from multiprocessing import Process -from time import sleep - -counter = 0 - - -def sub_task(string): - global counter - while counter < 10: - print(string, end='', flush=True) - counter += 1 - sleep(0.01) - - -def main(): - Process(target=sub_task, args=('Ping', )).start() - Process(target=sub_task, args=('Pong', )).start() - - -if __name__ == '__main__': - main() - -``` - -看起来没毛病,但是最后的结果是Ping和Pong各输出了10个,Why?当我们在程序中创建进程的时候,子进程复制了父进程及其所有的数据结构,每个子进程有自己独立的内存空间,这也就意味着两个子进程中各有一个`counter`变量,所以结果也就可想而知了。要解决这个问题比较简单的办法是使用multiprocessing模块中的`Queue`类,它是可以被多个进程共享的队列,底层是通过管道和[信号量(semaphore)]()机制来实现的,有兴趣的读者可以自己尝试一下。 - - -### Python中的多线程 - -在Python早期的版本中就引入了thread模块(现在名为_thread)来实现多线程编程,然而该模块过于底层,而且很多功能都没有提供,因此目前的多线程开发我们推荐使用threading模块,该模块对多线程编程提供了更好的面向对象的封装。我们把刚才下载文件的例子用多线程的方式来实现一遍。 - -```Python -from random import randint -from threading import Thread -from time import time, sleep - - -def download(filename): - print('开始下载%s...' % filename) - time_to_download = randint(5, 10) - sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) - - -def main(): - start = time() - t1 = Thread(target=download, args=('Python从入门到住院.pdf',)) - t1.start() - t2 = Thread(target=download, args=('Peking Hot.avi',)) - t2.start() - t1.join() - t2.join() - end = time() - print('总共耗费了%.3f秒' % (end - start)) - - -if __name__ == '__main__': - main() - -``` - -我们可以直接使用threading模块的`Thread`类来创建线程,但是我们之前讲过一个非常重要的概念叫“继承”,我们可以从已有的类创建新类,因此也可以通过继承`Thread`类的方式来创建自定义的线程类,然后再创建线程对象并启动线程。代码如下所示。 - -```Python -from random import randint -from threading import Thread -from time import time, sleep - - -class DownloadTask(Thread): - - def __init__(self, filename): - super().__init__() - self._filename = filename - - def run(self): - print('开始下载%s...' % self._filename) - time_to_download = randint(5, 10) - sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (self._filename, time_to_download)) - - -def main(): - start = time() - t1 = DownloadTask('Python从入门到住院.pdf') - t1.start() - t2 = DownloadTask('Peking Hot.avi') - t2.start() - t1.join() - t2.join() - end = time() - print('总共耗费了%.2f秒.' % (end - start)) - - -if __name__ == '__main__': - main() - -``` - -因为多个线程可以共享进程的内存空间,因此要实现多个线程间的通信相对简单,大家能想到的最直接的办法就是设置一个全局变量,多个线程共享这个全局变量即可。但是当多个线程共享同一个变量(我们通常称之为“资源”)的时候,很有可能产生不可控的结果从而导致程序失效甚至崩溃。如果一个资源被多个线程竞争使用,那么我们通常称之为“临界资源”,对“临界资源”的访问需要加上保护,否则资源会处于“混乱”的状态。下面的例子演示了100个线程向同一个银行账户转账(转入1元钱)的场景,在这个例子中,银行账户就是一个临界资源,在没有保护的情况下我们很有可能会得到错误的结果。 - -```Python -from time import sleep -from threading import Thread - - -class Account(object): - - def __init__(self): - self._balance = 0 - - def deposit(self, money): - # 计算存款后的余额 - new_balance = self._balance + money - # 模拟受理存款业务需要0.01秒的时间 - sleep(0.01) - # 修改账户余额 - self._balance = new_balance - - @property - def balance(self): - return self._balance - - -class AddMoneyThread(Thread): - - def __init__(self, account, money): - super().__init__() - self._account = account - self._money = money - - def run(self): - self._account.deposit(self._money) - - -def main(): - account = Account() - threads = [] - # 创建100个存款的线程向同一个账户中存钱 - for _ in range(100): - t = AddMoneyThread(account, 1) - threads.append(t) - t.start() - # 等所有存款的线程都执行完毕 - for t in threads: - t.join() - print('账户余额为: ¥%d元' % account.balance) - - -if __name__ == '__main__': - main() - -``` - -运行上面的程序,结果让人大跌眼镜,100个线程分别向账户中转入1元钱,结果居然远远小于100元。之所以出现这种情况是因为我们没有对银行账户这个“临界资源”加以保护,多个线程同时向账户中存钱时,会一起执行到`new_balance = self._balance + money`这行代码,多个线程得到的账户余额都是初始状态下的`0`,所以都是`0`上面做了+1的操作,因此得到了错误的结果。在这种情况下,“锁”就可以派上用场了。我们可以通过“锁”来保护“临界资源”,只有获得“锁”的线程才能访问“临界资源”,而其他没有得到“锁”的线程只能被阻塞起来,直到获得“锁”的线程释放了“锁”,其他线程才有机会获得“锁”,进而访问被保护的“临界资源”。下面的代码演示了如何使用“锁”来保护对银行账户的操作,从而获得正确的结果。 - -```Python -from time import sleep -from threading import Thread, Lock - - -class Account(object): - - def __init__(self): - self._balance = 0 - self._lock = Lock() - - def deposit(self, money): - # 先获取锁才能执行后续的代码 - self._lock.acquire() - try: - new_balance = self._balance + money - sleep(0.01) - self._balance = new_balance - finally: - # 在finally中执行释放锁的操作保证正常异常锁都能释放 - self._lock.release() - - @property - def balance(self): - return self._balance - - -class AddMoneyThread(Thread): - - def __init__(self, account, money): - super().__init__() - self._account = account - self._money = money - - def run(self): - self._account.deposit(self._money) - - -def main(): - account = Account() - threads = [] - for _ in range(100): - t = AddMoneyThread(account, 1) - threads.append(t) - t.start() - for t in threads: - t.join() - print('账户余额为: ¥%d元' % account.balance) - - -if __name__ == '__main__': - main() - -``` - -比较遗憾的一件事情是Python的多线程并不能发挥CPU的多核特性,这一点只要启动几个执行死循环的线程就可以得到证实了。之所以如此,是因为Python的解释器有一个“全局解释器锁”(GIL)的东西,任何线程执行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行,这是一个历史遗留问题,但是即便如此,就如我们之前举的例子,使用多线程在提升执行效率和改善用户体验方面仍然是有积极意义的。 - -### 多进程还是多线程 - -无论是多进程还是多线程,只要数量一多,效率肯定上不去,为什么呢?我们打个比方,假设你不幸正在准备中考,每天晚上需要做语文、数学、英语、物理、化学这5科的作业,每项作业耗时1小时。如果你先花1小时做语文作业,做完了,再花1小时做数学作业,这样,依次全部做完,一共花5小时,这种方式称为单任务模型。如果你打算切换到多任务模型,可以先做1分钟语文,再切换到数学作业,做1分钟,再切换到英语,以此类推,只要切换速度足够快,这种方式就和单核CPU执行多任务是一样的了,以旁观者的角度来看,你就正在同时写5科作业。 - -但是,切换作业是有代价的,比如从语文切到数学,要先收拾桌子上的语文书本、钢笔(这叫保存现场),然后,打开数学课本、找出圆规直尺(这叫准备新环境),才能开始做数学作业。操作系统在切换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。这个切换过程虽然很快,但是也需要耗费时间。如果有几千个任务同时进行,操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了,这种情况最常见的就是硬盘狂响,点窗口无反应,系统处于假死状态。所以,多任务一旦多到一个限度,反而会使得系统性能急剧下降,最终导致所有任务都做不好。 - -是否采用多任务的第二个考虑是任务的类型,可以把任务分为计算密集型和I/O密集型。计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如对视频进行编码解码或者格式转换等等,这种任务全靠CPU的运算能力,虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低。计算密集型任务由于主要消耗CPU资源,这类任务用Python这样的脚本语言去执行效率通常很低,最能胜任这类任务的是C语言,我们之前提到了Python中有嵌入C/C++代码的机制。 - -除了计算密集型任务,其他的涉及到网络、存储介质I/O的任务都可以视为I/O密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待I/O操作完成(因为I/O的速度远远低于CPU和内存的速度)。对于I/O密集型任务,如果启动多任务,就可以减少I/O等待时间从而让CPU高效率的运转。有一大类的任务都属于I/O密集型任务,这其中包括了我们很快会涉及到的网络应用和Web应用。 - -> **说明:**上面的内容和例子来自于[廖雪峰官方网站的《Python教程》](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000),因为对作者文中的某些观点持有不同的看法,对原文的文字描述做了适当的调整。 - -### 单线程+异步I/O - -现代操作系统对I/O操作的改进中最为重要的就是支持异步I/O。如果充分利用操作系统提供的异步I/O支持,就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型。Nginx就是支持异步I/O的Web服务器,它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上,可以运行多个进程(数量与CPU核心数相同),充分利用多核CPU。用Node.js开发的服务器端程序也使用了这种工作模式,这也是当下实现多任务编程的一种趋势。 - -在Python语言中,单线程+异步I/O的编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。协程最大的优势就是极高的执行效率,因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销。协程的第二个优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不用加锁,只需要判断状态就好了,所以执行效率比多线程高很多。如果想要充分利用CPU的多核特性,最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。关于这方面的内容,我稍后会做一个专题来进行讲解。 - -### 应用案例 - -#### 例子1:将耗时间的任务放到线程中以获得更好的用户体验。 - -如下所示的界面中,有“下载”和“关于”两个按钮,用休眠的方式模拟点击“下载”按钮会联网下载文件需要耗费10秒的时间,如果不使用“多线程”,我们会发现,当点击“下载”按钮后整个程序的其他部分都被这个耗时间的任务阻塞而无法执行了,这显然是非常糟糕的用户体验,代码如下所示。 - -```Python -import time -import tkinter -import tkinter.messagebox - - -def download(): - # 模拟下载任务需要花费10秒钟时间 - time.sleep(10) - tkinter.messagebox.showinfo('提示', '下载完成!') - - -def show_about(): - tkinter.messagebox.showinfo('关于', '作者: 骆昊(v1.0)') - - -def main(): - top = tkinter.Tk() - top.title('单线程') - top.geometry('200x150') - top.wm_attributes('-topmost', True) - - panel = tkinter.Frame(top) - button1 = tkinter.Button(panel, text='下载', command=download) - button1.pack(side='left') - button2 = tkinter.Button(panel, text='关于', command=show_about) - button2.pack(side='right') - panel.pack(side='bottom') - - tkinter.mainloop() - - -if __name__ == '__main__': - main() - -``` - -如果使用多线程将耗时间的任务放到一个独立的线程中执行,这样就不会因为执行耗时间的任务而阻塞了主线程,修改后的代码如下所示。 - -```Python -import time -import tkinter -import tkinter.messagebox -from threading import Thread - - -def main(): - - class DownloadTaskHandler(Thread): - - def run(self): - time.sleep(10) - tkinter.messagebox.showinfo('提示', '下载完成!') - # 启用下载按钮 - button1.config(state=tkinter.NORMAL) - - def download(): - # 禁用下载按钮 - button1.config(state=tkinter.DISABLED) - # 通过daemon参数将线程设置为守护线程(主程序退出就不再保留执行) - # 在线程中处理耗时间的下载任务 - DownloadTaskHandler(daemon=True).start() - - def show_about(): - tkinter.messagebox.showinfo('关于', '作者: 骆昊(v1.0)') - - top = tkinter.Tk() - top.title('单线程') - top.geometry('200x150') - top.wm_attributes('-topmost', 1) - - panel = tkinter.Frame(top) - button1 = tkinter.Button(panel, text='下载', command=download) - button1.pack(side='left') - button2 = tkinter.Button(panel, text='关于', command=show_about) - button2.pack(side='right') - panel.pack(side='bottom') - - tkinter.mainloop() - - -if __name__ == '__main__': - main() - -``` - -#### 例子2:使用多进程对复杂任务进行“分而治之”。 - -我们来完成1~100000000求和的计算密集型任务,这个问题本身非常简单,有点循环的知识就能解决,代码如下所示。 - -```Python -from time import time - - -def main(): - total = 0 - number_list = [x for x in range(1, 100000001)] - start = time() - for number in number_list: - total += number - print(total) - end = time() - print('Execution time: %.3fs' % (end - start)) - - -if __name__ == '__main__': - main() - -``` - -在上面的代码中,我故意先去创建了一个列表容器然后填入了100000000个数,这一步其实是比较耗时间的,所以为了公平起见,当我们将这个任务分解到8个进程中去执行的时候,我们暂时也不考虑列表切片操作花费的时间,只是把做运算和合并运算结果的时间统计出来,代码如下所示。 - -```Python -from multiprocessing import Process, Queue -from random import randint -from time import time - - -def task_handler(curr_list, result_queue): - total = 0 - for number in curr_list: - total += number - result_queue.put(total) - - -def main(): - processes = [] - number_list = [x for x in range(1, 100000001)] - result_queue = Queue() - index = 0 - # 启动8个进程将数据切片后进行运算 - for _ in range(8): - p = Process(target=task_handler, - args=(number_list[index:index + 12500000], result_queue)) - index += 12500000 - processes.append(p) - p.start() - # 开始记录所有进程执行完成花费的时间 - start = time() - for p in processes: - p.join() - # 合并执行结果 - total = 0 - while not result_queue.empty(): - total += result_queue.get() - print(total) - end = time() - print('Execution time: ', (end - start), 's', sep='') - - -if __name__ == '__main__': - main() - -``` - -比较两段代码的执行结果(在我目前使用的MacBook上,上面的代码需要大概6秒左右的时间,而下面的代码只需要不到1秒的时间,再强调一次我们只是比较了运算的时间,不考虑列表创建及切片操作花费的时间),使用多进程后由于获得了更多的CPU执行时间以及更好的利用了CPU的多核特性,明显的减少了程序的执行时间,而且计算量越大效果越明显。当然,如果愿意还可以将多个进程部署在不同的计算机上,做成分布式进程,具体的做法就是通过multiprocessing.managers模块中提供的管理器将`Queue`对象通过网络共享出来(注册到网络上让其他计算机可以访问),这部分内容也留到爬虫的专题再进行讲解。 \ No newline at end of file diff --git a/Day01-15/Day14/guido.jpg b/Day01-15/Day14/guido.jpg deleted file mode 100644 index 78f71ae77..000000000 Binary files a/Day01-15/Day14/guido.jpg and /dev/null differ diff --git a/Day01-15/Day14/res/TCP-IP-model.png b/Day01-15/Day14/res/TCP-IP-model.png deleted file mode 100644 index 777af4733..000000000 Binary files a/Day01-15/Day14/res/TCP-IP-model.png and /dev/null differ diff --git a/Day01-15/Day14/res/after-browser.jpg b/Day01-15/Day14/res/after-browser.jpg deleted file mode 100644 index 7cc95202e..000000000 Binary files a/Day01-15/Day14/res/after-browser.jpg and /dev/null differ diff --git a/Day01-15/Day14/res/arpanet.png b/Day01-15/Day14/res/arpanet.png deleted file mode 100644 index 65d2dd37d..000000000 Binary files a/Day01-15/Day14/res/arpanet.png and /dev/null differ diff --git a/Day01-15/Day14/res/before-browser.jpg b/Day01-15/Day14/res/before-browser.jpg deleted file mode 100644 index f8161445c..000000000 Binary files a/Day01-15/Day14/res/before-browser.jpg and /dev/null differ diff --git a/Day01-15/Day14/res/browers.jpg b/Day01-15/Day14/res/browers.jpg deleted file mode 100644 index 0be1d4dcf..000000000 Binary files a/Day01-15/Day14/res/browers.jpg and /dev/null differ diff --git a/Day01-15/Day14/res/browser-market-place.jpeg b/Day01-15/Day14/res/browser-market-place.jpeg deleted file mode 100644 index 196b0f461..000000000 Binary files a/Day01-15/Day14/res/browser-market-place.jpeg and /dev/null differ diff --git a/Day01-15/Day14/res/how-data-is-processed.jpg b/Day01-15/Day14/res/how-data-is-processed.jpg deleted file mode 100644 index 4359bcd4b..000000000 Binary files a/Day01-15/Day14/res/how-data-is-processed.jpg and /dev/null differ diff --git a/Day01-15/Day14/res/osi_rm.gif b/Day01-15/Day14/res/osi_rm.gif deleted file mode 100644 index 876960d9a..000000000 Binary files a/Day01-15/Day14/res/osi_rm.gif and /dev/null differ diff --git a/Day01-15/Day14/res/osimodel.png b/Day01-15/Day14/res/osimodel.png deleted file mode 100644 index f0361c924..000000000 Binary files a/Day01-15/Day14/res/osimodel.png and /dev/null differ diff --git a/Day01-15/Day14/res/tcpipprotocols.png b/Day01-15/Day14/res/tcpipprotocols.png deleted file mode 100644 index 4acf2169a..000000000 Binary files a/Day01-15/Day14/res/tcpipprotocols.png and /dev/null differ diff --git a/Day01-15/Day14/res/telnet.png b/Day01-15/Day14/res/telnet.png deleted file mode 100644 index 20c86530c..000000000 Binary files a/Day01-15/Day14/res/telnet.png and /dev/null differ diff --git a/Day01-15/Day14/socket1.py b/Day01-15/Day14/socket1.py deleted file mode 100644 index 82389d763..000000000 --- a/Day01-15/Day14/socket1.py +++ /dev/null @@ -1,25 +0,0 @@ -""" - -套接字 - 基于TCP协议创建时间服务器 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-22 - -""" - -from socket import * -from time import * - -server = socket(AF_INET, SOCK_STREAM) -server.bind(('localhost', 6789)) -server.listen() -print('服务器已经启动正在监听客户端连接.') -while True: - client, addr = server.accept() - print('客户端%s:%d连接成功.' % (addr[0], addr[1])) - currtime = localtime(time()) - timestr = strftime('%Y-%m-%d %H:%M:%S', currtime) - client.send(timestr.encode('utf-8')) - client.close() -server.close() diff --git a/Day01-15/Day14/socket2.py b/Day01-15/Day14/socket2.py deleted file mode 100644 index ca5ce7b16..000000000 --- a/Day01-15/Day14/socket2.py +++ /dev/null @@ -1,20 +0,0 @@ -""" - -套接字 - 基于TCP协议创建时间客户端 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-22 - -""" - -from socket import * - -client = socket(AF_INET, SOCK_STREAM) -client.connect(('localhost', 6789)) -while True: - data = client.recv(1024) - if not data: - break - print(data.decode('utf-8')) -client.close() diff --git a/Day01-15/Day14/socket4.py b/Day01-15/Day14/socket4.py deleted file mode 100644 index 4fdf9c4d4..000000000 --- a/Day01-15/Day14/socket4.py +++ /dev/null @@ -1,22 +0,0 @@ -""" - -套接字 - 基于UDP协议创建Echo客户端 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-22 - -""" - -from socket import * - -client = socket(AF_INET, SOCK_DGRAM) -while True: - data_str = input('请输入: ') - client.sendto(data_str.encode('utf-8'), ('localhost', 6789)) - data, addr = client.recvfrom(1024) - data_str = data.decode('utf-8') - print('服务器回应:', data_str) - if data_str == 'bye': - break -client.close() diff --git a/Day01-15/Day14/socket5.py b/Day01-15/Day14/socket5.py deleted file mode 100644 index 97fe088b0..000000000 --- a/Day01-15/Day14/socket5.py +++ /dev/null @@ -1,24 +0,0 @@ -""" - -使用socketserver模块创建时间服务器 - -Version: 0.1 -Author: 骆昊 -Date: 2018-03-22 - -""" - -from socketserver import TCPServer, StreamRequestHandler -from time import * - - -class EchoRequestHandler(StreamRequestHandler): - - def handle(self): - currtime = localtime(time()) - timestr = strftime('%Y-%m-%d %H:%M:%S', currtime) - self.wfile.write(timestr.encode('utf-8')) - - -server = TCPServer(('localhost', 6789), EchoRequestHandler) -server.serve_forever() diff --git "a/Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" "b/Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" deleted file mode 100644 index 7f33271ec..000000000 --- "a/Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" +++ /dev/null @@ -1,303 +0,0 @@ -## 网络编程入门 - -### 计算机网络基础 - -计算机网络是独立自主的计算机互联而成的系统的总称,组建计算机网络最主要的目的是实现多台计算机之间的通信和资源共享。今天计算机网络中的设备和计算机网络的用户已经多得不可计数,而计算机网络也可以称得上是一个“复杂巨系统”,对于这样的系统,我们不可能用一两篇文章把它讲清楚,有兴趣的读者可以自行阅读Andrew S.Tanenbaum老师的经典之作《计算机网络》或Kurose和Ross老师合著的《计算机网络:自顶向下方法》来了解计算机网络的相关知识。 - -#### 计算机网络发展史 - -1. 1960s - 美国国防部ARPANET项目问世,奠定了分组交换网络的基础。 - - ![](./res/arpanet.png) - -2. 1980s - 国际标准化组织(ISO)发布OSI/RM,奠定了网络技术标准化的基础。 - - ![](./res/osimodel.png) - -3. 1990s - 英国人[蒂姆·伯纳斯-李](https://zh.wikipedia.org/wiki/%E6%8F%90%E5%A7%86%C2%B7%E6%9F%8F%E5%85%A7%E8%8C%B2-%E6%9D%8E)发明了图形化的浏览器,浏览器的简单易用性使得计算机网络迅速被普及。 - - 在没有浏览器的年代,上网是这样的。 - - ![](./res/before-browser.jpg) - - 有了浏览器以后,上网是这样的。 - - ![](./res/after-browser.jpg) - -#### TCP/IP模型 - -实现网络通信的基础是网络通信协议,这些协议通常是由[互联网工程任务组](https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%94%E7%BD%91%E5%B7%A5%E7%A8%8B%E4%BB%BB%E5%8A%A1%E7%BB%84) (IETF)制定的。所谓“协议”就是通信计算机双方必须共同遵从的一组约定,例如怎样建立连接、怎样互相识别等,网络协议的三要素是:语法、语义和时序。构成我们今天使用的Internet的基础的是TCP/IP协议族,所谓协议族就是一系列的协议及其构成的通信模型,我们通常也把这套东西称为TCP/IP模型。与国际标准化组织发布的OSI/RM这个七层模型不同,TCP/IP是一个四层模型,也就是说,该模型将我们使用的网络从逻辑上分解为四个层次,自底向上依次是:网络接口层、网络层、传输层和应用层,如下图所示。 - -![](./res/TCP-IP-model.png) - -IP通常被翻译为网际协议,它服务于网络层,主要实现了寻址和路由的功能。接入网络的每一台主机都需要有自己的IP地址,IP地址就是主机在计算机网络上的身份标识。当然由于IPv4地址的匮乏,我们平常在家里、办公室以及其他可以接入网络的公共区域上网时获得的IP地址并不是全球唯一的IP地址,而是一个[局域网(LAN)](https://zh.wikipedia.org/zh-hans/%E5%B1%80%E5%9F%9F%E7%BD%91)中的内部IP地址,通过[网络地址转换(NAT)服务](https://zh.wikipedia.org/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2)我们也可以实现对网络的访问。计算机网络上有大量的被我们称为“[路由器](https://zh.wikipedia.org/wiki/%E8%B7%AF%E7%94%B1%E5%99%A8)”的网络中继设备,它们会存储转发我们发送到网络上的数据分组,让从源头发出的数据最终能够找到传送到目的地通路,这项功能就是所谓的路由。 - -TCP全称传输控制协议,它是基于IP提供的寻址和路由服务而建立起来的负责实现端到端可靠传输的协议,之所以将TCP称为可靠的传输协议是因为TCP向调用者承诺了三件事情: - -1. 数据不传丢不传错(利用握手、校验和重传机制可以实现)。 -2. 流量控制(通过滑动窗口匹配数据发送者和接收者之间的传输速度)。 -3. 拥塞控制(通过RTT时间以及对滑动窗口的控制缓解网络拥堵)。 - - - -#### 网络应用模式 - -1. C/S模式和B/S模式。这里的C指的是Client(客户端),通常是一个需要安装到某个宿主操作系统上的应用程序;而B指的是Browser(浏览器),它几乎是所有图形化操作系统都默认安装了的一个应用软件;通过C或B都可以实现对S(服务器)的访问。关于二者的比较和讨论在网络上有一大堆的文章,在此我们就不再浪费笔墨了。 -2. 去中心化的网络应用模式。不管是B/S还是C/S都需要服务器的存在,服务器就是整个应用模式的中心,而去中心化的网络应用通常没有固定的服务器或者固定的客户端,所有应用的使用者既可以作为资源的提供者也可以作为资源的访问者。 - -### 基于HTTP协议的网络资源访问 - -#### HTTP(超文本传输协议) - -HTTP是超文本传输协议(Hyper-Text Transfer Proctol)的简称,维基百科上对HTTP的解释是:超文本传输协议是一种用于分布式、协作式和超媒体信息系统的应用层协议,它是[万维网](https://zh.wikipedia.org/wiki/%E5%85%A8%E7%90%83%E8%B3%87%E8%A8%8A%E7%B6%B2)数据通信的基础,设计HTTP最初的目的是为了提供一种发布和接收[HTML](https://zh.wikipedia.org/wiki/HTML)页面的方法,通过HTTP或者[HTTPS](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8%E5%8D%8F%E8%AE%AE)(超文本传输安全协议)请求的资源由URI([统一资源标识符](https://zh.wikipedia.org/wiki/%E7%B5%B1%E4%B8%80%E8%B3%87%E6%BA%90%E6%A8%99%E8%AD%98%E7%AC%A6))来标识。关于HTTP的更多内容,我们推荐阅读阮一峰老师的[《HTTP 协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html),简单的说,通过HTTP我们可以获取网络上的(基于字符的)资源,开发中经常会用到的网络API(有的地方也称之为网络数据接口)就是基于HTTP来实现数据传输的。 - -#### JSON格式 - -**JSON**(**J**ava**S**cript **O**bject **N**otation)是一种轻量级的数据交换语言,该语言以易于让人阅读的文字(纯文本)为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是最初只是Javascript中一种创建对象的字面量语法,但它在当下更是一种独立于语言的数据格式,很多编程语言都支持JSON格式数据的生成和解析,Python内置的json模块也提供了这方面的功能。由于JSON是纯文本,它和[XML](https://zh.wikipedia.org/wiki/XML)一样都适用于异构系统之间的数据交换,而相较于XML,JSON显得更加的轻便和优雅。下面是表达同样信息的XML和JSON,而JSON的优势是相当直观的。 - -XML的例子: - -```XML - - - Alice - Bob - Will you marry me? - -``` - -JSON的例子: - -```JSON -{ - 'from': 'Alice', - 'to': 'Bob', - 'content': 'Will you marry me?' -} -``` - -#### requests库 - -requests是一个基于HTTP协议来使用网络的第三库,其[官方网站](http://cn.python-requests.org/zh_CN/latest/)有这样的一句介绍它的话:“Requests是唯一的一个**非转基因**的Python HTTP库,人类可以安全享用。”简单的说,使用requests库可以非常方便的使用HTTP,避免安全缺陷、冗余代码以及“重复发明轮子”(行业黑话,通常用在软件工程领域表示重新创造一个已有的或是早已被优化過的基本方法)。前面的文章中我们已经使用过这个库,下面我们还是通过requests来实现一个访问网络数据接口并从中获取美女图片下载链接然后下载美女图片到本地的例子程序,程序中使用了[天行数据](https://www.tianapi.com/)提供的网络API。 - -我们可以先通过pip安装requests及其依赖库。 - -```Shell -pip install requests -``` - -如果使用PyCharm作为开发工具,可以直接在代码中书写`import requests`,然后通过代码修复功能来自动下载安装requests。 - -```Python -from time import time -from threading import Thread - -import requests - - -# 继承Thread类创建自定义的线程类 -class DownloadHanlder(Thread): - - def __init__(self, url): - super().__init__() - self.url = url - - def run(self): - filename = self.url[self.url.rfind('/') + 1:] - resp = requests.get(self.url) - with open('/Users/Hao/' + filename, 'wb') as f: - f.write(resp.content) - - -def main(): - # 通过requests模块的get函数获取网络资源 - # 下面的代码中使用了天行数据接口提供的网络API - # 要使用该数据接口需要在天行数据的网站上注册 - # 然后用自己的Key替换掉下面代码的中APIKey即可 - resp = requests.get( - 'http://api.tianapi.com/meinv/?key=APIKey&num=10') - # 将服务器返回的JSON格式的数据解析为字典 - data_model = resp.json() - for mm_dict in data_model['newslist']: - url = mm_dict['picUrl'] - # 通过多线程的方式实现图片下载 - DownloadHanlder(url).start() - - -if __name__ == '__main__': - main() - -``` - -### 基于传输层协议的套接字编程 - -套接字这个词对很多不了解网络编程的人来说显得非常晦涩和陌生,其实说得通俗点,套接字就是一套用[C语言](https://zh.wikipedia.org/wiki/C%E8%AF%AD%E8%A8%80)写成的应用程序开发库,主要用于实现进程间通信和网络编程,在网络应用开发中被广泛使用。在Python中也可以基于套接字来使用传输层提供的传输服务,并基于此开发自己的网络应用。实际开发中使用的套接字可以分为三类:流套接字(TCP套接字)、数据报套接字和原始套接字。 - -#### TCP套接字 - -所谓TCP套接字就是使用TCP协议提供的传输服务来实现网络通信的编程接口。在Python中可以通过创建socket对象并指定type属性为SOCK_STREAM来使用TCP套接字。由于一台主机可能拥有多个IP地址,而且很有可能会配置多个不同的服务,所以作为服务器端的程序,需要在创建套接字对象后将其绑定到指定的IP地址和端口上。这里的端口并不是物理设备而是对IP地址的扩展,用于区分不同的服务,例如我们通常将HTTP服务跟80端口绑定,而MySQL数据库服务默认绑定在3306端口,这样当服务器收到用户请求时就可以根据端口号来确定到底用户请求的是HTTP服务器还是数据库服务器提供的服务。端口的取值范围是0~65535,而1024以下的端口我们通常称之为“著名端口”(留给像FTP、HTTP、SMTP等“著名服务”使用的端口,有的地方也称之为“周知端口”),自定义的服务通常不使用这些端口,除非自定义的是HTTP或FTP这样的著名服务。 - -下面的代码实现了一个提供时间日期的服务器。 - -```Python -from socket import socket, SOCK_STREAM, AF_INET -from datetime import datetime - - -def main(): - # 1.创建套接字对象并指定使用哪种传输服务 - # family=AF_INET - IPv4地址 - # family=AF_INET6 - IPv6地址 - # type=SOCK_STREAM - TCP套接字 - # type=SOCK_DGRAM - UDP套接字 - # type=SOCK_RAW - 原始套接字 - server = socket(family=AF_INET, type=SOCK_STREAM) - # 2.绑定IP地址和端口(端口用于区分不同的服务) - # 同一时间在同一个端口上只能绑定一个服务否则报错 - server.bind(('192.168.1.2', 6789)) - # 3.开启监听 - 监听客户端连接到服务器 - # 参数512可以理解为连接队列的大小 - server.listen(512) - print('服务器启动开始监听...') - while True: - # 4.通过循环接收客户端的连接并作出相应的处理(提供服务) - # accept方法是一个阻塞方法如果没有客户端连接到服务器代码不会向下执行 - # accept方法返回一个元组其中的第一个元素是客户端对象 - # 第二个元素是连接到服务器的客户端的地址(由IP和端口两部分构成) - client, addr = server.accept() - print(str(addr) + '连接到了服务器.') - # 5.发送数据 - client.send(str(datetime.now()).encode('utf-8')) - # 6.断开连接 - client.close() - - -if __name__ == '__main__': - main() - -``` - -运行服务器程序后我们可以通过Windows系统的telnet来访问该服务器,结果如下图所示。 - -```Shell -telnet 192.168.1.2 6789 -``` - -![](./res/telnet.png) - -当然我们也可以通过Python的程序来实现TCP客户端的功能,相较于实现服务器程序,实现客户端程序就简单多了,代码如下所示。 - -```Python -from socket import socket - - -def main(): - # 1.创建套接字对象默认使用IPv4和TCP协议 - client = socket() - # 2.连接到服务器(需要指定IP地址和端口) - client.connect(('192.168.1.2', 6789)) - # 3.从服务器接收数据 - print(client.recv(1024).decode('utf-8')) - client.close() - - -if __name__ == '__main__': - main() - -``` - -需要注意的是,上面的服务器并没有使用多线程或者异步I/O的处理方式,这也就意味着当服务器与一个客户端处于通信状态时,其他的客户端只能排队等待。很显然,这样的服务器并不能满足我们的需求,我们需要的服务器是能够同时接纳和处理多个用户请求的。下面我们来设计一个使用多线程技术处理多个用户请求的服务器,该服务器会向连接到服务器的客户端发送一张图片。 - -服务器端代码: - -```Python -from socket import socket, SOCK_STREAM, AF_INET -from base64 import b64encode -from json import dumps -from threading import Thread - - -def main(): - - # 自定义线程类 - class FileTransferHandler(Thread): - - def __init__(self, cclient): - super().__init__() - self.cclient = cclient - - def run(self): - my_dict = {} - my_dict['filename'] = 'guido.jpg' - # JSON是纯文本不能携带二进制数据 - # 所以图片的二进制数据要处理成base64编码 - my_dict['filedata'] = data - # 通过dumps函数将字典处理成JSON字符串 - json_str = dumps(my_dict) - # 发送JSON字符串 - self.cclient.send(json_str.encode('utf-8')) - self.cclient.close() - - # 1.创建套接字对象并指定使用哪种传输服务 - server = socket() - # 2.绑定IP地址和端口(区分不同的服务) - server.bind(('192.168.1.2', 5566)) - # 3.开启监听 - 监听客户端连接到服务器 - server.listen(512) - print('服务器启动开始监听...') - with open('guido.jpg', 'rb') as f: - # 将二进制数据处理成base64再解码成字符串 - data = b64encode(f.read()).decode('utf-8') - while True: - client, addr = server.accept() - # 启动一个线程来处理客户端的请求 - FileTransferHandler(client).start() - - -if __name__ == '__main__': - main() - -``` - -客户端代码: - -```Python -from socket import socket -from json import loads -from base64 import b64decode - - -def main(): - client = socket() - client.connect(('192.168.1.2', 5566)) - # 定义一个保存二进制数据的对象 - in_data = bytes() - # 由于不知道服务器发送的数据有多大每次接收1024字节 - data = client.recv(1024) - while data: - # 将收到的数据拼接起来 - in_data += data - data = client.recv(1024) - # 将收到的二进制数据解码成JSON字符串并转换成字典 - # loads函数的作用就是将JSON字符串转成字典对象 - my_dict = loads(in_data.decode('utf-8')) - filename = my_dict['filename'] - filedata = my_dict['filedata'].encode('utf-8') - with open('/Users/Hao/' + filename, 'wb') as f: - # 将base64格式的数据解码成二进制数据并写入文件 - f.write(b64decode(filedata)) - print('图片已保存.') - - -if __name__ == '__main__': - main() - -``` - -在这个案例中,我们使用了JSON作为数据传输的格式(通过JSON格式对传输的数据进行了序列化和反序列化的操作),但是JSON并不能携带二进制数据,因此对图片的二进制数据进行了Base64编码的处理。Base64是一种用64个字符表示所有二进制数据的编码方式,通过将二进制数据每6位一组的方式重新组织,刚好可以使用0~9的数字、大小写字母以及“+”和“/”总共64个字符表示从`000000`到`111111`的64种状态。[维基百科](https://zh.wikipedia.org/wiki/Base64)上有关于Base64编码的详细讲解,不熟悉Base64的读者可以自行阅读。 - -> **说明**:上面的代码主要为了讲解网络编程的相关内容因此并没有对异常状况进行处理,请读者自行添加异常处理代码来增强程序的健壮性。 - -#### UDP套接字 - -传输层除了有可靠的传输协议TCP之外,还有一种非常轻便的传输协议叫做用户数据报协议,简称UDP。TCP和UDP都是提供端到端传输服务的协议,二者的差别就如同打电话和发短信的区别,后者不对传输的可靠性和可达性做出任何承诺从而避免了TCP中握手和重传的开销,所以在强调性能和而不是数据完整性的场景中(例如传输网络音视频数据),UDP可能是更好的选择。可能大家会注意到一个现象,就是在观看网络视频时,有时会出现卡顿,有时会出现花屏,这无非就是部分数据传丢或传错造成的。在Python中也可以使用UDP套接字来创建网络应用,对此我们不进行赘述,有兴趣的读者可以自行研究。 diff --git "a/Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" "b/Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" deleted file mode 100644 index 36c5aafa2..000000000 --- "a/Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" +++ /dev/null @@ -1,125 +0,0 @@ -## 网络应用开发 - -### 发送电子邮件 - -在即时通信软件如此发达的今天,电子邮件仍然是互联网上使用最为广泛的应用之一,公司向应聘者发出录用通知、网站向用户发送一个激活账号的链接、银行向客户推广它们的理财产品等几乎都是通过电子邮件来完成的,而这些任务应该都是由程序自动完成的。 - -就像我们可以用HTTP(超文本传输协议)来访问一个网站一样,发送邮件要使用SMTP(简单邮件传输协议),SMTP也是一个建立在TCP(传输控制协议)提供的可靠数据传输服务的基础上的应用级协议,它规定了邮件的发送者如何跟发送邮件的服务器进行通信的细节,而Python中的smtplib模块将这些操作简化成了几个简单的函数。 - -下面的代码演示了如何在Python发送邮件。 - -```Python -from smtplib import SMTP -from email.header import Header -from email.mime.text import MIMEText - - -def main(): - # 请自行修改下面的邮件发送者和接收者 - sender = 'abcdefg@126.com' - receivers = ['uvwxyz@qq.com', 'uvwxyz@126.com'] - message = MIMEText('用Python发送邮件的示例代码.', 'plain', 'utf-8') - message['From'] = Header('王大锤', 'utf-8') - message['To'] = Header('骆昊', 'utf-8') - message['Subject'] = Header('示例代码实验邮件', 'utf-8') - smtper = SMTP('smtp.126.com') - # 请自行修改下面的登录口令 - smtper.login(sender, 'secretpass') - smtper.sendmail(sender, receivers, message.as_string()) - print('邮件发送完成!') - - -if __name__ == '__main__': - main() - -``` - -如果要发送带有附件的邮件,那么可以按照下面的方式进行操作。 - -```Python -from smtplib import SMTP -from email.header import Header -from email.mime.text import MIMEText -from email.mime.image import MIMEImage -from email.mime.multipart import MIMEMultipart - -import urllib - - -def main(): - # 创建一个带附件的邮件消息对象 - message = MIMEMultipart() - - # 创建文本内容 - text_content = MIMEText('附件中有本月数据请查收', 'plain', 'utf-8') - message['Subject'] = Header('本月数据', 'utf-8') - # 将文本内容添加到邮件消息对象中 - message.attach(text_content) - - # 读取文件并将文件作为附件添加到邮件消息对象中 - with open('/Users/Hao/Desktop/hello.txt', 'rb') as f: - txt = MIMEText(f.read(), 'base64', 'utf-8') - txt['Content-Type'] = 'text/plain' - txt['Content-Disposition'] = 'attachment; filename=hello.txt' - message.attach(txt) - # 读取文件并将文件作为附件添加到邮件消息对象中 - with open('/Users/Hao/Desktop/汇总数据.xlsx', 'rb') as f: - xls = MIMEText(f.read(), 'base64', 'utf-8') - xls['Content-Type'] = 'application/vnd.ms-excel' - xls['Content-Disposition'] = 'attachment; filename=month-data.xlsx' - message.attach(xls) - - # 创建SMTP对象 - smtper = SMTP('smtp.126.com') - # 开启安全连接 - # smtper.starttls() - sender = 'abcdefg@126.com' - receivers = ['uvwxyz@qq.com'] - # 登录到SMTP服务器 - # 请注意此处不是使用密码而是邮件客户端授权码进行登录 - # 对此有疑问的读者可以联系自己使用的邮件服务器客服 - smtper.login(sender, 'secretpass') - # 发送邮件 - smtper.sendmail(sender, receivers, message.as_string()) - # 与邮件服务器断开连接 - smtper.quit() - print('发送完成!') - - -if __name__ == '__main__': - main() - -``` - -### 发送短信 - -发送短信也是项目中常见的功能,网站的注册码、验证码、营销信息基本上都是通过短信来发送给用户的。在下面的代码中我们使用了[互亿无线](http://www.ihuyi.com/)短信平台(该平台为注册用户提供了50条免费短信以及常用开发语言发送短信的demo,可以登录该网站并在用户自服务页面中对短信进行配置)提供的API接口实现了发送短信的服务,当然国内的短信平台很多,读者可以根据自己的需要进行选择(通常会考虑费用预算、短信达到率、使用的难易程度等指标),如果需要在商业项目中使用短信服务建议购买短信平台提供的套餐服务。 - -```Python -import urllib.parse -import http.client -import json - - -def main(): - host = "106.ihuyi.com" - sms_send_uri = "/webservice/sms.php?method=Submit" - # 下面的参数需要填入自己注册的账号和对应的密码 - params = urllib.parse.urlencode({'account': '你自己的账号', 'password' : '你自己的密码', 'content': '您的验证码是:147258。请不要把验证码泄露给其他人。', 'mobile': '接收者的手机号', 'format':'json' }) - print(params) - headers = {'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'} - conn = http.client.HTTPConnection(host, port=80, timeout=30) - conn.request('POST', sms_send_uri, params, headers) - response = conn.getresponse() - response_str = response.read() - jsonstr = response_str.decode('utf-8') - print(json.loads(jsonstr)) - conn.close() - - -if __name__ == '__main__': - main() - - -``` - diff --git a/Day01-15/code/Day01/flag.py b/Day01-15/code/Day01/flag.py new file mode 100755 index 000000000..2029b08e6 --- /dev/null +++ b/Day01-15/code/Day01/flag.py @@ -0,0 +1,74 @@ +""" +用Python的turtle模块绘制国旗 +""" +import turtle + + +def draw_rectangle(x, y, width, height): + """绘制矩形""" + turtle.goto(x, y) + turtle.pencolor('red') + turtle.fillcolor('red') + turtle.begin_fill() + for i in range(2): + turtle.forward(width) + turtle.left(90) + turtle.forward(height) + turtle.left(90) + turtle.end_fill() + + +def draw_star(x, y, radius): + """绘制五角星""" + turtle.setpos(x, y) + pos1 = turtle.pos() + turtle.circle(-radius, 72) + pos2 = turtle.pos() + turtle.circle(-radius, 72) + pos3 = turtle.pos() + turtle.circle(-radius, 72) + pos4 = turtle.pos() + turtle.circle(-radius, 72) + pos5 = turtle.pos() + turtle.color('yellow', 'yellow') + turtle.begin_fill() + turtle.goto(pos3) + turtle.goto(pos1) + turtle.goto(pos4) + turtle.goto(pos2) + turtle.goto(pos5) + turtle.end_fill() + + +def main(): + """主程序""" + turtle.speed(12) + turtle.penup() + x, y = -270, -180 + # 画国旗主体 + width, height = 540, 360 + draw_rectangle(x, y, width, height) + # 画大星星 + pice = 22 + center_x, center_y = x + 5 * pice, y + height - pice * 5 + turtle.goto(center_x, center_y) + turtle.left(90) + turtle.forward(pice * 3) + turtle.right(90) + draw_star(turtle.xcor(), turtle.ycor(), pice * 3) + x_poses, y_poses = [10, 12, 12, 10], [2, 4, 7, 9] + # 画小星星 + for x_pos, y_pos in zip(x_poses, y_poses): + turtle.goto(x + x_pos * pice, y + height - y_pos * pice) + turtle.left(turtle.towards(center_x, center_y) - turtle.heading()) + turtle.forward(pice) + turtle.right(90) + draw_star(turtle.xcor(), turtle.ycor(), pice) + # 隐藏海龟 + turtle.ht() + # 显示绘图窗口 + turtle.mainloop() + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/Day01-15/code/Day01/hello.py b/Day01-15/code/Day01/hello.py new file mode 100755 index 000000000..0ff774703 --- /dev/null +++ b/Day01-15/code/Day01/hello.py @@ -0,0 +1,22 @@ +""" +第一个Python程序 - hello, world! +向伟大的Dennis M. Ritchie先生致敬 + +Version: 0.1 +Author: 骆昊 +Date: 2018-02-26 + +请将该文件命名为hello.py + +使用Windows的小伙伴可以在命令行提示下通过下面的命令运行该程序 +python hello.py + +对于使用Linux或macOS的小伙伴可以打开终端并键入下面的命令来运行程序 +python3 hello.py +""" + +print('hello, world!') +# print("你好,世界!") +print('你好', '世界') +print('hello', 'world', sep=', ', end='!') +print('goodbye, world', end='!\n') diff --git a/Day01-15/code/Day01/peppa_pig.py b/Day01-15/code/Day01/peppa_pig.py new file mode 100755 index 000000000..21cda98ee --- /dev/null +++ b/Day01-15/code/Day01/peppa_pig.py @@ -0,0 +1,204 @@ +""" +绘制小猪佩奇 +""" +from turtle import * + + +def nose(x,y): + """画鼻子""" + penup() + # 将海龟移动到指定的坐标 + goto(x,y) + pendown() + # 设置海龟的方向(0-东、90-北、180-西、270-南) + setheading(-30) + begin_fill() + a = 0.4 + for i in range(120): + if 0 <= i < 30 or 60 <= i <90: + a = a + 0.08 + # 向左转3度 + left(3) + # 向前走 + forward(a) + else: + a = a - 0.08 + left(3) + forward(a) + end_fill() + penup() + setheading(90) + forward(25) + setheading(0) + forward(10) + pendown() + # 设置画笔的颜色(红, 绿, 蓝) + pencolor(255, 155, 192) + setheading(10) + begin_fill() + circle(5) + color(160, 82, 45) + end_fill() + penup() + setheading(0) + forward(20) + pendown() + pencolor(255, 155, 192) + setheading(10) + begin_fill() + circle(5) + color(160, 82, 45) + end_fill() + + +def head(x, y): + """画头""" + color((255, 155, 192), "pink") + penup() + goto(x,y) + setheading(0) + pendown() + begin_fill() + setheading(180) + circle(300, -30) + circle(100, -60) + circle(80, -100) + circle(150, -20) + circle(60, -95) + setheading(161) + circle(-300, 15) + penup() + goto(-100, 100) + pendown() + setheading(-30) + a = 0.4 + for i in range(60): + if 0<= i < 30 or 60 <= i < 90: + a = a + 0.08 + lt(3) #向左转3度 + fd(a) #向前走a的步长 + else: + a = a - 0.08 + lt(3) + fd(a) + end_fill() + + +def ears(x,y): + """画耳朵""" + color((255, 155, 192), "pink") + penup() + goto(x, y) + pendown() + begin_fill() + setheading(100) + circle(-50, 50) + circle(-10, 120) + circle(-50, 54) + end_fill() + penup() + setheading(90) + forward(-12) + setheading(0) + forward(30) + pendown() + begin_fill() + setheading(100) + circle(-50, 50) + circle(-10, 120) + circle(-50, 56) + end_fill() + + +def eyes(x,y): + """画眼睛""" + color((255, 155, 192), "white") + penup() + setheading(90) + forward(-20) + setheading(0) + forward(-95) + pendown() + begin_fill() + circle(15) + end_fill() + color("black") + penup() + setheading(90) + forward(12) + setheading(0) + forward(-3) + pendown() + begin_fill() + circle(3) + end_fill() + color((255, 155, 192), "white") + penup() + seth(90) + forward(-25) + seth(0) + forward(40) + pendown() + begin_fill() + circle(15) + end_fill() + color("black") + penup() + setheading(90) + forward(12) + setheading(0) + forward(-3) + pendown() + begin_fill() + circle(3) + end_fill() + + +def cheek(x,y): + """画脸颊""" + color((255, 155, 192)) + penup() + goto(x,y) + pendown() + setheading(0) + begin_fill() + circle(30) + end_fill() + + +def mouth(x,y): + """画嘴巴""" + color(239, 69, 19) + penup() + goto(x, y) + pendown() + setheading(-80) + circle(30, 40) + circle(40, 80) + + +def setting(): + """设置参数""" + pensize(4) + # 隐藏海龟 + hideturtle() + colormode(255) + color((255, 155, 192), "pink") + setup(840, 500) + speed(10) + + +def main(): + """主函数""" + setting() + nose(-100, 100) + head(-69, 167) + ears(0, 160) + eyes(0, 140) + cheek(80, 10) + mouth(-20, 30) + done() + + +if __name__ == '__main__': + main() diff --git a/Day01-15/Day02/centigrade.py b/Day01-15/code/Day02/centigrade.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day02/centigrade.py rename to Day01-15/code/Day02/centigrade.py index e6d5f55a6..6b35d393b --- a/Day01-15/Day02/centigrade.py +++ b/Day01-15/code/Day02/centigrade.py @@ -1,12 +1,10 @@ """ - 将华氏温度转换为摄氏温度 F = 1.8C + 32 Version: 0.1 Author: 骆昊 Date: 2018-02-27 - """ f = float(input('请输入华氏温度: ')) diff --git a/Day01-15/code/Day02/circle.py b/Day01-15/code/Day02/circle.py new file mode 100755 index 000000000..314f0bbb9 --- /dev/null +++ b/Day01-15/code/Day02/circle.py @@ -0,0 +1,14 @@ +""" +输入半径计算圆的周长和面积 + +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/Day01-15/Day02/leap.py b/Day01-15/code/Day02/leap.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day02/leap.py rename to Day01-15/code/Day02/leap.py index d83cc74eb..ba76d8bbc --- a/Day01-15/Day02/leap.py +++ b/Day01-15/code/Day02/leap.py @@ -1,11 +1,9 @@ """ - 输入年份 如果是闰年输出True 否则输出False Version: 0.1 Author: 骆昊 Date: 2018-02-27 - """ year = int(input('请输入年份: ')) diff --git a/Day01-15/Day02/operator.py b/Day01-15/code/Day02/operator.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day02/operator.py rename to Day01-15/code/Day02/operator.py index 0bbd9fc99..6940175c1 --- a/Day01-15/Day02/operator.py +++ b/Day01-15/code/Day02/operator.py @@ -1,11 +1,9 @@ """ - 运算符的使用 Version: 0.1 Author: 骆昊 Date: 2018-02-27 - """ a = 5 diff --git a/Day01-15/code/Day02/strings.py b/Day01-15/code/Day02/strings.py new file mode 100755 index 000000000..7a76383b4 --- /dev/null +++ b/Day01-15/code/Day02/strings.py @@ -0,0 +1,21 @@ +""" +字符串常用操作 + +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/Day01-15/Day02/variable1.py b/Day01-15/code/Day02/variable1.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day02/variable1.py rename to Day01-15/code/Day02/variable1.py index f2c5fe379..1aa3d4ef2 --- a/Day01-15/Day02/variable1.py +++ b/Day01-15/code/Day02/variable1.py @@ -1,11 +1,9 @@ """ - 使用变量保存数据并进行操作 Version: 0.1 Author: 骆昊 Date: 2018-02-27 - """ a = 321 diff --git a/Day01-15/Day02/variable2.py b/Day01-15/code/Day02/variable2.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day02/variable2.py rename to Day01-15/code/Day02/variable2.py index 398cc69f2..343d39d86 --- a/Day01-15/Day02/variable2.py +++ b/Day01-15/code/Day02/variable2.py @@ -1,11 +1,9 @@ """ - 将input函数输入的数据保存在变量中并进行操作 Version: 0.1 Author: 骆昊 Date: 2018-02-27 - """ a = int(input('a = ')) diff --git a/Day01-15/Day02/variable3.py b/Day01-15/code/Day02/variable3.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day02/variable3.py rename to Day01-15/code/Day02/variable3.py index 4e4b4e175..5d1616a63 --- a/Day01-15/Day02/variable3.py +++ b/Day01-15/code/Day02/variable3.py @@ -1,11 +1,9 @@ """ - 格式化输出 Version: 0.1 Author: 骆昊 Date: 2018-02-27 - """ a = int(input('a = ')) diff --git a/Day01-15/Day02/variable4.py b/Day01-15/code/Day02/variable4.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day02/variable4.py rename to Day01-15/code/Day02/variable4.py index 62a08233f..6f632ab44 --- a/Day01-15/Day02/variable4.py +++ b/Day01-15/code/Day02/variable4.py @@ -1,11 +1,9 @@ """ - 检查变量的类型 Version: 0.1 Author: 骆昊 Date: 2018-02-27 - """ a = 100 diff --git a/Day01-15/Day02/variable5.py b/Day01-15/code/Day02/variable5.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day02/variable5.py rename to Day01-15/code/Day02/variable5.py index 6560f2dc7..5d1c90f0a --- a/Day01-15/Day02/variable5.py +++ b/Day01-15/code/Day02/variable5.py @@ -1,11 +1,9 @@ """ - 类型转换 Version: 0.1 Author: 骆昊 Date: 2018-02-27 - """ a = 100 diff --git a/Day01-15/code/Day03/conversion.py b/Day01-15/code/Day03/conversion.py new file mode 100755 index 000000000..aac13ca64 --- /dev/null +++ b/Day01-15/code/Day03/conversion.py @@ -0,0 +1,16 @@ +""" +英制单位英寸和公制单位厘米互换 + +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/Day01-15/code/Day03/grade.py b/Day01-15/code/Day03/grade.py new file mode 100755 index 000000000..f78a46858 --- /dev/null +++ b/Day01-15/code/Day03/grade.py @@ -0,0 +1,25 @@ +""" +百分制成绩转等级制成绩 +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/Day01-15/code/Day03/piecewise.py b/Day01-15/code/Day03/piecewise.py new file mode 100755 index 000000000..4ebc68694 --- /dev/null +++ b/Day01-15/code/Day03/piecewise.py @@ -0,0 +1,19 @@ +""" +分段函数求值 + 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/Day01-15/code/Day03/rolldice.py b/Day01-15/code/Day03/rolldice.py new file mode 100755 index 000000000..3a232a779 --- /dev/null +++ b/Day01-15/code/Day03/rolldice.py @@ -0,0 +1,23 @@ +""" +掷骰子决定做什么事情 + +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/Day01-15/code/Day03/tax.py b/Day01-15/code/Day03/tax.py new file mode 100755 index 000000000..819642e0f --- /dev/null +++ b/Day01-15/code/Day03/tax.py @@ -0,0 +1,39 @@ +""" +输入月收入和五险一金计算个人所得税 +说明:写这段代码时新的个人所得税计算方式还没有颁布 + +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/Day01-15/code/Day03/triangle.py b/Day01-15/code/Day03/triangle.py new file mode 100755 index 000000000..890e08297 --- /dev/null +++ b/Day01-15/code/Day03/triangle.py @@ -0,0 +1,20 @@ +""" +判断输入的边长能否构成三角形 +如果能则计算出三角形的周长和面积 + +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/Day01-15/Day03/verify.py b/Day01-15/code/Day03/verify.py old mode 100644 new mode 100755 similarity index 85% rename from Day01-15/Day03/verify.py rename to Day01-15/code/Day03/verify.py index e2587dc44..d0d162c80 --- a/Day01-15/Day03/verify.py +++ b/Day01-15/code/Day03/verify.py @@ -1,13 +1,10 @@ """ - 用户身份验证 Version: 0.1 Author: 骆昊 Date: 2018-02-28 - """ - # import getpass # from getpass import getpass # from getpass import * @@ -17,6 +14,6 @@ # 输入口令的时候终端中没有回显 # password = getpass.getpass('请输入口令: ') if username == 'admin' and password == '123456': - print('身份验证成功!') + print('身份验证成功!') else: - print('身份验证失败!') + print('身份验证失败!') diff --git a/Day01-15/Day04/for1.py b/Day01-15/code/Day04/for1.py old mode 100644 new mode 100755 similarity index 81% rename from Day01-15/Day04/for1.py rename to Day01-15/code/Day04/for1.py index e2a0783ec..659724d67 --- a/Day01-15/Day04/for1.py +++ b/Day01-15/code/Day04/for1.py @@ -1,15 +1,12 @@ """ - 用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 + sum += x print(sum) diff --git a/Day01-15/Day04/for2.py b/Day01-15/code/Day04/for2.py old mode 100644 new mode 100755 similarity index 91% rename from Day01-15/Day04/for2.py rename to Day01-15/code/Day04/for2.py index 65bd932b6..2bc8aa714 --- a/Day01-15/Day04/for2.py +++ b/Day01-15/code/Day04/for2.py @@ -1,14 +1,12 @@ """ - 用for循环实现1~100之间的偶数求和 Version: 0.1 Author: 骆昊 Date: 2018-03-01 - """ sum = 0 for x in range(2, 101, 2): - sum += x + sum += x print(sum) diff --git a/Day01-15/Day04/for3.py b/Day01-15/code/Day04/for3.py old mode 100644 new mode 100755 similarity index 91% rename from Day01-15/Day04/for3.py rename to Day01-15/code/Day04/for3.py index bfc9d8533..c3ddaa397 --- a/Day01-15/Day04/for3.py +++ b/Day01-15/code/Day04/for3.py @@ -1,15 +1,13 @@ """ - 输入非负整数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 + result *= x print('%d! = %d' % (n, result)) diff --git a/Day01-15/code/Day04/for4.py b/Day01-15/code/Day04/for4.py new file mode 100755 index 000000000..7ae5d2558 --- /dev/null +++ b/Day01-15/code/Day04/for4.py @@ -0,0 +1,20 @@ +""" +输入一个正整数判断它是不是素数 + +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/Day01-15/code/Day04/for5.py b/Day01-15/code/Day04/for5.py new file mode 100755 index 000000000..c4ab64e8a --- /dev/null +++ b/Day01-15/code/Day04/for5.py @@ -0,0 +1,17 @@ +""" +输入两个正整数计算最大公约数和最小公倍数 + +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/Day01-15/Day04/for6.py b/Day01-15/code/Day04/for6.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day04/for6.py rename to Day01-15/code/Day04/for6.py index ed3c0fc73..265f2078f --- a/Day01-15/Day04/for6.py +++ b/Day01-15/code/Day04/for6.py @@ -1,5 +1,4 @@ """ - 打印各种三角形图案 * @@ -23,7 +22,6 @@ Version: 0.1 Author: 骆昊 Date: 2018-03-01 - """ row = int(input('请输入行数: ')) diff --git a/Day01-15/Day04/while1.py b/Day01-15/code/Day04/while1.py old mode 100644 new mode 100755 similarity index 82% rename from Day01-15/Day04/while1.py rename to Day01-15/code/Day04/while1.py index 3a3d6e6cc..8df2afe3f --- a/Day01-15/Day04/while1.py +++ b/Day01-15/code/Day04/while1.py @@ -1,16 +1,14 @@ """ - 用while循环实现1~100求和 Version: 0.1 Author: 骆昊 Date: 2018-03-01 - """ sum = 0 num = 1 while num <= 100: - sum += num - num += 1 + sum += num + num += 1 print(sum) diff --git a/Day01-15/code/Day04/while2.py b/Day01-15/code/Day04/while2.py new file mode 100755 index 000000000..63a95593a --- /dev/null +++ b/Day01-15/code/Day04/while2.py @@ -0,0 +1,13 @@ +""" +用while循环实现1~100之间的偶数求和 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-01 +""" + +sum, num = 0, 2 +while num <= 100: + sum += num + num += 2 +print(sum) diff --git a/Day01-15/code/Day05/chicken.py b/Day01-15/code/Day05/chicken.py new file mode 100755 index 000000000..441b2c2c8 --- /dev/null +++ b/Day01-15/code/Day05/chicken.py @@ -0,0 +1,15 @@ +""" +求解《百钱百鸡》问题 +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/Day01-15/code/Day05/craps.py b/Day01-15/code/Day05/craps.py new file mode 100755 index 000000000..f50cbcc48 --- /dev/null +++ b/Day01-15/code/Day05/craps.py @@ -0,0 +1,47 @@ +""" +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 0 < debt <= money: + break + first = randint(1, 6) + randint(1, 6) + print('玩家摇出了%d点' % first) + if first == 7 or first == 11: + print('玩家胜!') + money += debt + elif first == 2 or first == 3 or first == 12: + print('庄家胜!') + money -= debt + else: + needs_go_on = True + + while needs_go_on: + 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/Day01-15/code/Day05/fibonacci.py b/Day01-15/code/Day05/fibonacci.py new file mode 100755 index 000000000..fdcacbf4d --- /dev/null +++ b/Day01-15/code/Day05/fibonacci.py @@ -0,0 +1,14 @@ +""" +输出斐波那契数列的前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/Day01-15/code/Day05/guess.py b/Day01-15/code/Day05/guess.py new file mode 100755 index 000000000..aef847b55 --- /dev/null +++ b/Day01-15/code/Day05/guess.py @@ -0,0 +1,26 @@ +""" +猜数字游戏 +计算机出一个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/Day01-15/code/Day05/lily.py b/Day01-15/code/Day05/lily.py new file mode 100755 index 000000000..04645e436 --- /dev/null +++ b/Day01-15/code/Day05/lily.py @@ -0,0 +1,16 @@ +""" +找出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/Day01-15/code/Day05/palindrome.py b/Day01-15/code/Day05/palindrome.py new file mode 100755 index 000000000..3cc7af255 --- /dev/null +++ b/Day01-15/code/Day05/palindrome.py @@ -0,0 +1,20 @@ +""" +判断输入的正整数是不是回文数 +回文数是指将一个正整数从左往右排列和从右往左排列值一样的数 + +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/Day01-15/code/Day05/perfect.py b/Day01-15/code/Day05/perfect.py new file mode 100755 index 000000000..ab18c5b96 --- /dev/null +++ b/Day01-15/code/Day05/perfect.py @@ -0,0 +1,20 @@ +""" +找出1~9999之间的所有完美数 +完美数是除自身外其他所有因子的和正好等于这个数本身的数 +例如: 6 = 1 + 2 + 3, 28 = 1 + 2 + 4 + 7 + 14 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-02 +""" +import math + +for num in range(2, 10000): + result = 0 + for factor in range(1, int(math.sqrt(num)) + 1): + if num % factor == 0: + result += factor + if factor > 1 and num // factor != factor: + result += num // factor + if result == num: + print(num) diff --git a/Day01-15/code/Day05/prime.py b/Day01-15/code/Day05/prime.py new file mode 100755 index 000000000..38389dd2e --- /dev/null +++ b/Day01-15/code/Day05/prime.py @@ -0,0 +1,18 @@ +""" +输出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/Day01-15/code/Day05/table.py b/Day01-15/code/Day05/table.py new file mode 100755 index 000000000..47ecd6329 --- /dev/null +++ b/Day01-15/code/Day05/table.py @@ -0,0 +1,12 @@ +""" +输出乘法口诀表(九九表) + +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/Day01-15/code/Day06/function1.py b/Day01-15/code/Day06/function1.py new file mode 100755 index 000000000..5db4d0a3d --- /dev/null +++ b/Day01-15/code/Day06/function1.py @@ -0,0 +1,18 @@ +""" +函数的定义和使用 - 计算组合数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/Day01-15/code/Day06/function2.py b/Day01-15/code/Day06/function2.py new file mode 100755 index 000000000..471eb3ab4 --- /dev/null +++ b/Day01-15/code/Day06/function2.py @@ -0,0 +1,24 @@ +""" +函数的定义和使用 - 求最大公约数和最小公倍数 + +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/Day01-15/code/Day06/function3.py b/Day01-15/code/Day06/function3.py new file mode 100755 index 000000000..1c9b82167 --- /dev/null +++ b/Day01-15/code/Day06/function3.py @@ -0,0 +1,29 @@ +""" +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/Day01-15/code/Day06/function4.py b/Day01-15/code/Day06/function4.py new file mode 100755 index 000000000..7ea326556 --- /dev/null +++ b/Day01-15/code/Day06/function4.py @@ -0,0 +1,40 @@ +""" +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/Day01-15/code/Day06/function5.py b/Day01-15/code/Day06/function5.py new file mode 100755 index 000000000..2dbc5739f --- /dev/null +++ b/Day01-15/code/Day06/function5.py @@ -0,0 +1,52 @@ +""" +函数的参数 +- 位置参数 +- 可变参数 +- 关键字参数 +- 命名关键字参数 + +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/Day01-15/code/Day06/function6.py b/Day01-15/code/Day06/function6.py new file mode 100755 index 000000000..d59db30b6 --- /dev/null +++ b/Day01-15/code/Day06/function6.py @@ -0,0 +1,45 @@ +""" +作用域问题 + +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/Day01-15/code/Day07/avgscore.py b/Day01-15/code/Day07/avgscore.py new file mode 100755 index 000000000..9a4bd5b2d --- /dev/null +++ b/Day01-15/code/Day07/avgscore.py @@ -0,0 +1,25 @@ +""" +输入学生考试成绩计算平均分 + +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/Day01-15/code/Day07/dict1.py b/Day01-15/code/Day07/dict1.py new file mode 100755 index 000000000..56eee3182 --- /dev/null +++ b/Day01-15/code/Day07/dict1.py @@ -0,0 +1,32 @@ +""" +定义和使用字典 + +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/Day01-15/code/Day07/dict2.py b/Day01-15/code/Day07/dict2.py new file mode 100755 index 000000000..ce4add26d --- /dev/null +++ b/Day01-15/code/Day07/dict2.py @@ -0,0 +1,31 @@ +""" +字典的常用操作 + +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/Day01-15/code/Day07/fibonacci.py b/Day01-15/code/Day07/fibonacci.py new file mode 100755 index 000000000..33c15b31c --- /dev/null +++ b/Day01-15/code/Day07/fibonacci.py @@ -0,0 +1,20 @@ +""" +生成斐波拉切数列 + +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/Day01-15/code/Day07/findmax.py b/Day01-15/code/Day07/findmax.py new file mode 100755 index 000000000..85bec717f --- /dev/null +++ b/Day01-15/code/Day07/findmax.py @@ -0,0 +1,27 @@ +""" +找出列表中最大或最小的元素 + +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/Day01-15/code/Day07/list1.py b/Day01-15/code/Day07/list1.py new file mode 100755 index 000000000..5d0586ecd --- /dev/null +++ b/Day01-15/code/Day07/list1.py @@ -0,0 +1,38 @@ +""" +定义和使用列表 +- 用下标访问元素 +- 添加元素 +- 删除元素 + +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/Day01-15/code/Day07/list2.py b/Day01-15/code/Day07/list2.py new file mode 100755 index 000000000..f4ed7ebe5 --- /dev/null +++ b/Day01-15/code/Day07/list2.py @@ -0,0 +1,37 @@ +""" +列表常用操作 +- 列表连接 +- 获取长度 +- 遍历列表 +- 列表切片 +- 列表排序 +- 列表反转 +- 查找元素 + +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/Day01-15/code/Day07/list3.py b/Day01-15/code/Day07/list3.py new file mode 100755 index 000000000..e471af34c --- /dev/null +++ b/Day01-15/code/Day07/list3.py @@ -0,0 +1,45 @@ +""" +生成列表 +- 用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/Day01-15/code/Day07/lottery.py b/Day01-15/code/Day07/lottery.py new file mode 100755 index 000000000..c5d72d85a --- /dev/null +++ b/Day01-15/code/Day07/lottery.py @@ -0,0 +1,48 @@ +""" +双色球随机选号程序 + +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/Day01-15/code/Day07/marquee.py b/Day01-15/code/Day07/marquee.py new file mode 100755 index 000000000..9d6f546de --- /dev/null +++ b/Day01-15/code/Day07/marquee.py @@ -0,0 +1,24 @@ +""" +输入学生考试成绩计算平均分 + +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/Day01-15/code/Day07/scoretable.py b/Day01-15/code/Day07/scoretable.py new file mode 100755 index 000000000..188e4e64c --- /dev/null +++ b/Day01-15/code/Day07/scoretable.py @@ -0,0 +1,28 @@ +""" +学生考试成绩表 + +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/Day01-15/Day07/set1.py b/Day01-15/code/Day07/set1.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day07/set1.py rename to Day01-15/code/Day07/set1.py index 9b2f0f535..ebf39c7dc --- a/Day01-15/Day07/set1.py +++ b/Day01-15/code/Day07/set1.py @@ -1,11 +1,9 @@ """ - 定义和使用集合 Version: 0.1 Author: 骆昊 Date: 2018-03-06 - """ diff --git a/Day01-15/code/Day07/set2.py b/Day01-15/code/Day07/set2.py new file mode 100755 index 000000000..bcbb35c60 --- /dev/null +++ b/Day01-15/code/Day07/set2.py @@ -0,0 +1,40 @@ +""" +集合的常用操作 +- 交集 +- 并集 +- 差集 +- 子集 +- 超集 + +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/Day01-15/code/Day07/tic-tac-toe.py b/Day01-15/code/Day07/tic-tac-toe.py new file mode 100755 index 000000000..ff9393fe4 --- /dev/null +++ b/Day01-15/code/Day07/tic-tac-toe.py @@ -0,0 +1,51 @@ +""" +井字棋游戏 + +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/Day01-15/code/Day07/tuple.py b/Day01-15/code/Day07/tuple.py new file mode 100755 index 000000000..15de6b576 --- /dev/null +++ b/Day01-15/code/Day07/tuple.py @@ -0,0 +1,40 @@ +""" +元组的定义和使用 + +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/Day01-15/Day07/yanghui.py b/Day01-15/code/Day07/yanghui.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day07/yanghui.py rename to Day01-15/code/Day07/yanghui.py index 4be882a5c..0a0d289cc --- a/Day01-15/Day07/yanghui.py +++ b/Day01-15/code/Day07/yanghui.py @@ -1,5 +1,4 @@ """ - 输出10行的杨辉三角 - 二项式的n次方展开系数 1 1 1 @@ -12,7 +11,6 @@ Version: 0.1 Author: 骆昊 Date: 2018-03-06 - """ diff --git a/Day01-15/code/Day08/access.py b/Day01-15/code/Day08/access.py new file mode 100755 index 000000000..9394e1dc9 --- /dev/null +++ b/Day01-15/code/Day08/access.py @@ -0,0 +1,18 @@ +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/Day01-15/code/Day08/circle.py b/Day01-15/code/Day08/circle.py new file mode 100755 index 000000000..65ebb3be1 --- /dev/null +++ b/Day01-15/code/Day08/circle.py @@ -0,0 +1,42 @@ +""" +练习 +修一个游泳池 半径(以米为单位)在程序运行时输入 游泳池外修一条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/Day01-15/code/Day08/clock.py b/Day01-15/code/Day08/clock.py new file mode 100755 index 000000000..d3b8a77fe --- /dev/null +++ b/Day01-15/code/Day08/clock.py @@ -0,0 +1,51 @@ +""" +定义和使用时钟类 + +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/Day01-15/code/Day08/guess.py b/Day01-15/code/Day08/guess.py new file mode 100755 index 000000000..1ca99ba57 --- /dev/null +++ b/Day01-15/code/Day08/guess.py @@ -0,0 +1,56 @@ +""" +面向对象版本的猜数字游戏 + +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/Day01-15/code/Day08/hack.py b/Day01-15/code/Day08/hack.py new file mode 100755 index 000000000..91df48cf9 --- /dev/null +++ b/Day01-15/code/Day08/hack.py @@ -0,0 +1,25 @@ +""" +另一种创建类的方式 + +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/Day01-15/code/Day08/rect.py b/Day01-15/code/Day08/rect.py new file mode 100755 index 000000000..204f0df4f --- /dev/null +++ b/Day01-15/code/Day08/rect.py @@ -0,0 +1,43 @@ +""" +定义和使用矩形类 + +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/Day01-15/code/Day08/student.py b/Day01-15/code/Day08/student.py new file mode 100755 index 000000000..845492d17 --- /dev/null +++ b/Day01-15/code/Day08/student.py @@ -0,0 +1,44 @@ +""" +定义和使用学生类 + +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() diff --git a/Day01-15/code/Day09/association.py b/Day01-15/code/Day09/association.py new file mode 100755 index 000000000..29457336a --- /dev/null +++ b/Day01-15/code/Day09/association.py @@ -0,0 +1,71 @@ +""" +对象之间的关联关系 + +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/Day01-15/code/Day09/car1.py b/Day01-15/code/Day09/car1.py new file mode 100755 index 000000000..05aec62dc --- /dev/null +++ b/Day01-15/code/Day09/car1.py @@ -0,0 +1,63 @@ +""" +属性的使用 +- 访问器/修改器/删除器 +- 使用__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/Day01-15/code/Day09/car2.py b/Day01-15/code/Day09/car2.py new file mode 100755 index 000000000..9150d1000 --- /dev/null +++ b/Day01-15/code/Day09/car2.py @@ -0,0 +1,48 @@ +""" +属性的使用 +- 使用已有方法定义访问器/修改器/删除器 + +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/Day01-15/Day09/clock.py b/Day01-15/code/Day09/clock.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day09/clock.py rename to Day01-15/code/Day09/clock.py diff --git a/Day01-15/code/Day09/dependency.py b/Day01-15/code/Day09/dependency.py new file mode 100755 index 000000000..9360949a6 --- /dev/null +++ b/Day01-15/code/Day09/dependency.py @@ -0,0 +1,81 @@ +""" +对象之间的依赖关系和运算符重载 + +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/Day01-15/code/Day09/diamond.py b/Day01-15/code/Day09/diamond.py new file mode 100755 index 000000000..8425edf22 --- /dev/null +++ b/Day01-15/code/Day09/diamond.py @@ -0,0 +1,45 @@ +""" +多重继承 +- 菱形继承(钻石继承) +- 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/Day01-15/code/Day09/employee.py b/Day01-15/code/Day09/employee.py new file mode 100755 index 000000000..43fdd52b3 --- /dev/null +++ b/Day01-15/code/Day09/employee.py @@ -0,0 +1,75 @@ +""" +抽象类 / 方法重写 / 多态 +实现一个工资结算系统 公司有三种类型的员工 +- 部门经理固定月薪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/Day01-15/code/Day09/multi.py b/Day01-15/code/Day09/multi.py new file mode 100755 index 000000000..4a695bc46 --- /dev/null +++ b/Day01-15/code/Day09/multi.py @@ -0,0 +1,66 @@ +""" +多重继承 +- 通过多重继承可以给一个类的对象具备多方面的能力 +- 这样在设计类的时候可以避免设计太多层次的复杂的继承关系 + +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/Day01-15/Day09/pet.py b/Day01-15/code/Day09/pet.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day09/pet.py rename to Day01-15/code/Day09/pet.py diff --git a/Day01-15/code/Day09/rational.py b/Day01-15/code/Day09/rational.py new file mode 100755 index 000000000..c3bcbce72 --- /dev/null +++ b/Day01-15/code/Day09/rational.py @@ -0,0 +1,74 @@ +""" +运算符重载 - 自定义分数类 + +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/Day01-15/code/Day09/shape.py b/Day01-15/code/Day09/shape.py new file mode 100755 index 000000000..ab72e80f8 --- /dev/null +++ b/Day01-15/code/Day09/shape.py @@ -0,0 +1,64 @@ +""" +继承的应用 +- 抽象类 +- 抽象方法 +- 方法重写 +- 多态 + +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/Day01-15/code/Day09/triangle.py b/Day01-15/code/Day09/triangle.py new file mode 100755 index 000000000..47c48354d --- /dev/null +++ b/Day01-15/code/Day09/triangle.py @@ -0,0 +1,51 @@ +""" +实例方法和类方法的应用 + +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/Day01-15/Day10/ball.py b/Day01-15/code/Day10/ball.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day10/ball.py rename to Day01-15/code/Day10/ball.py diff --git a/Day01-15/code/Day10/gui1.py b/Day01-15/code/Day10/gui1.py new file mode 100755 index 000000000..34ea4180e --- /dev/null +++ b/Day01-15/code/Day10/gui1.py @@ -0,0 +1,55 @@ +""" +使用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/Day01-15/code/Day10/gui2.py b/Day01-15/code/Day10/gui2.py new file mode 100755 index 000000000..b5fb2eca9 --- /dev/null +++ b/Day01-15/code/Day10/gui2.py @@ -0,0 +1,41 @@ +""" +使用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('', mouse_evt_handler) +canvas.create_rectangle(0, 0, 600, 600, fill='yellow', outline='white') +for index in range(15): + canvas.create_line(20, 20 + 40 * index, 580, 20 + 40 * index, fill='black') + canvas.create_line(20 + 40 * index, 20, 20 + 40 * index, 580, fill='black') +canvas.create_rectangle(15, 15, 585, 585, outline='black', width=4) +canvas.pack() +tkinter.mainloop() + +# 请思考如何用面向对象的编程思想对上面的代码进行封装 diff --git a/Day01-15/Day10/gui3.py b/Day01-15/code/Day10/gui3.py old mode 100644 new mode 100755 similarity index 86% rename from Day01-15/Day10/gui3.py rename to Day01-15/code/Day10/gui3.py index 1cda3bff5..d50413400 --- a/Day01-15/Day10/gui3.py +++ b/Day01-15/code/Day10/gui3.py @@ -1,7 +1,7 @@ """ 使用tkinter创建GUI - - 在窗口上制作动画 +- 在窗口上制作动画 Version: 0.1 Author: 骆昊 @@ -15,9 +15,9 @@ # 播放动画效果的函数 def play_animation(): - canvas.move(oval, 2, 2) - canvas.update() - top.after(50, play_animation) + canvas.move(oval, 2, 2) + canvas.update() + top.after(50, play_animation) x = 10 diff --git a/Day01-15/code/Day10/renju.py b/Day01-15/code/Day10/renju.py new file mode 100755 index 000000000..5f8cd8812 --- /dev/null +++ b/Day01-15/code/Day10/renju.py @@ -0,0 +1,78 @@ +import pygame + +EMPTY = 0 +BLACK = 1 +WHITE = 2 + +black_color = [0, 0, 0] +white_color = [255, 255, 255] + + +class RenjuBoard(object): + + def __init__(self): + self._board = [[]] * 15 + self.reset() + + def reset(self): + for row in range(len(self._board)): + self._board[row] = [EMPTY] * 15 + + def move(self, row, col, is_black): + if self._board[row][col] == EMPTY: + self._board[row][col] = BLACK if is_black else WHITE + return True + return False + + def draw(self, screen): + for index in range(1, 16): + pygame.draw.line(screen, black_color, + [40, 40 * index], [600, 40 * index], 1) + pygame.draw.line(screen, black_color, + [40 * index, 40], [40 * index, 600], 1) + pygame.draw.rect(screen, black_color, [36, 36, 568, 568], 4) + pygame.draw.circle(screen, black_color, [320, 320], 5, 0) + pygame.draw.circle(screen, black_color, [160, 160], 5, 0) + pygame.draw.circle(screen, black_color, [480, 480], 5, 0) + pygame.draw.circle(screen, black_color, [480, 160], 5, 0) + pygame.draw.circle(screen, black_color, [160, 480], 5, 0) + for row in range(len(self._board)): + for col in range(len(self._board[row])): + if self._board[row][col] != EMPTY: + ccolor = black_color \ + if self._board[row][col] == BLACK else white_color + pos = [40 * (col + 1), 40 * (row + 1)] + pygame.draw.circle(screen, ccolor, pos, 20, 0) + + +def main(): + board = RenjuBoard() + is_black = True + pygame.init() + pygame.display.set_caption('五子棋') + screen = pygame.display.set_mode([640, 640]) + screen.fill([255, 255, 0]) + board.draw(screen) + pygame.display.flip() + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + elif event.type == pygame.KEYUP: + pass + elif event.type == pygame.MOUSEBUTTONDOWN\ + and event.button == 1: + x, y = event.pos + row = round((y - 40) / 40) + col = round((x - 40) / 40) + if board.move(row, col, is_black): + is_black = not is_black + screen.fill([255, 255, 0]) + board.draw(screen) + pygame.display.flip() + pygame.quit() + + +if __name__ == '__main__': + main() diff --git a/Day01-15/code/Day10/snake.py b/Day01-15/code/Day10/snake.py new file mode 100755 index 000000000..5147930ac --- /dev/null +++ b/Day01-15/code/Day10/snake.py @@ -0,0 +1,334 @@ +from abc import ABCMeta, abstractmethod +from enum import Enum, unique +from random import randrange +from threading import Thread + +import pygame + + +class Color(object): + """颜色""" + + GRAY = (242, 242, 242) + BLACK = (0, 0, 0) + GREEN = (0, 255, 0) + PINK = (255, 20, 147) + + +@unique +class Direction(Enum): + """方向""" + + UP = 0 + RIGHT = 1 + DOWN = 2 + LEFT = 3 + + +class GameObject(object, metaclass=ABCMeta): + """游戏中的对象""" + + def __init__(self, x=0, y=0, color=Color.BLACK): + """ + 初始化方法 + + :param x: 横坐标 + :param y: 纵坐标 + :param color: 颜色 + """ + self._x = x + self._y = y + self._color = color + + @property + def x(self): + return self._x + + @property + def y(self): + return self._y + + @abstractmethod + def draw(self, screen): + """ + 绘制 + + :param screen: 屏幕 + """ + pass + + +class Wall(GameObject): + """围墙""" + + def __init__(self, x, y, width, height, color=Color.BLACK): + """ + 初始化方法 + + :param x: 横坐标 + :param y: 纵坐标 + :param width: 宽度 + :param height: 高度 + :param color: 颜色 + """ + super().__init__(x, y, color) + self._width = width + self._height = height + + @property + def width(self): + return self._width + + @property + def height(self): + return self._height + + def draw(self, screen): + pygame.draw.rect(screen, self._color, + (self._x, self._y, self._width, self._height), 4) + + +class Food(GameObject): + """食物""" + + def __init__(self, x, y, size, color=Color.PINK): + """ + 初始化方法 + + :param x: 横坐标 + :param y: 纵坐标 + :param size: 大小 + :param color: 颜色 + """ + super().__init__(x, y, color) + self._size = size + self._hidden = False + + def draw(self, screen): + if not self._hidden: + pygame.draw.circle(screen, self._color, + (self._x + self._size // 2, self._y + self._size // 2), + self._size // 2, 0) + self._hidden = not self._hidden + + +class SnakeNode(GameObject): + """蛇身上的节点""" + + def __init__(self, x, y, size, color=Color.GREEN): + """ + 初始化方法 + + :param x: 横坐标 + :param y: 纵坐标 + :param size: 大小 + :param color: 颜色 + """ + super().__init__(x, y, color) + self._size = size + + @property + def size(self): + return self._size + + def draw(self, screen): + pygame.draw.rect(screen, self._color, + (self._x, self._y, self._size, self._size), 0) + pygame.draw.rect(screen, Color.BLACK, + (self._x, self._y, self._size, self._size), 1) + + +class Snake(GameObject): + """蛇""" + + def __init__(self, x, y, size=20, length=5): + """ + 初始化方法 + + :param x: 横坐标 + :param y: 纵坐标 + :param size: 大小 + :param length: 初始长度 + """ + super().__init__() + self._dir = Direction.LEFT + self._nodes = [] + self._alive = True + self._new_dir = None + for index in range(length): + node = SnakeNode(x + index * size, y, size) + self._nodes.append(node) + + @property + def dir(self): + return self._dir + + @property + def alive(self): + return self._alive + + @property + def head(self): + return self._nodes[0] + + def change_dir(self, new_dir): + """ + 改变方向 + + :param new_dir: 新方向 + """ + if new_dir != self._dir and \ + (self._dir.value + new_dir.value) % 2 != 0: + self._new_dir = new_dir + + def move(self): + """移动""" + if self._new_dir: + self._dir, self._new_dir = self._new_dir, None + snake_dir = self._dir + x, y, size = self.head.x, self.head.y, self.head.size + if snake_dir == Direction.UP: + y -= size + elif snake_dir == Direction.RIGHT: + x += size + elif snake_dir == Direction.DOWN: + y += size + else: + x -= size + new_head = SnakeNode(x, y, size) + self._nodes.insert(0, new_head) + self._nodes.pop() + + def collide(self, wall): + """ + 撞墙 + + :param wall: 围墙 + """ + head = self.head + if head.x < wall.x or head.x + head.size > wall.x + wall.width \ + or head.y < wall.y or head.y + head.size > wall.y + wall.height: + self._alive = False + + def eat_food(self, food): + """ + 吃食物 + + :param food: 食物 + + :return: 吃到食物返回True否则返回False + """ + if self.head.x == food.x and self.head.y == food.y: + tail = self._nodes[-1] + self._nodes.append(tail) + return True + return False + + def eat_self(self): + """咬自己""" + for index in range(4, len(self._nodes)): + node = self._nodes[index] + if node.x == self.head.x and node.y == self.head.y: + self._alive = False + + def draw(self, screen): + for node in self._nodes: + node.draw(screen) + + +def main(): + + def refresh(): + """刷新游戏窗口""" + screen.fill(Color.GRAY) + wall.draw(screen) + food.draw(screen) + snake.draw(screen) + pygame.display.flip() + + def handle_key_event(key_event): + """处理按键事件""" + key = key_event.key + if key == pygame.K_F2: + reset_game() + elif key in (pygame.K_a, pygame.K_w, pygame.K_d, pygame.K_s): + if snake.alive: + if key == pygame.K_w: + new_dir = Direction.UP + elif key == pygame.K_d: + new_dir = Direction.RIGHT + elif key == pygame.K_s: + new_dir = Direction.DOWN + else: + new_dir = Direction.LEFT + snake.change_dir(new_dir) + + def create_food(): + """创建食物""" + unit_size = snake.head.size + max_row = wall.height // unit_size + max_col = wall.width // unit_size + row = randrange(0, max_row) + col = randrange(0, max_col) + return Food(wall.x + unit_size * col, wall.y + unit_size * row, unit_size) + + def reset_game(): + """重置游戏""" + nonlocal food, snake + food = create_food() + snake = Snake(250, 290) + + def background_task(): + nonlocal running, food + while running: + if snake.alive: + refresh() + clock.tick(10) + if snake.alive: + snake.move() + snake.collide(wall) + if snake.eat_food(food): + food = create_food() + snake.eat_self() + + """ + class BackgroundTask(Thread): + + def run(self): + nonlocal running, food + while running: + if snake.alive: + refresh() + clock.tick(10) + if snake.alive: + snake.move() + snake.collide(wall) + if snake.eat_food(food): + food = create_food() + snake.eat_self() + """ + + wall = Wall(10, 10, 600, 600) + snake = Snake(250, 290) + food = create_food() + pygame.init() + screen = pygame.display.set_mode((620, 620)) + pygame.display.set_caption('贪吃蛇') + # 创建控制游戏每秒帧数的时钟 + clock = pygame.time.Clock() + running = True + # 启动后台线程负责刷新窗口和让蛇移动 + # BackgroundTask().start() + Thread(target=background_task).start() + # 处理事件的消息循环 + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + elif event.type == pygame.KEYDOWN: + handle_key_event(event) + pygame.quit() + + +if __name__ == '__main__': + main() diff --git a/Day01-15/Day10/turtle1.py b/Day01-15/code/Day10/turtle1.py old mode 100644 new mode 100755 similarity index 89% rename from Day01-15/Day10/turtle1.py rename to Day01-15/code/Day10/turtle1.py index fe0c4f721..afd1b0123 --- a/Day01-15/Day10/turtle1.py +++ b/Day01-15/code/Day10/turtle1.py @@ -19,7 +19,7 @@ turtle.pendown() turtle.begin_fill() for _ in range(36): - turtle.forward(200) - turtle.right(170) + turtle.forward(200) + turtle.right(170) turtle.end_fill() turtle.mainloop() diff --git a/Day01-15/code/Day11/csv1.py b/Day01-15/code/Day11/csv1.py new file mode 100755 index 000000000..1facf6a47 --- /dev/null +++ b/Day01-15/code/Day11/csv1.py @@ -0,0 +1,21 @@ +""" +读取CSV文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +import csv + +filename = 'example.csv' + +try: + with open(filename) as f: + reader = csv.reader(f) + data = list(reader) +except FileNotFoundError: + print('无法打开文件:', filename) +else: + for item in data: + print('%-30s%-20s%-10s' % (item[0], item[1], item[2])) diff --git a/Day01-15/code/Day11/csv2.py b/Day01-15/code/Day11/csv2.py new file mode 100755 index 000000000..d4ee14860 --- /dev/null +++ b/Day01-15/code/Day11/csv2.py @@ -0,0 +1,44 @@ +""" +写入CSV文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +import csv + + +class Teacher(object): + + def __init__(self, name, age, title): + self.__name = name + self.__age = age + self.__title = title + self.__index = -1 + + @property + def name(self): + return self.__name + + @property + def age(self): + return self.__age + + @property + def title(self): + return self.__title + + +filename = 'teacher.csv' +teachers = [Teacher('骆昊', 38, '叫兽'), Teacher('狄仁杰', 25, '砖家')] + +try: + with open(filename, 'w') as f: + writer = csv.writer(f) + for teacher in teachers: + writer.writerow([teacher.name, teacher.age, teacher.title]) +except BaseException as e: + print('无法写入文件:', filename) +else: + print('保存数据完成!') diff --git a/Day01-15/code/Day11/ex1.py b/Day01-15/code/Day11/ex1.py new file mode 100755 index 000000000..5f3979cbb --- /dev/null +++ b/Day01-15/code/Day11/ex1.py @@ -0,0 +1,21 @@ +""" +异常机制 - 处理程序在运行时可能发生的状态 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +input_again = True +while input_again: + try: + a = int(input('a = ')) + b = int(input('b = ')) + print('%d / %d = %f' % (a, b, a / b)) + input_again = False + except ValueError: + print('请输入整数') + except ZeroDivisionError: + print('除数不能为0') +# 处理异常让代码不因异常而崩溃是一方面 +# 更重要的是可以通过对异常的处理让代码从异常中恢复过来 diff --git a/Day01-15/code/Day11/ex2.py b/Day01-15/code/Day11/ex2.py new file mode 100755 index 000000000..77090a603 --- /dev/null +++ b/Day01-15/code/Day11/ex2.py @@ -0,0 +1,17 @@ +""" +异常机制 - 处理程序在运行时可能发生的状态 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +input_again = True +while input_again: + try: + a = int(input('a = ')) + b = int(input('b = ')) + print('%d / %d = %f' % (a, b, a / b)) + input_again = False + except (ValueError, ZeroDivisionError) as msg: + print(msg) diff --git a/Day01-15/code/Day11/ex3.py b/Day01-15/code/Day11/ex3.py new file mode 100755 index 000000000..1b69b9518 --- /dev/null +++ b/Day01-15/code/Day11/ex3.py @@ -0,0 +1,28 @@ +""" +异常机制 - 处理程序在运行时可能发生的状态 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +import time +import sys + +filename = input('请输入文件名: ') +try: + with open(filename) as f: + lines = f.readlines() +except FileNotFoundError as msg: + print('无法打开文件:', filename) + print(msg) +except UnicodeDecodeError as msg: + print('非文本文件无法解码') + sys.exit() +else: + for line in lines: + print(line.rstrip()) + time.sleep(0.5) +finally: + # 此处最适合做善后工作 + print('不管发生什么我都会执行') diff --git a/Day01-15/code/Day11/ex4.py b/Day01-15/code/Day11/ex4.py new file mode 100755 index 000000000..6b56e6cfd --- /dev/null +++ b/Day01-15/code/Day11/ex4.py @@ -0,0 +1,22 @@ +""" +引发异常和异常栈 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + + +def f1(): + raise AssertionError('发生异常') + + +def f2(): + f1() + + +def f3(): + f2() + + +f3() diff --git a/Day01-15/Day11/example.csv b/Day01-15/code/Day11/example.csv similarity index 100% rename from Day01-15/Day11/example.csv rename to Day01-15/code/Day11/example.csv diff --git a/Day01-15/code/Day11/file1.py b/Day01-15/code/Day11/file1.py new file mode 100755 index 000000000..935a90bc3 --- /dev/null +++ b/Day01-15/code/Day11/file1.py @@ -0,0 +1,31 @@ +""" +从文本文件中读取数据 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +import time + + +def main(): + # 一次性读取整个文件内容 + with open('致橡树.txt', 'r', encoding='utf-8') as f: + print(f.read()) + + # 通过for-in循环逐行读取 + with open('致橡树.txt', mode='r') as f: + for line in f: + print(line, end='') + time.sleep(0.5) + print() + + # 读取文件按行读取到列表中 + with open('致橡树.txt') as f: + lines = f.readlines() + print(lines) + + +if __name__ == '__main__': + main() diff --git a/Day01-15/code/Day11/file2.py b/Day01-15/code/Day11/file2.py new file mode 100755 index 000000000..0ad3d6fe8 --- /dev/null +++ b/Day01-15/code/Day11/file2.py @@ -0,0 +1,16 @@ +""" +读取圆周率文件判断其中是否包含自己的生日 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +birth = input('请输入你的生日: ') +with open('pi_million_digits.txt') as f: + lines = f.readlines() + pi_string = '' + for line in lines: + pi_string += line.strip() + if birth in pi_string: + print('Bingo!!!') diff --git a/Day01-15/code/Day11/file3.py b/Day01-15/code/Day11/file3.py new file mode 100755 index 000000000..3f6f8c8c0 --- /dev/null +++ b/Day01-15/code/Day11/file3.py @@ -0,0 +1,26 @@ +""" +写文本文件 +将100以内的素数写入到文件中 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +from math import sqrt + + +def is_prime(n): + for factor in range(2, int(sqrt(n)) + 1): + if n % factor == 0: + return False + return True + + +# 试一试有什么不一样 +# with open('prime.txt', 'a') as f: +with open('prime.txt', 'w') as f: + for num in range(2, 100): + if is_prime(num): + f.write(str(num) + '\n') +print('写入完成!') diff --git a/Day01-15/code/Day11/file4.py b/Day01-15/code/Day11/file4.py new file mode 100755 index 000000000..210a852ab --- /dev/null +++ b/Day01-15/code/Day11/file4.py @@ -0,0 +1,20 @@ +""" +读写二进制文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" +import base64 + +with open('mm.jpg', 'rb') as f: + data = f.read() + # print(type(data)) + # print(data) + print('字节数:', len(data)) + # 将图片处理成BASE-64编码 + print(base64.b64encode(data)) + +with open('girl.jpg', 'wb') as f: + f.write(data) +print('写入完成!') diff --git a/Day01-15/code/Day11/json1.py b/Day01-15/code/Day11/json1.py new file mode 100755 index 000000000..e3a6bb804 --- /dev/null +++ b/Day01-15/code/Day11/json1.py @@ -0,0 +1,85 @@ +""" +读取JSON数据 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-13 +""" + +import json +import csv2 + +json_str = '{"name": "骆昊", "age": 38, "title": "叫兽"}' +result = json.loads(json_str) +print(result) +print(type(result)) +print(result['name']) +print(result['age']) + +# 把转换得到的字典作为关键字参数传入Teacher的构造器 +teacher = csv2.Teacher(**result) +print(teacher) +print(teacher.name) +print(teacher.age) +print(teacher.title) + +# 请思考如何将下面JSON格式的天气数据转换成对象并获取我们需要的信息 +# 稍后我们会讲解如何通过网络API获取我们需要的JSON格式的数据 +""" + { + "wendu": "29", + "ganmao": "各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。", + "forecast": [ + { + "fengxiang": "南风", + "fengli": "3-4级", + "high": "高温 32℃", + "type": "多云", + "low": "低温 17℃", + "date": "16日星期二" + }, + { + "fengxiang": "南风", + "fengli": "微风级", + "high": "高温 34℃", + "type": "晴", + "low": "低温 19℃", + "date": "17日星期三" + }, + { + "fengxiang": "南风", + "fengli": "微风级", + "high": "高温 35℃", + "type": "晴", + "low": "低温 22℃", + "date": "18日星期四" + }, + { + "fengxiang": "南风", + "fengli": "微风级", + "high": "高温 35℃", + "type": "多云", + "low": "低温 22℃", + "date": "19日星期五" + }, + { + "fengxiang": "南风", + "fengli": "3-4级", + "high": "高温 34℃", + "type": "晴", + "low": "低温 21℃", + "date": "20日星期六" + } + ], + "yesterday": { + "fl": "微风", + "fx": "南风", + "high": "高温 28℃", + "type": "晴", + "low": "低温 15℃", + "date": "15日星期一" + }, + "aqi": "72", + "city": "北京" + } +""" diff --git a/Day01-15/Day11/json2.py b/Day01-15/code/Day11/json2.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day11/json2.py rename to Day01-15/code/Day11/json2.py index ddbbcae69..3c843feba --- a/Day01-15/Day11/json2.py +++ b/Day01-15/code/Day11/json2.py @@ -1,11 +1,9 @@ """ - 写入JSON文件 Version: 0.1 Author: 骆昊 Date: 2018-03-13 - """ import json diff --git a/Day01-15/code/Day11/mm.jpg b/Day01-15/code/Day11/mm.jpg new file mode 100755 index 000000000..e3381b777 Binary files /dev/null and b/Day01-15/code/Day11/mm.jpg differ diff --git a/Day01-15/Day11/pi_million_digits.txt b/Day01-15/code/Day11/pi_million_digits.txt old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day11/pi_million_digits.txt rename to Day01-15/code/Day11/pi_million_digits.txt diff --git a/Day01-15/Day11/teacher.csv b/Day01-15/code/Day11/teacher.csv old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day11/teacher.csv rename to Day01-15/code/Day11/teacher.csv diff --git "a/Day01-15/code/Day11/\350\207\264\346\251\241\346\240\221.txt" "b/Day01-15/code/Day11/\350\207\264\346\251\241\346\240\221.txt" new file mode 100755 index 000000000..098dc92d1 --- /dev/null +++ "b/Day01-15/code/Day11/\350\207\264\346\251\241\346\240\221.txt" @@ -0,0 +1,32 @@ +我如果爱你 +绝不学攀援的凌霄花 +借你的高枝炫耀自己 + +我如果爱你 +绝不学痴情的鸟儿 +为绿荫重复单调的歌曲 + +也不止像泉源 +常年送来清凉的慰藉 +也不止像险峰 +增加你的高度 衬托你的威仪 +甚至日光 甚至春雨 +不 这些都还不够 +我必须是你近旁的一株木棉 +作为树的形象和你站在一起 +根 紧握在地下 +叶 相触在云里 +每一阵风过 +我们都互相致意 +但没有人 听懂我们的言语 +你有你的铜枝铁干 +像刀 像剑 也像戟; +我有我红硕的花朵 +像沉重的叹息 又像英勇的火炬 +我们分担寒潮、风雷、霹雳 +我们共享雾霭、流岚、虹霓 +仿佛永远分离 却又终身相依 +这才是伟大的爱情 +坚贞就在这里 +爱 不仅爱你伟岸的身躯 +也爱你坚持的位置 足下的土地 \ No newline at end of file diff --git a/Day01-15/Day12/str1.py b/Day01-15/code/Day12/str1.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day12/str1.py rename to Day01-15/code/Day12/str1.py index 61b1d81e8..dac984f91 --- a/Day01-15/Day12/str1.py +++ b/Day01-15/code/Day12/str1.py @@ -1,11 +1,9 @@ """ - 字符串常用操作 Version: 0.1 Author: 骆昊 Date: 2018-03-19 - """ import pyperclip diff --git a/Day01-15/code/Day12/str2.py b/Day01-15/code/Day12/str2.py new file mode 100755 index 000000000..ac5f98e65 --- /dev/null +++ b/Day01-15/code/Day12/str2.py @@ -0,0 +1,59 @@ +""" +字符串常用操作 - 实现字符串倒转的方法 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-19 +""" + +from io import StringIO + + +def reverse_str1(str): + return str[::-1] + + +def reverse_str2(str): + if len(str) <= 1: + return str + return reverse_str2(str[1:]) + str[0:1] + + +def reverse_str3(str): + # StringIO对象是Python中的可变字符串 + # 不应该使用不变字符串做字符串连接操作 因为会产生很多无用字符串对象 + rstr = StringIO() + str_len = len(str) + for index in range(str_len - 1, -1, -1): + rstr.write(str[index]) + return rstr.getvalue() + + +def reverse_str4(str): + return ''.join(str[index] for index in range(len(str) - 1, -1, -1)) + + +def reverse_str5(str): + # 将字符串处理成列表 + str_list = list(str) + str_len = len(str) + # 使用zip函数将两个序列合并成一个产生元组的迭代器 + # 每次正好可以取到一前一后两个下标来实现元素的交换 + for i, j in zip(range(str_len // 2), range(str_len - 1, str_len // 2, -1)): + str_list[i], str_list[j] = str_list[j], str_list[i] + # 将列表元素连接成字符串 + return ''.join(str_list) + + +if __name__ == '__main__': + str = 'I love Python' + print(reverse_str1(str)) + print(str) + print(reverse_str2(str)) + print(str) + print(reverse_str3(str)) + print(str) + print(reverse_str4(str)) + print(str) + print(reverse_str5(str)) + print(str) diff --git a/Day01-15/Day12/test3.py b/Day01-15/code/Day12/test3.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day12/test3.py rename to Day01-15/code/Day12/test3.py index 126f19f88..6e06f15a8 --- a/Day01-15/Day12/test3.py +++ b/Day01-15/code/Day12/test3.py @@ -1,11 +1,9 @@ """ - 验证输入用户名和QQ号是否有效并给出对应的提示信息 要求: 用户名必须由字母、数字或下划线构成且长度在6~20个字符之间 QQ号是5~12的数字且首位不能为0 - """ import re @@ -26,4 +24,3 @@ def main(): if __name__ == '__main__': main() - diff --git a/Day01-15/Day12/test4.py b/Day01-15/code/Day12/test4.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day12/test4.py rename to Day01-15/code/Day12/test4.py diff --git a/Day01-15/code/Day12/test5.py b/Day01-15/code/Day12/test5.py new file mode 100755 index 000000000..42db621d6 --- /dev/null +++ b/Day01-15/code/Day12/test5.py @@ -0,0 +1,15 @@ +""" +不良内容过滤 +""" +import re + + +def main(): + sentence = '你丫是傻叉吗? 我操你大爷的. Fuck you.' + purified = re.sub('[操肏艹]|fuck|shit|傻[比屄逼叉缺吊屌]|煞笔', + '*', sentence, flags=re.IGNORECASE) + print(purified) + + +if __name__ == '__main__': + main() diff --git a/Day01-15/code/Day13/asyncio1.py b/Day01-15/code/Day13/asyncio1.py new file mode 100755 index 000000000..fdbfa00df --- /dev/null +++ b/Day01-15/code/Day13/asyncio1.py @@ -0,0 +1,30 @@ +""" +异步I/O操作 - asyncio模块 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-21 +""" + +import asyncio +import threading +# import time + + +@asyncio.coroutine +def hello(): + print('%s: hello, world!' % threading.current_thread()) + # 休眠不会阻塞主线程因为使用了异步I/O操作 + # 注意有yield from才会等待休眠操作执行完成 + yield from asyncio.sleep(2) + # asyncio.sleep(1) + # time.sleep(1) + print('%s: goodbye, world!' % threading.current_thread()) + + +loop = asyncio.get_event_loop() +tasks = [hello(), hello()] +# 等待两个异步I/O操作执行结束 +loop.run_until_complete(asyncio.wait(tasks)) +print('game over!') +loop.close() diff --git a/Day01-15/code/Day13/asyncio2.py b/Day01-15/code/Day13/asyncio2.py new file mode 100755 index 000000000..cbe935c84 --- /dev/null +++ b/Day01-15/code/Day13/asyncio2.py @@ -0,0 +1,24 @@ +""" +异步I/O操作 - async和await + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-21 +""" +import asyncio +import threading + + +# 通过async修饰的函数不再是普通函数而是一个协程 +# 注意async和await将在Python 3.7中作为关键字出现 +async def hello(): + print('%s: hello, world!' % threading.current_thread()) + await asyncio.sleep(2) + print('%s: goodbye, world!' % threading.current_thread()) + + +loop = asyncio.get_event_loop() +tasks = [hello(), hello()] +# 等待两个异步I/O操作执行结束 +loop.run_until_complete(asyncio.wait(tasks)) +loop.close() diff --git a/Day01-15/code/Day13/asyncio3.py b/Day01-15/code/Day13/asyncio3.py new file mode 100755 index 000000000..bec113579 --- /dev/null +++ b/Day01-15/code/Day13/asyncio3.py @@ -0,0 +1,35 @@ +""" +异步I/O操作 - asyncio模块 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-21 +""" +import asyncio + + +async def wget(host): + print('wget %s...' % host) + connect = asyncio.open_connection(host, 80) + # 异步方式等待连接结果 + reader, writer = await connect + header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host + writer.write(header.encode('utf-8')) + # 异步I/O方式执行写操作 + await writer.drain() + while True: + # 异步I/O方式执行读操作 + line = await reader.readline() + if line == b'\r\n': + break + print('%s header > %s' % (host, line.decode('utf-8').rstrip())) + writer.close() + + +loop = asyncio.get_event_loop() +# 通过生成式语法创建一个装了三个协程的列表 +hosts_list = ['www.sina.com.cn', 'www.sohu.com', 'www.163.com'] +tasks = [wget(host) for host in hosts_list] +# 下面的方法将异步I/O操作放入EventLoop直到执行完毕 +loop.run_until_complete(asyncio.wait(tasks)) +loop.close() diff --git a/Day01-15/code/Day13/coroutine1.py b/Day01-15/code/Day13/coroutine1.py new file mode 100755 index 000000000..9168a6269 --- /dev/null +++ b/Day01-15/code/Day13/coroutine1.py @@ -0,0 +1,40 @@ +""" +使用协程 - 模拟快递中心派发快递 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-21 +""" + +from time import sleep +from random import random + + +def build_deliver_man(man_id): + total = 0 + while True: + total += 1 + print('%d号快递员准备接今天的第%d单.' % (man_id, total)) + pkg = yield + print('%d号快递员收到编号为%s的包裹.' % (man_id, pkg)) + sleep(random() * 3) + + +def package_center(deliver_man, max_per_day): + num = 1 + deliver_man.send(None) + # next(deliver_man) + while num <= max_per_day: + package_id = 'PKG-%d' % num + deliver_man.send(package_id) + num += 1 + sleep(0.1) + deliver_man.close() + print('今天的包裹派送完毕!') + + +dm = build_deliver_man(1) +package_center(dm, 10) + +# 两个函数虽然没有调用关系但是创建快递员的函数作为一个协程协助了快递中心函数完成任务 +# 想一想如果有多个快递员的时候应该如何处理 diff --git a/Day01-15/code/Day13/coroutine2.py b/Day01-15/code/Day13/coroutine2.py new file mode 100755 index 000000000..2267166ec --- /dev/null +++ b/Day01-15/code/Day13/coroutine2.py @@ -0,0 +1,42 @@ +""" +使用协程 - 查看协程的状态 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-21 +""" + +from time import sleep +from inspect import getgeneratorstate + + +def build_deliver_man(man_id): + total = 0 + while True: + total += 1 + print('%d号快递员准备接今天的第%d单.' % (man_id, total)) + pkg = yield + print('%d号快递员收到编号为%s的包裹.' % (man_id, pkg)) + sleep(0.5) + + +def package_center(deliver_man, max_per_day): + num = 1 + # 创建状态(GEN_CREATED) - 等待开始执行 + print(getgeneratorstate(deliver_man)) + deliver_man.send(None) + # 挂起状态(GEN_SUSPENDED) - 在yield表达式处暂停 + print(getgeneratorstate(deliver_man)) + # next(deliver_man) + while num <= max_per_day: + package_id = 'PKG-%d' % num + deliver_man.send(package_id) + num += 1 + deliver_man.close() + # 结束状态(GEN_CLOSED) - 执行完毕 + print(getgeneratorstate(deliver_man)) + print('今天的包裹派送完毕!') + + +dm = build_deliver_man(1) +package_center(dm, 10) diff --git a/Day01-15/Day13/generator1.py b/Day01-15/code/Day13/generator1.py old mode 100644 new mode 100755 similarity index 86% rename from Day01-15/Day13/generator1.py rename to Day01-15/code/Day13/generator1.py index 0f6f44886..dc5052301 --- a/Day01-15/Day13/generator1.py +++ b/Day01-15/code/Day13/generator1.py @@ -1,11 +1,9 @@ """ - 生成器 - 生成器语法 Version: 0.1 Author: 骆昊 Date: 2018-03-21 - """ seq = [x * x for x in range(10)] @@ -14,12 +12,12 @@ gen = (x * x for x in range(10)) print(gen) for x in gen: - print(x) + print(x) num = 10 gen = (x ** y for x, y in zip(range(1, num), range(num - 1, 0, -1))) print(gen) n = 1 while n < num: - print(next(gen)) - n += 1 + print(next(gen)) + n += 1 diff --git a/Day01-15/code/Day13/generator2.py b/Day01-15/code/Day13/generator2.py new file mode 100755 index 000000000..e5568d8b4 --- /dev/null +++ b/Day01-15/code/Day13/generator2.py @@ -0,0 +1,19 @@ +""" +生成器 - 使用yield关键字 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-21 +""" + + +def fib(num): + n, a, b = 0, 0, 1 + while n < num: + yield b + a, b = b, a + b + n += 1 + + +for x in fib(20): + print(x) diff --git a/Day01-15/code/Day13/multiprocess1.py b/Day01-15/code/Day13/multiprocess1.py new file mode 100755 index 000000000..6e13e909d --- /dev/null +++ b/Day01-15/code/Day13/multiprocess1.py @@ -0,0 +1,33 @@ +""" +使用Process类创建多个进程 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-20 +""" + +# 通过下面程序的执行结果可以证实 父进程在创建子进程时复制了进程及其数据结构 +# 每个进程都有自己独立的内存空间 所以进程之间共享数据只能通过IPC的方式 + + +from multiprocessing import Process, Queue, current_process +from time import sleep + + +def sub_task(content, counts): + print(f'PID: {current_process().pid}') + counter = 0 + while counter < counts: + counter += 1 + print(f'{counter}: {content}') + sleep(0.01) + + +def main(): + number = random.randrange(5, 10) + Process(target=sub_task, args=('Ping', number)).start() + Process(target=sub_task, args=('Pong', number)).start() + + +if __name__ == '__main__': + main() diff --git a/Day01-15/code/Day13/multiprocess2.py b/Day01-15/code/Day13/multiprocess2.py new file mode 100755 index 000000000..1922186f3 --- /dev/null +++ b/Day01-15/code/Day13/multiprocess2.py @@ -0,0 +1,32 @@ +""" +实现进程间的通信 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-20 +""" +import multiprocessing +import os + + +def sub_task(queue): + print('子进程进程号:', os.getpid()) + counter = 0 + while counter < 1000: + queue.put('Pong') + counter += 1 + + +if __name__ == '__main__': + print('当前进程号:', os.getpid()) + queue = multiprocessing.Queue() + p = multiprocessing.Process(target=sub_task, args=(queue,)) + p.start() + counter = 0 + while counter < 1000: + queue.put('Ping') + counter += 1 + p.join() + print('子任务已经完成.') + for _ in range(2000): + print(queue.get(), end='') diff --git a/Day01-15/code/Day13/multiprocess3.py b/Day01-15/code/Day13/multiprocess3.py new file mode 100755 index 000000000..4d784243b --- /dev/null +++ b/Day01-15/code/Day13/multiprocess3.py @@ -0,0 +1,27 @@ +""" +创建进程调用其他程序 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-20 +""" + +import subprocess +import sys + +def main(): + # 通过sys.argv获取命令行参数 + if len(sys.argv) > 1: + # 第一个命令行参数是程序本身所以从第二个开始取 + for index in range(1, len(sys.argv)): + try: + # 通过subprocess模块的call函数启动子进程 + status = subprocess.call(sys.argv[index]) + except FileNotFoundError: + print('不能执行%s命令' % sys.argv[index]) + else: + print('请使用命令行参数指定要执行的进程') + + +if __name__ == '__main__': + main() diff --git a/Day01-15/Day13/multiprocess4.py b/Day01-15/code/Day13/multiprocess4.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day13/multiprocess4.py rename to Day01-15/code/Day13/multiprocess4.py diff --git a/Day01-15/code/Day13/multithread1.py b/Day01-15/code/Day13/multithread1.py new file mode 100755 index 000000000..f4b6f7fd1 --- /dev/null +++ b/Day01-15/code/Day13/multithread1.py @@ -0,0 +1,42 @@ +""" +使用多线程的情况 - 模拟多个下载任务 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-20 +""" + +from random import randint +from time import time, sleep +import atexit +import _thread + + +def download_task(filename): + print('开始下载%s...' % filename) + time_to_download = randint(5, 10) + print('剩余时间%d秒.' % time_to_download) + sleep(time_to_download) + print('%s下载完成!' % filename) + + +def shutdown_hook(start): + end = time() + print('总共耗费了%.3f秒.' % (end - start)) + + +def main(): + start = time() + # 将多个下载任务放到多个线程中执行 + thread1 = _thread.start_new_thread(download_task, ('Python从入门到住院.pdf',)) + thread2 = _thread.start_new_thread(download_task, ('Peking Hot.avi',)) + # 注册关机钩子在程序执行结束前计算执行时间 + atexit.register(shutdown_hook, start) + + +if __name__ == '__main__': + main() + +# 执行这里的代码会引发致命错误(不要被这个词吓到) 因为主线程结束后下载线程再想执行就会出问题 +# 需要说明一下 由于_thread模块属于比较底层的线程操作而且不支持守护线程的概念 +# 在实际开发中会有诸多不便 因此我们推荐使用threading模块提供的高级操作进行多线程编程 diff --git a/Day01-15/code/Day13/multithread2.py b/Day01-15/code/Day13/multithread2.py new file mode 100755 index 000000000..4c7e439a4 --- /dev/null +++ b/Day01-15/code/Day13/multithread2.py @@ -0,0 +1,34 @@ +""" +使用多线程的情况 - 模拟多个下载任务 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-20 +""" + +from random import randint +from threading import Thread +from time import time, sleep + + +def download_task(filename): + print('开始下载%s...' % filename) + time_to_download = randint(5, 10) + sleep(time_to_download) + print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) + + +def main(): + start = time() + thread1 = Thread(target=download_task, args=('Python从入门到住院.pdf',)) + thread1.start() + thread2 = Thread(target=download_task, args=('Peking Hot.avi',)) + thread2.start() + thread1.join() + thread2.join() + end = time() + print('总共耗费了%.3f秒' % (end - start)) + + +if __name__ == '__main__': + main() diff --git a/Day01-15/code/Day13/multithread3.py b/Day01-15/code/Day13/multithread3.py new file mode 100755 index 000000000..9e7668cfa --- /dev/null +++ b/Day01-15/code/Day13/multithread3.py @@ -0,0 +1,45 @@ +""" +使用多线程的情况 - 模拟多个下载任务 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-20 +""" + +from random import randint +from time import time, sleep +import threading + + +class DownloadTask(threading.Thread): + + def __init__(self, filename): + super().__init__() + self._filename = filename + + def run(self): + print('开始下载%s...' % self._filename) + time_to_download = randint(5, 10) + print('剩余时间%d秒.' % time_to_download) + sleep(time_to_download) + print('%s下载完成!' % self._filename) + + +def main(): + start = time() + # 将多个下载任务放到多个线程中执行 + # 通过自定义的线程类创建线程对象 线程启动后会回调执行run方法 + thread1 = DownloadTask('Python从入门到住院.pdf') + thread1.start() + thread2 = DownloadTask('Peking Hot.avi') + thread2.start() + thread1.join() + thread2.join() + end = time() + print('总共耗费了%.3f秒' % (end - start)) + + +if __name__ == '__main__': + main() + +# 请注意通过threading.Thread创建的线程默认是非守护线程 diff --git a/Day01-15/Day13/multithread4.py b/Day01-15/code/Day13/multithread4.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day13/multithread4.py rename to Day01-15/code/Day13/multithread4.py index babe91528..b5fca5a17 --- a/Day01-15/Day13/multithread4.py +++ b/Day01-15/code/Day13/multithread4.py @@ -1,11 +1,9 @@ """ - 使用多线程的情况 - 耗时间的任务在独立的线程中执行 Version: 0.1 Author: 骆昊 Date: 2018-03-20 - """ import time diff --git a/Day01-15/Day13/multithread5.py b/Day01-15/code/Day13/multithread5.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day13/multithread5.py rename to Day01-15/code/Day13/multithread5.py index f23ef9a5f..368d0ce72 --- a/Day01-15/Day13/multithread5.py +++ b/Day01-15/code/Day13/multithread5.py @@ -1,11 +1,9 @@ """ - 多个线程共享数据 - 没有锁的情况 Version: 0.1 Author: 骆昊 Date: 2018-03-20 - """ from time import sleep diff --git a/Day01-15/code/Day13/multithread6.py b/Day01-15/code/Day13/multithread6.py new file mode 100755 index 000000000..d0354e7cb --- /dev/null +++ b/Day01-15/code/Day13/multithread6.py @@ -0,0 +1,44 @@ +""" +多个线程共享数据 - 有锁的情况 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-20 +""" + +import time +import threading + + +class Account(object): + + def __init__(self): + self._balance = 0 + self._lock = threading.Lock() + + def deposit(self, money): + # 获得锁后代码才能继续执行 + self._lock.acquire() + try: + new_balance = self._balance + money + time.sleep(0.01) + self._balance = new_balance + finally: + # 操作完成后一定要记着释放锁 + self._lock.release() + + @property + def balance(self): + return self._balance + + +if __name__ == '__main__': + account = Account() + # 创建100个存款的线程向同一个账户中存钱 + for _ in range(100): + threading.Thread(target=account.deposit, args=(1,)).start() + # 等所有存款的线程都执行完毕 + time.sleep(2) + print('账户余额为: ¥%d元' % account.balance) + +# 想一想结果为什么不是我们期望的100元 diff --git a/Day01-15/Day13/singlethread1.py b/Day01-15/code/Day13/singlethread1.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day13/singlethread1.py rename to Day01-15/code/Day13/singlethread1.py index 85aed3f1b..6b502e0d6 --- a/Day01-15/Day13/singlethread1.py +++ b/Day01-15/code/Day13/singlethread1.py @@ -1,11 +1,9 @@ """ - 不使用多线程的情况 - 模拟多个下载任务 Version: 0.1 Author: 骆昊 Date: 2018-03-20 - """ from random import randint diff --git a/Day01-15/Day13/singlethread2.py b/Day01-15/code/Day13/singlethread2.py old mode 100644 new mode 100755 similarity index 99% rename from Day01-15/Day13/singlethread2.py rename to Day01-15/code/Day13/singlethread2.py index cd33a89a8..0db8aec74 --- a/Day01-15/Day13/singlethread2.py +++ b/Day01-15/code/Day13/singlethread2.py @@ -1,11 +1,9 @@ """ - 不使用多线程的情况 - 耗时间的任务阻塞主事件循环 Version: 0.1 Author: 骆昊 Date: 2018-03-20 - """ import time diff --git a/Day01-15/Day13/test2.py b/Day01-15/code/Day13/test2.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day13/test2.py rename to Day01-15/code/Day13/test2.py diff --git a/Day01-15/Day13/test3.py b/Day01-15/code/Day13/test3.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day13/test3.py rename to Day01-15/code/Day13/test3.py diff --git a/Day01-15/Day14/chatclient.py b/Day01-15/code/Day14/chatclient.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day14/chatclient.py rename to Day01-15/code/Day14/chatclient.py diff --git a/Day01-15/Day14/chatserver.py b/Day01-15/code/Day14/chatserver.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day14/chatserver.py rename to Day01-15/code/Day14/chatserver.py diff --git a/Day01-15/Day14/fileclient.py b/Day01-15/code/Day14/fileclient.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day14/fileclient.py rename to Day01-15/code/Day14/fileclient.py diff --git a/Day01-15/Day14/fileserver.py b/Day01-15/code/Day14/fileserver.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day14/fileserver.py rename to Day01-15/code/Day14/fileserver.py diff --git a/Day01-15/code/Day14/guido.jpg b/Day01-15/code/Day14/guido.jpg new file mode 100755 index 000000000..a8f217c6d Binary files /dev/null and b/Day01-15/code/Day14/guido.jpg differ diff --git a/Day01-15/Day14/mmdownloader.py b/Day01-15/code/Day14/mmdownloader.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day14/mmdownloader.py rename to Day01-15/code/Day14/mmdownloader.py diff --git a/Day01-15/code/Day14/socket1.py b/Day01-15/code/Day14/socket1.py new file mode 100755 index 000000000..16bb978c6 --- /dev/null +++ b/Day01-15/code/Day14/socket1.py @@ -0,0 +1,23 @@ +""" +套接字 - 基于TCP协议创建时间服务器 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-22 +""" + +from socket import * +from time import * + +server = socket(AF_INET, SOCK_STREAM) +server.bind(('localhost', 6789)) +server.listen() +print('服务器已经启动正在监听客户端连接.') +while True: + client, addr = server.accept() + print('客户端%s:%d连接成功.' % (addr[0], addr[1])) + currtime = localtime(time()) + timestr = strftime('%Y-%m-%d %H:%M:%S', currtime) + client.send(timestr.encode('utf-8')) + client.close() +server.close() diff --git a/Day01-15/code/Day14/socket2.py b/Day01-15/code/Day14/socket2.py new file mode 100755 index 000000000..916d6e212 --- /dev/null +++ b/Day01-15/code/Day14/socket2.py @@ -0,0 +1,18 @@ +""" +套接字 - 基于TCP协议创建时间客户端 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-22 +""" + +from socket import * + +client = socket(AF_INET, SOCK_STREAM) +client.connect(('localhost', 6789)) +while True: + data = client.recv(1024) + if not data: + break + print(data.decode('utf-8')) +client.close() diff --git a/Day01-15/Day14/socket3.py b/Day01-15/code/Day14/socket3.py old mode 100644 new mode 100755 similarity index 77% rename from Day01-15/Day14/socket3.py rename to Day01-15/code/Day14/socket3.py index e70d154c8..0b80a1a8b --- a/Day01-15/Day14/socket3.py +++ b/Day01-15/code/Day14/socket3.py @@ -1,19 +1,16 @@ """ - 套接字 - 基于UDP协议Echo服务器 Version: 0.1 Author: 骆昊 Date: 2018-03-22 - """ - from socket import * from time import * server = socket(AF_INET, SOCK_DGRAM) server.bind(('localhost', 6789)) while True: - data, addr = server.recvfrom(1024) - server.sendto(data, addr) + data, addr = server.recvfrom(1024) + server.sendto(data, addr) server.close() diff --git a/Day01-15/code/Day14/socket4.py b/Day01-15/code/Day14/socket4.py new file mode 100755 index 000000000..d1ab5f3b9 --- /dev/null +++ b/Day01-15/code/Day14/socket4.py @@ -0,0 +1,20 @@ +""" +套接字 - 基于UDP协议创建Echo客户端 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-22 +""" + +from socket import * + +client = socket(AF_INET, SOCK_DGRAM) +while True: + data_str = input('请输入: ') + client.sendto(data_str.encode('utf-8'), ('localhost', 6789)) + data, addr = client.recvfrom(1024) + data_str = data.decode('utf-8') + print('服务器回应:', data_str) + if data_str == 'bye': + break +client.close() diff --git a/Day01-15/code/Day14/socket5.py b/Day01-15/code/Day14/socket5.py new file mode 100755 index 000000000..d22f81655 --- /dev/null +++ b/Day01-15/code/Day14/socket5.py @@ -0,0 +1,21 @@ +""" +使用socketserver模块创建时间服务器 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-22 +""" +from socketserver import TCPServer, StreamRequestHandler +from time import * + + +class EchoRequestHandler(StreamRequestHandler): + + def handle(self): + currtime = localtime(time()) + timestr = strftime('%Y-%m-%d %H:%M:%S', currtime) + self.wfile.write(timestr.encode('utf-8')) + + +server = TCPServer(('localhost', 6789), EchoRequestHandler) +server.serve_forever() diff --git a/Day01-15/Day14/timeclient.py b/Day01-15/code/Day14/timeclient.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day14/timeclient.py rename to Day01-15/code/Day14/timeclient.py diff --git a/Day01-15/Day14/timeserver.py b/Day01-15/code/Day14/timeserver.py old mode 100644 new mode 100755 similarity index 100% rename from Day01-15/Day14/timeserver.py rename to Day01-15/code/Day14/timeserver.py diff --git a/Day01-15/code/Day15/excel1.py b/Day01-15/code/Day15/excel1.py new file mode 100755 index 000000000..2e4f95452 --- /dev/null +++ b/Day01-15/code/Day15/excel1.py @@ -0,0 +1,26 @@ +""" +创建Excel文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-26 +""" +from openpyxl import Workbook +from openpyxl.worksheet.table import Table, TableStyleInfo + +workbook = Workbook() +sheet = workbook.active +data = [ + [1001, '白元芳', '男', '13123456789'], + [1002, '白洁', '女', '13233445566'] +] +sheet.append(['学号', '姓名', '性别', '电话']) +for row in data: + sheet.append(row) +tab = Table(displayName="Table1", ref="A1:E5") + +tab.tableStyleInfo = TableStyleInfo( + name="TableStyleMedium9", showFirstColumn=False, + showLastColumn=False, showRowStripes=True, showColumnStripes=True) +sheet.add_table(tab) +workbook.save('./res/全班学生数据.xlsx') diff --git a/Day01-15/code/Day15/excel2.py b/Day01-15/code/Day15/excel2.py new file mode 100755 index 000000000..c1d1cd865 --- /dev/null +++ b/Day01-15/code/Day15/excel2.py @@ -0,0 +1,20 @@ +""" +读取Excel文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-26 +""" + +from openpyxl import load_workbook +from openpyxl import Workbook + +workbook = load_workbook('./res/学生明细表.xlsx') +print(workbook.sheetnames) +sheet = workbook[workbook.sheetnames[0]] +print(sheet.title) +for row in range(2, 7): + for col in range(65, 70): + cell_index = chr(col) + str(row) + print(sheet[cell_index].value, end='\t') + print() diff --git a/Day01-15/code/Day15/pdf1.py b/Day01-15/code/Day15/pdf1.py new file mode 100755 index 000000000..def1585cb --- /dev/null +++ b/Day01-15/code/Day15/pdf1.py @@ -0,0 +1,7 @@ +""" +创建PDF文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-26 +""" \ No newline at end of file diff --git a/Day01-15/code/Day15/pdf2.py b/Day01-15/code/Day15/pdf2.py new file mode 100755 index 000000000..1c496c984 --- /dev/null +++ b/Day01-15/code/Day15/pdf2.py @@ -0,0 +1,18 @@ +""" +读取PDF文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-26 +""" + +from PyPDF2 import PdfFileReader + +with open('./res/Python课程大纲.pdf', 'rb') as f: + reader = PdfFileReader(f, strict=False) + print(reader.numPages) + if reader.isEncrypted: + reader.decrypt('') + current_page = reader.getPage(5) + print(current_page) + print(current_page.extractText()) diff --git a/Day01-15/code/Day15/pillow1.py b/Day01-15/code/Day15/pillow1.py new file mode 100755 index 000000000..17f57c551 --- /dev/null +++ b/Day01-15/code/Day15/pillow1.py @@ -0,0 +1,23 @@ +""" +使用pillow操作图像 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-26 +""" +from PIL import Image + +img = Image.open('./res/guido.jpg') +print(img.size) +print(img.format) +print(img.format_description) +img.save('./res/guido.png') + +img2 = Image.open('./res/guido.png') +img3 = img2.crop((335, 435, 430, 615)) +for x in range(4): + for y in range(5): + img2.paste(img3, (95 * y , 180 * x)) +img2.resize((img.size[0] // 2, img.size[1] // 2)) +img2.rotate(90) +img2.save('./res/guido2.png') diff --git "a/Day01-15/code/Day15/res/Docker\345\205\245\351\227\250\346\225\231\347\250\213.pdf" "b/Day01-15/code/Day15/res/Docker\345\205\245\351\227\250\346\225\231\347\250\213.pdf" new file mode 100755 index 000000000..10e98241b Binary files /dev/null and "b/Day01-15/code/Day15/res/Docker\345\205\245\351\227\250\346\225\231\347\250\213.pdf" differ diff --git a/Day01-15/code/Day15/res/guido.jpg b/Day01-15/code/Day15/res/guido.jpg new file mode 100755 index 000000000..a8f217c6d Binary files /dev/null and b/Day01-15/code/Day15/res/guido.jpg differ diff --git a/Day01-15/code/Day15/res/luohao.png b/Day01-15/code/Day15/res/luohao.png new file mode 100755 index 000000000..a1900740e Binary files /dev/null and b/Day01-15/code/Day15/res/luohao.png differ diff --git "a/Day01-15/code/Day15/res/\345\255\246\347\224\237\346\230\216\347\273\206\350\241\250.xlsx" "b/Day01-15/code/Day15/res/\345\255\246\347\224\237\346\230\216\347\273\206\350\241\250.xlsx" new file mode 100755 index 000000000..dce5c3e1a Binary files /dev/null and "b/Day01-15/code/Day15/res/\345\255\246\347\224\237\346\230\216\347\273\206\350\241\250.xlsx" differ diff --git "a/Day01-15/code/Day15/res/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.docx" "b/Day01-15/code/Day15/res/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.docx" new file mode 100755 index 000000000..7d105c782 Binary files /dev/null and "b/Day01-15/code/Day15/res/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.docx" differ diff --git a/Day01-15/code/Day15/word1.py b/Day01-15/code/Day15/word1.py new file mode 100755 index 000000000..e678bd25c --- /dev/null +++ b/Day01-15/code/Day15/word1.py @@ -0,0 +1,7 @@ +""" +创建Word文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-26 +""" \ No newline at end of file diff --git a/Day01-15/code/Day15/word2.py b/Day01-15/code/Day15/word2.py new file mode 100755 index 000000000..18b49baec --- /dev/null +++ b/Day01-15/code/Day15/word2.py @@ -0,0 +1,19 @@ +""" +读取Word文件 + +Version: 0.1 +Author: 骆昊 +Date: 2018-03-26 +""" + +from docx import Document + +doc = Document('./res/用函数还是用复杂的表达式.docx') +print(len(doc.paragraphs)) +print(doc.paragraphs[0].text) +# print(doc.paragraphs[1].runs[0].text) + +content = [] +for para in doc.paragraphs: + content.append(para.text) +print(''.join(content)) diff --git a/Day01-15/res/TCP-IP-model.png b/Day01-15/res/TCP-IP-model.png new file mode 100755 index 000000000..1c641fcde Binary files /dev/null and b/Day01-15/res/TCP-IP-model.png differ diff --git a/Day01-15/res/after-browser.jpg b/Day01-15/res/after-browser.jpg new file mode 100755 index 000000000..fb6b27cec Binary files /dev/null and b/Day01-15/res/after-browser.jpg differ diff --git a/Day01-15/res/arpanet.png b/Day01-15/res/arpanet.png new file mode 100755 index 000000000..3a1f632c4 Binary files /dev/null and b/Day01-15/res/arpanet.png differ diff --git a/Day01-15/res/ball-game.png b/Day01-15/res/ball-game.png new file mode 100755 index 000000000..a0b175bad Binary files /dev/null and b/Day01-15/res/ball-game.png differ diff --git a/Day01-15/res/ball.png b/Day01-15/res/ball.png new file mode 100755 index 000000000..496e6b59b Binary files /dev/null and b/Day01-15/res/ball.png differ diff --git a/Day01-15/res/before-browser.jpg b/Day01-15/res/before-browser.jpg new file mode 100755 index 000000000..89c3acf14 Binary files /dev/null and b/Day01-15/res/before-browser.jpg differ diff --git a/Day01-15/res/browers.jpg b/Day01-15/res/browers.jpg new file mode 100755 index 000000000..2802a2399 Binary files /dev/null and b/Day01-15/res/browers.jpg differ diff --git a/Day01-15/res/browser-market-place.jpeg b/Day01-15/res/browser-market-place.jpeg new file mode 100755 index 000000000..cb71d38d1 Binary files /dev/null and b/Day01-15/res/browser-market-place.jpeg differ diff --git a/Day01-15/res/fibonacci-blocks.png b/Day01-15/res/fibonacci-blocks.png new file mode 100755 index 000000000..be4fc13d4 Binary files /dev/null and b/Day01-15/res/fibonacci-blocks.png differ diff --git a/Day01-15/res/file-open-mode.png b/Day01-15/res/file-open-mode.png new file mode 100755 index 000000000..dd6b927f5 Binary files /dev/null and b/Day01-15/res/file-open-mode.png differ diff --git a/Day01-15/res/formula_1.png b/Day01-15/res/formula_1.png new file mode 100755 index 000000000..ec5e55104 Binary files /dev/null and b/Day01-15/res/formula_1.png differ diff --git a/Day01-15/res/formula_2.png b/Day01-15/res/formula_2.png new file mode 100755 index 000000000..954d92129 Binary files /dev/null and b/Day01-15/res/formula_2.png differ diff --git a/Day01-15/res/formula_3.png b/Day01-15/res/formula_3.png new file mode 100755 index 000000000..716835fcb Binary files /dev/null and b/Day01-15/res/formula_3.png differ diff --git a/Day01-15/res/formula_4.png b/Day01-15/res/formula_4.png new file mode 100755 index 000000000..c87452ffc Binary files /dev/null and b/Day01-15/res/formula_4.png differ diff --git a/Day01-15/res/formula_5.png b/Day01-15/res/formula_5.png new file mode 100755 index 000000000..29e0a5825 Binary files /dev/null and b/Day01-15/res/formula_5.png differ diff --git a/Day01-15/res/formula_6.png b/Day01-15/res/formula_6.png new file mode 100755 index 000000000..41e0dfa9a Binary files /dev/null and b/Day01-15/res/formula_6.png differ diff --git a/Day01-15/res/formula_7.png b/Day01-15/res/formula_7.png new file mode 100755 index 000000000..dfade18a1 Binary files /dev/null and b/Day01-15/res/formula_7.png differ diff --git a/Day01-15/res/formula_8.png b/Day01-15/res/formula_8.png new file mode 100755 index 000000000..f3f2e29dd Binary files /dev/null and b/Day01-15/res/formula_8.png differ diff --git a/Day01-15/res/how-data-is-processed.jpg b/Day01-15/res/how-data-is-processed.jpg new file mode 100755 index 000000000..b1f54868c Binary files /dev/null and b/Day01-15/res/how-data-is-processed.jpg differ diff --git a/Day01-15/res/image-crop.png b/Day01-15/res/image-crop.png new file mode 100755 index 000000000..fa19b5bd3 Binary files /dev/null and b/Day01-15/res/image-crop.png differ diff --git a/Day01-15/res/image-filter.png b/Day01-15/res/image-filter.png new file mode 100755 index 000000000..ff512e59f Binary files /dev/null and b/Day01-15/res/image-filter.png differ diff --git a/Day01-15/res/image-paste.png b/Day01-15/res/image-paste.png new file mode 100755 index 000000000..df25e85b4 Binary files /dev/null and b/Day01-15/res/image-paste.png differ diff --git a/Day01-15/res/image-putpixel.png b/Day01-15/res/image-putpixel.png new file mode 100755 index 000000000..4a4a7a8e8 Binary files /dev/null and b/Day01-15/res/image-putpixel.png differ diff --git a/Day01-15/res/image-rotate.png b/Day01-15/res/image-rotate.png new file mode 100755 index 000000000..84af30bfb Binary files /dev/null and b/Day01-15/res/image-rotate.png differ diff --git a/Day01-15/res/image-show.png b/Day01-15/res/image-show.png new file mode 100755 index 000000000..15723fbaa Binary files /dev/null and b/Day01-15/res/image-show.png differ diff --git a/Day01-15/res/image-thumbnail.png b/Day01-15/res/image-thumbnail.png new file mode 100755 index 000000000..a6d5239bc Binary files /dev/null and b/Day01-15/res/image-thumbnail.png differ diff --git a/Day01-15/res/image-transpose.png b/Day01-15/res/image-transpose.png new file mode 100755 index 000000000..43a80077c Binary files /dev/null and b/Day01-15/res/image-transpose.png differ diff --git a/Day01-15/res/ipython-timeit.png b/Day01-15/res/ipython-timeit.png new file mode 100755 index 000000000..912592184 Binary files /dev/null and b/Day01-15/res/ipython-timeit.png differ diff --git a/Day01-15/res/macos-monitor.png b/Day01-15/res/macos-monitor.png new file mode 100755 index 000000000..cac51381d Binary files /dev/null and b/Day01-15/res/macos-monitor.png differ diff --git a/Day01-15/res/object-feature.png b/Day01-15/res/object-feature.png new file mode 100755 index 000000000..538fa4d86 Binary files /dev/null and b/Day01-15/res/object-feature.png differ diff --git a/Day01-15/res/oop-zhihu.png b/Day01-15/res/oop-zhihu.png new file mode 100755 index 000000000..8aa05c64f Binary files /dev/null and b/Day01-15/res/oop-zhihu.png differ diff --git a/Day01-15/res/osi_rm.gif b/Day01-15/res/osi_rm.gif new file mode 100755 index 000000000..9228af5fb Binary files /dev/null and b/Day01-15/res/osi_rm.gif differ diff --git a/Day01-15/res/osimodel.png b/Day01-15/res/osimodel.png new file mode 100755 index 000000000..9c139681d Binary files /dev/null and b/Day01-15/res/osimodel.png differ diff --git a/Day01-15/res/python-idle.png b/Day01-15/res/python-idle.png new file mode 100755 index 000000000..e55248f61 Binary files /dev/null and b/Day01-15/res/python-idle.png differ diff --git a/Day01-15/res/python-ipython.png b/Day01-15/res/python-ipython.png new file mode 100755 index 000000000..35d34a7a2 Binary files /dev/null and b/Day01-15/res/python-ipython.png differ diff --git a/Day01-15/res/python-jupyter-1.png b/Day01-15/res/python-jupyter-1.png new file mode 100755 index 000000000..cad28e22b Binary files /dev/null and b/Day01-15/res/python-jupyter-1.png differ diff --git a/Day01-15/res/python-jupyter-2.png b/Day01-15/res/python-jupyter-2.png new file mode 100755 index 000000000..70102b04c Binary files /dev/null and b/Day01-15/res/python-jupyter-2.png differ diff --git a/Day01-15/res/python-pycharm.png b/Day01-15/res/python-pycharm.png new file mode 100755 index 000000000..57f2da55a Binary files /dev/null and b/Day01-15/res/python-pycharm.png differ diff --git a/Day01-15/res/python-set.png b/Day01-15/res/python-set.png new file mode 100755 index 000000000..0d59ce195 Binary files /dev/null and b/Day01-15/res/python-set.png differ diff --git a/Day01-15/res/python-sublime.png b/Day01-15/res/python-sublime.png new file mode 100755 index 000000000..ce9590d39 Binary files /dev/null and b/Day01-15/res/python-sublime.png differ diff --git a/Day01-15/res/tcpipprotocols.png b/Day01-15/res/tcpipprotocols.png new file mode 100755 index 000000000..bb62ed232 Binary files /dev/null and b/Day01-15/res/tcpipprotocols.png differ diff --git a/Day01-15/res/tel-start-number.png b/Day01-15/res/tel-start-number.png new file mode 100755 index 000000000..827522c8d Binary files /dev/null and b/Day01-15/res/tel-start-number.png differ diff --git a/Day01-15/res/telnet.png b/Day01-15/res/telnet.png new file mode 100755 index 000000000..1aa4d4dac Binary files /dev/null and b/Day01-15/res/telnet.png differ diff --git a/Day01-15/res/uml-components.png b/Day01-15/res/uml-components.png new file mode 100755 index 000000000..00598b84f Binary files /dev/null and b/Day01-15/res/uml-components.png differ diff --git a/Day01-15/res/uml-example.png b/Day01-15/res/uml-example.png new file mode 100755 index 000000000..1638fe79b Binary files /dev/null and b/Day01-15/res/uml-example.png differ diff --git "a/Day16-20/16-20.Python\350\257\255\350\250\200\350\277\233\351\230\266.md" "b/Day16-20/16-20.Python\350\257\255\350\250\200\350\277\233\351\230\266.md" new file mode 100755 index 000000000..caed2efc1 --- /dev/null +++ "b/Day16-20/16-20.Python\350\257\255\350\250\200\350\277\233\351\230\266.md" @@ -0,0 +1,1376 @@ +## Python语言进阶 + +### 重要知识点 + +- 生成式(推导式)的用法 + + ```Python + prices = { + 'AAPL': 191.88, + 'GOOG': 1186.96, + 'IBM': 149.24, + 'ORCL': 48.44, + 'ACN': 166.89, + 'FB': 208.09, + 'SYMC': 21.29 + } + # 用股票价格大于100元的股票构造一个新的字典 + prices2 = {key: value for key, value in prices.items() if value > 100} + print(prices2) + ``` + + > 说明:生成式(推导式)可以用来生成列表、集合和字典。 + +- 嵌套的列表的坑 + + ```Python + names = ['关羽', '张飞', '赵云', '马超', '黄忠'] + courses = ['语文', '数学', '英语'] + # 录入五个学生三门课程的成绩 + # 错误 - 参考http://pythontutor.com/visualize.html#mode=edit + # scores = [[None] * len(courses)] * len(names) + scores = [[None] * len(courses) for _ in range(len(names))] + for row, name in enumerate(names): + for col, course in enumerate(courses): + scores[row][col] = float(input(f'请输入{name}的{course}成绩: ')) + print(scores) + ``` + + [Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP + +- `heapq`模块(堆排序) + + ```Python + """ + 从列表中找出最大的或最小的N个元素 + 堆结构(大根堆/小根堆) + """ + import heapq + + list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92] + list2 = [ + {'name': 'IBM', 'shares': 100, 'price': 91.1}, + {'name': 'AAPL', 'shares': 50, 'price': 543.22}, + {'name': 'FB', 'shares': 200, 'price': 21.09}, + {'name': 'HPQ', 'shares': 35, 'price': 31.75}, + {'name': 'YHOO', 'shares': 45, 'price': 16.35}, + {'name': 'ACME', 'shares': 75, 'price': 115.65} + ] + print(heapq.nlargest(3, list1)) + print(heapq.nsmallest(3, list1)) + print(heapq.nlargest(2, list2, key=lambda x: x['price'])) + print(heapq.nlargest(2, list2, key=lambda x: x['shares'])) + ``` + +- `itertools`模块 + + ```Python + """ + 迭代工具模块 + """ + import itertools + + # 产生ABCD的全排列 + itertools.permutations('ABCD') + # 产生ABCDE的五选三组合 + itertools.combinations('ABCDE', 3) + # 产生ABCD和123的笛卡尔积 + itertools.product('ABCD', '123') + # 产生ABC的无限循环序列 + itertools.cycle(('A', 'B', 'C')) + ``` + +- `collections`模块 + + 常用的工具类: + + - `namedtuple`:命令元组,它是一个类工厂,接受类型的名称和属性列表来创建一个类。 + - `deque`:双端队列,是列表的替代实现。Python中的列表底层是基于数组来实现的,而deque底层是双向链表,因此当你需要在头尾添加和删除元素时,deque会表现出更好的性能,渐近时间复杂度为$O(1)$。 + - `Counter`:`dict`的子类,键是元素,值是元素的计数,它的`most_common()`方法可以帮助我们获取出现频率最高的元素。`Counter`和`dict`的继承关系我认为是值得商榷的,按照CARP原则,`Counter`跟`dict`的关系应该设计为关联关系更为合理。 + - `OrderedDict`:`dict`的子类,它记录了键值对插入的顺序,看起来既有字典的行为,也有链表的行为。 + - `defaultdict`:类似于字典类型,但是可以通过默认的工厂函数来获得键对应的默认值,相比字典中的`setdefault()`方法,这种做法更加高效。 + + ```Python + """ + 找出序列中出现次数最多的元素 + """ + from collections import Counter + + words = [ + 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', + 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', + 'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes', + 'look', 'into', 'my', 'eyes', "you're", 'under' + ] + counter = Counter(words) + print(counter.most_common(3)) + ``` + +### 数据结构和算法 + +- 算法:解决问题的方法和步骤 + +- 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。 + +- 渐近时间复杂度的大O标记: + - - 常量时间复杂度 - 布隆过滤器 / 哈希存储 + - - 对数时间复杂度 - 折半查找(二分查找) + - - 线性时间复杂度 - 顺序查找 / 计数排序 + - - 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序) + - - 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序) + - - 立方时间复杂度 - Floyd算法 / 矩阵乘法运算 + - - 几何级数时间复杂度 - 汉诺塔 + - - 阶乘时间复杂度 - 旅行经销商问题 - NPC + + ![](./res/algorithm_complexity_1.png) + + ![](./res/algorithm_complexity_2.png) + +- 排序算法(选择、冒泡和归并)和查找算法(顺序和折半) + + ```Python + def select_sort(items, comp=lambda x, y: x < y): + """简单选择排序""" + items = items[:] + for i in range(len(items) - 1): + min_index = i + for j in range(i + 1, len(items)): + if comp(items[j], items[min_index]): + min_index = j + items[i], items[min_index] = items[min_index], items[i] + return items + ``` + + ```Python + def bubble_sort(items, comp=lambda x, y: x > y): + """冒泡排序""" + items = items[:] + for i in range(len(items) - 1): + swapped = False + for j in range(len(items) - 1 - i): + if comp(items[j], items[j + 1]): + items[j], items[j + 1] = items[j + 1], items[j] + swapped = True + if not swapped: + break + return items + ``` + + ```Python + def bubble_sort(items, comp=lambda x, y: x > y): + """搅拌排序(冒泡排序升级版)""" + items = items[:] + for i in range(len(items) - 1): + swapped = False + for j in range(len(items) - 1 - i): + if comp(items[j], items[j + 1]): + items[j], items[j + 1] = items[j + 1], items[j] + swapped = True + if swapped: + swapped = False + for j in range(len(items) - 2 - i, i, -1): + if comp(items[j - 1], items[j]): + items[j], items[j - 1] = items[j - 1], items[j] + swapped = True + if not swapped: + break + return items + ``` + + ```Python + def merge(items1, items2, comp=lambda x, y: x < y): + """合并(将两个有序的列表合并成一个有序的列表)""" + items = [] + index1, index2 = 0, 0 + while index1 < len(items1) and index2 < len(items2): + if comp(items1[index1], items2[index2]): + items.append(items1[index1]) + index1 += 1 + else: + items.append(items2[index2]) + index2 += 1 + items += items1[index1:] + items += items2[index2:] + return items + + + def merge_sort(items, comp=lambda x, y: x < y): + return _merge_sort(list(items), comp) + + + def _merge_sort(items, comp): + """归并排序""" + if len(items) < 2: + return items + mid = len(items) // 2 + left = _merge_sort(items[:mid], comp) + right = _merge_sort(items[mid:], comp) + return merge(left, right, comp) + ``` + + ```Python + def seq_search(items, key): + """顺序查找""" + for index, item in enumerate(items): + if item == key: + return index + return -1 + ``` + + ```Python + def bin_search(items, key): + """折半查找""" + start, end = 0, len(items) - 1 + while start <= end: + mid = (start + end) // 2 + if key > items[mid]: + start = mid + 1 + elif key < items[mid]: + end = mid - 1 + else: + return mid + return -1 + ``` + +- 常用算法: + + - 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。 + - 贪婪法 - 在对问题求解时,总是做出在当前看来 + - 最好的选择,不追求最优解,快速找到满意解。 + - 分治法 - 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到可以直接求解的程度,最后将子问题的解进行合并得到原问题的解。 + - 回溯法 - 回溯法又称为试探法,按选优条件向前搜索,当搜索到某一步发现原先选择并不优或达不到目标时,就退回一步重新选择。 + - 动态规划 - 基本思想也是将待求解问题分解成若干个子问题,先求解并保存这些子问题的解,避免产生大量的重复运算。 + + 穷举法例子:百钱百鸡和五人分鱼。 + + ```Python + # 公鸡5元一只 母鸡3元一只 小鸡1元三只 + # 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只 + for x in range(20): + for y in range(33): + z = 100 - x - y + if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0: + print(x, y, z) + + # A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉 + # 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份 + # B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份 + # 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼 + fish = 6 + while True: + total = fish + enough = True + for _ in range(5): + if (total - 1) % 5 == 0: + total = (total - 1) // 5 * 4 + else: + enough = False + break + if enough: + print(fish) + break + fish += 5 + ``` + + 贪婪法例子:假设小偷有一个背包,最多能装20公斤赃物,他闯入一户人家,发现如下表所示的物品。很显然,他不能把所有物品都装进背包,所以必须确定拿走哪些物品,留下哪些物品。 + + | 名称 | 价格(美元) | 重量(kg) | + | :----: | :----------: | :--------: | + | 电脑 | 200 | 20 | + | 收音机 | 20 | 4 | + | 钟 | 175 | 10 | + | 花瓶 | 50 | 2 | + | 书 | 10 | 1 | + | 油画 | 90 | 9 | + + ```Python + """ + 贪婪法:在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。 + 输入: + 20 6 + 电脑 200 20 + 收音机 20 4 + 钟 175 10 + 花瓶 50 2 + 书 10 1 + 油画 90 9 + """ + class Thing(object): + """物品""" + + def __init__(self, name, price, weight): + self.name = name + self.price = price + self.weight = weight + + @property + def value(self): + """价格重量比""" + return self.price / self.weight + + + def input_thing(): + """输入物品信息""" + name_str, price_str, weight_str = input().split() + return name_str, int(price_str), int(weight_str) + + + def main(): + """主函数""" + max_weight, num_of_things = map(int, input().split()) + all_things = [] + for _ in range(num_of_things): + all_things.append(Thing(*input_thing())) + all_things.sort(key=lambda x: x.value, reverse=True) + total_weight = 0 + total_price = 0 + for thing in all_things: + if total_weight + thing.weight <= max_weight: + print(f'小偷拿走了{thing.name}') + total_weight += thing.weight + total_price += thing.price + print(f'总价值: {total_price}美元') + + + if __name__ == '__main__': + main() + ``` + + 分治法例子:[快速排序](https://zh.wikipedia.org/zh/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F)。 + + ```Python + """ + 快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大 + """ + def quick_sort(items, comp=lambda x, y: x <= y): + items = list(items)[:] + _quick_sort(items, 0, len(items) - 1, comp) + return items + + + def _quick_sort(items, start, end, comp): + if start < end: + pos = _partition(items, start, end, comp) + _quick_sort(items, start, pos - 1, comp) + _quick_sort(items, pos + 1, end, comp) + + + def _partition(items, start, end, comp): + pivot = items[end] + i = start - 1 + for j in range(start, end): + if comp(items[j], pivot): + i += 1 + items[i], items[j] = items[j], items[i] + items[i + 1], items[end] = items[end], items[i + 1] + return i + 1 + ``` + + 回溯法例子:[骑士巡逻](https://zh.wikipedia.org/zh/%E9%AA%91%E5%A3%AB%E5%B7%A1%E9%80%BB)。 + + ```Python + """ + 递归回溯法:叫称为试探法,按选优条件向前搜索,当搜索到某一步,发现原先选择并不优或达不到目标时,就退回一步重新选择,比较经典的问题包括骑士巡逻、八皇后和迷宫寻路等。 + """ + import sys + import time + + SIZE = 5 + total = 0 + + + def print_board(board): + for row in board: + for col in row: + print(str(col).center(4), end='') + print() + + + def patrol(board, row, col, step=1): + if row >= 0 and row < SIZE and \ + col >= 0 and col < SIZE and \ + board[row][col] == 0: + board[row][col] = step + if step == SIZE * SIZE: + global total + total += 1 + print(f'第{total}种走法: ') + print_board(board) + patrol(board, row - 2, col - 1, step + 1) + patrol(board, row - 1, col - 2, step + 1) + patrol(board, row + 1, col - 2, step + 1) + patrol(board, row + 2, col - 1, step + 1) + patrol(board, row + 2, col + 1, step + 1) + patrol(board, row + 1, col + 2, step + 1) + patrol(board, row - 1, col + 2, step + 1) + patrol(board, row - 2, col + 1, step + 1) + board[row][col] = 0 + + + def main(): + board = [[0] * SIZE for _ in range(SIZE)] + patrol(board, SIZE - 1, SIZE - 1) + + + if __name__ == '__main__': + main() + ``` + + 动态规划例子:子列表元素之和的最大值。 + + > 说明:子列表指的是列表中索引(下标)连续的元素构成的列表;列表中的元素是int类型,可能包含正整数、0、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如: + > + > 输入:1 -2 3 5 -3 2 + > + > 输出:8 + > + > 输入:0 -2 3 5 -1 2 + > + > 输出:9 + > + > 输入:-9 -2 -3 -5 -3 + > + > 输出:-2 + + ```Python + def main(): + items = list(map(int, input().split())) + overall = partial = items[0] + for i in range(1, len(items)): + partial = max(items[i], partial + items[i]) + overall = max(partial, overall) + print(overall) + + + if __name__ == '__main__': + main() + ``` + + > **说明**:这个题目最容易想到的解法是使用二重循环,但是代码的时间性能将会变得非常的糟糕。使用动态规划的思想,仅仅是多用了两个变量,就将原来$O(N^2)$复杂度的问题变成了$O(N)$。 + +### 函数的使用方式 + +- 将函数视为“一等公民” + + - 函数可以赋值给变量 + - 函数可以作为函数的参数 + - 函数可以作为函数的返回值 + +- 高阶函数的用法(`filter`、`map`以及它们的替代品) + + ```Python + items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, range(1, 10)))) + items2 = [x ** 2 for x in range(1, 10) if x % 2] + ``` + +- 位置参数、可变参数、关键字参数、命名关键字参数 + +- 参数的元信息(代码可读性问题) + +- 匿名函数和内联函数的用法(`lambda`函数) + +- 闭包和作用域问题 + + - Python搜索变量的LEGB顺序(Local >>> Embedded >>> Global >>> Built-in) + + - `global`和`nonlocal`关键字的作用 + + `global`:声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域)。 + + `nonlocal`:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。 + +- 装饰器函数(使用装饰器和取消装饰器) + + 例子:输出函数执行时间的装饰器。 + + ```Python + def record_time(func): + """自定义装饰函数的装饰器""" + + @wraps(func) + def wrapper(*args, **kwargs): + start = time() + result = func(*args, **kwargs) + print(f'{func.__name__}: {time() - start}秒') + return result + + return wrapper + ``` + + 如果装饰器不希望跟`print`函数耦合,可以编写可以参数化的装饰器。 + + ```Python + from functools import wraps + from time import time + + + def record(output): + """可以参数化的装饰器""" + + def decorate(func): + + @wraps(func) + def wrapper(*args, **kwargs): + start = time() + result = func(*args, **kwargs) + output(func.__name__, time() - start) + return result + + return wrapper + + return decorate + ``` + + ```Python + from functools import wraps + from time import time + + + class Record(): + """通过定义类的方式定义装饰器""" + + def __init__(self, output): + self.output = output + + def __call__(self, func): + + @wraps(func) + def wrapper(*args, **kwargs): + start = time() + result = func(*args, **kwargs) + self.output(func.__name__, time() - start) + return result + + return wrapper + ``` + + > **说明**:由于对带装饰功能的函数添加了@wraps装饰器,可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。 + + 例子:用装饰器来实现单例模式。 + + ```Python + from functools import wraps + + + def singleton(cls): + """装饰类的装饰器""" + instances = {} + + @wraps(cls) + def wrapper(*args, **kwargs): + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return wrapper + + + @singleton + class President: + """总统(单例类)""" + pass + ``` + + > **提示**:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢? + + 线程安全的单例装饰器。 + + ```Python + from functools import wraps + from threading import RLock + + + def singleton(cls): + """线程安全的单例装饰器""" + instances = {} + locker = RLock() + + @wraps(cls) + def wrapper(*args, **kwargs): + if cls not in instances: + with locker: + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return wrapper + ``` + + > **提示**:上面的代码用到了`with`上下文语法来进行锁操作,因为锁对象本身就是上下文管理器对象(支持`__enter__`和`__exit__`魔术方法)。在`wrapper`函数中,我们先做了一次不带锁的检查,然后再做带锁的检查,这样做比直接加锁检查性能要更好,如果对象已经创建就没有必须再去加锁而是直接返回该对象就可以了。 + +### 面向对象相关知识 + +- 三大支柱:封装、继承、多态 + + 例子:工资结算系统。 + + ```Python + """ + 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成 + """ + from abc import ABCMeta, abstractmethod + + + class Employee(metaclass=ABCMeta): + """员工(抽象类)""" + + def __init__(self, name): + self.name = name + + @abstractmethod + def get_salary(self): + """结算月薪(抽象方法)""" + pass + + + class Manager(Employee): + """部门经理""" + + def get_salary(self): + return 15000.0 + + + class Programmer(Employee): + """程序员""" + + def __init__(self, name, working_hour=0): + self.working_hour = working_hour + super().__init__(name) + + def get_salary(self): + return 200.0 * self.working_hour + + + class Salesman(Employee): + """销售员""" + + def __init__(self, name, sales=0.0): + self.sales = sales + super().__init__(name) + + def get_salary(self): + return 1800.0 + self.sales * 0.05 + + + class EmployeeFactory: + """创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)""" + + @staticmethod + def create(emp_type, *args, **kwargs): + """创建员工""" + all_emp_types = {'M': Manager, 'P': Programmer, 'S': Salesman} + cls = all_emp_types[emp_type.upper()] + return cls(*args, **kwargs) if cls else None + + + def main(): + """主函数""" + emps = [ + EmployeeFactory.create('M', '曹操'), + EmployeeFactory.create('P', '荀彧', 120), + EmployeeFactory.create('P', '郭嘉', 85), + EmployeeFactory.create('S', '典韦', 123000), + ] + for emp in emps: + print(f'{emp.name}: {emp.get_salary():.2f}元') + + + if __name__ == '__main__': + main() + ``` + +- 类与类之间的关系 + + - is-a关系:继承 + - has-a关系:关联 / 聚合 / 合成 + - use-a关系:依赖 + + 例子:扑克游戏。 + + ```Python + """ + 经验:符号常量总是优于字面常量,枚举类型是定义符号常量的最佳选择 + """ + from enum import Enum, unique + + import random + + + @unique + class Suite(Enum): + """花色""" + + SPADE, HEART, CLUB, DIAMOND = range(4) + + def __lt__(self, other): + return self.value < other.value + + + class Card: + """牌""" + + def __init__(self, suite, face): + """初始化方法""" + self.suite = suite + self.face = face + + def show(self): + """显示牌面""" + suites = ['♠︎', '♥︎', '♣︎', '♦︎'] + faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'] + return f'{suites[self.suite.value]}{faces[self.face]}' + + def __repr__(self): + return self.show() + + + class Poker: + """扑克""" + + def __init__(self): + self.index = 0 + self.cards = [Card(suite, face) + for suite in Suite + for face in range(1, 14)] + + def shuffle(self): + """洗牌(随机乱序)""" + random.shuffle(self.cards) + self.index = 0 + + def deal(self): + """发牌""" + card = self.cards[self.index] + self.index += 1 + return card + + @property + def has_more(self): + return self.index < len(self.cards) + + + class Player: + """玩家""" + + def __init__(self, name): + self.name = name + self.cards = [] + + def get_one(self, card): + """摸一张牌""" + self.cards.append(card) + + def sort(self, comp=lambda card: (card.suite, card.face)): + """整理手上的牌""" + self.cards.sort(key=comp) + + + def main(): + """主函数""" + poker = Poker() + poker.shuffle() + players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')] + while poker.has_more: + for player in players: + player.get_one(poker.deal()) + for player in players: + player.sort() + print(player.name, end=': ') + print(player.cards) + + + if __name__ == '__main__': + main() + ``` + + > **说明**:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。 + +- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆) + +- 垃圾回收、循环引用和弱引用 + + Python使用了自动化内存管理,这种管理机制以**引用计数**为基础,同时也引入了**标记-清除**和**分代收集**两种机制为辅的策略。 + + ```C + typedef struct _object { + /* 引用计数 */ + int ob_refcnt; + /* 对象指针 */ + struct _typeobject *ob_type; + } PyObject; + ``` + + ```C + /* 增加引用计数的宏定义 */ + #define Py_INCREF(op) ((op)->ob_refcnt++) + /* 减少引用计数的宏定义 */ + #define Py_DECREF(op) \ //减少计数 + if (--(op)->ob_refcnt != 0) \ + ; \ + else \ + __Py_Dealloc((PyObject *)(op)) + ``` + + 导致引用计数+1的情况: + + - 对象被创建,例如`a = 23` + - 对象被引用,例如`b = a` + - 对象被作为参数,传入到一个函数中,例如`f(a)` + - 对象作为一个元素,存储在容器中,例如`list1 = [a, a]` + + 导致引用计数-1的情况: + + - 对象的别名被显式销毁,例如`del a` + - 对象的别名被赋予新的对象,例如`a = 24` + - 一个对象离开它的作用域,例如f函数执行完毕时,f函数中的局部变量(全局变量不会) + - 对象所在的容器被销毁,或从容器中删除对象 + + 引用计数可能会导致循环引用问题,而循环引用会导致内存泄露,如下面的代码所示。为了解决这个问题,Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。 + + ```Python + # 循环引用会导致内存泄露 - Python除了引用技术还引入了标记清理和分代回收 + # 在Python 3.6以前如果重写__del__魔术方法会导致循环引用处理失效 + # 如果不想造成循环引用可以使用弱引用 + list1 = [] + list2 = [] + list1.append(list2) + list2.append(list1) + ``` + + 以下情况会导致垃圾回收: + + - 调用`gc.collect()` + - `gc`模块的计数器达到阀值 + - 程序退出 + + 如果循环引用中两个对象都定义了`__del__`方法,`gc`模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。 + + 也可以通过`weakref`模块构造弱引用的方式来解决循环引用的问题。 + +- 魔法属性和方法(请参考《Python魔法方法指南》) + + 有几个小问题请大家思考: + + - 自定义的对象能不能使用运算符做运算? + - 自定义的对象能不能放到`set`中?能去重吗? + - 自定义的对象能不能作为`dict`的键? + - 自定义的对象能不能使用上下文语法? + +- 混入(Mixin) + + 例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。 + + ```Python + class SetOnceMappingMixin: + """自定义混入类""" + __slots__ = () + + def __setitem__(self, key, value): + if key in self: + raise KeyError(str(key) + ' already set') + return super().__setitem__(key, value) + + + class SetOnceDict(SetOnceMappingMixin, dict): + """自定义字典""" + pass + + + my_dict= SetOnceDict() + try: + my_dict['username'] = 'jackfrued' + my_dict['username'] = 'hellokitty' + except KeyError: + pass + print(my_dict) + ``` + +- 元编程和元类 + + 对象是通过类创建的,类是通过元类创建的,元类提供了创建类的元信息。所有的类都直接或间接的继承自`object`,所有的元类都直接或间接的继承自`type`。 + + 例子:用元类实现单例模式。 + + ```Python + import threading + + + class SingletonMeta(type): + """自定义元类""" + + def __init__(cls, *args, **kwargs): + cls.__instance = None + cls.__lock = threading.RLock() + super().__init__(*args, **kwargs) + + def __call__(cls, *args, **kwargs): + if cls.__instance is None: + with cls.__lock: + if cls.__instance is None: + cls.__instance = super().__call__(*args, **kwargs) + return cls.__instance + + + class President(metaclass=SingletonMeta): + """总统(单例类)""" + + pass + ``` + +- 面向对象设计原则 + + - 单一职责原则 (**S**RP)- 一个类只做该做的事情(类的设计要高内聚) + - 开闭原则 (**O**CP)- 软件实体应该对扩展开发对修改关闭 + - 依赖倒转原则(DIP)- 面向抽象编程(在弱类型语言中已经被弱化) + - 里氏替换原则(**L**SP) - 任何时候可以用子类对象替换掉父类对象 + - 接口隔离原则(**I**SP)- 接口要小而专不要大而全(Python中没有接口的概念) + - 合成聚合复用原则(CARP) - 优先使用强关联关系而不是继承关系复用代码 + - 最少知识原则(迪米特法则,Lo**D**)- 不要给没有必然联系的对象发消息 + + > **说明**:上面加粗的字母放在一起称为面向对象的**SOLID**原则。 + +- GoF设计模式 + + - 创建型模式:单例、工厂、建造者、原型 + - 结构型模式:适配器、门面(外观)、代理 + - 行为型模式:迭代器、观察者、状态、策略 + + 例子:可插拔的哈希算法(策略模式)。 + + ```Python + class StreamHasher: + """哈希摘要生成器""" + + def __init__(self, alg='md5', size=4096): + self.size = size + alg = alg.lower() + self.hasher = getattr(__import__('hashlib'), alg.lower())() + + def __call__(self, stream): + return self.to_digest(stream) + + def to_digest(self, stream): + """生成十六进制形式的摘要""" + for buf in iter(lambda: stream.read(self.size), b''): + self.hasher.update(buf) + return self.hasher.hexdigest() + + def main(): + """主函数""" + hasher1 = StreamHasher() + with open('Python-3.7.6.tgz', 'rb') as stream: + print(hasher1.to_digest(stream)) + hasher2 = StreamHasher('sha1') + with open('Python-3.7.6.tgz', 'rb') as stream: + print(hasher2(stream)) + + + if __name__ == '__main__': + main() + ``` + +### 迭代器和生成器 + +- 迭代器是实现了迭代器协议的对象。 + + - Python中没有像`protocol`或`interface`这样的定义协议的关键字。 + - Python中用魔术方法表示协议。 + - `__iter__`和`__next__`魔术方法就是迭代器协议。 + + ```Python + class Fib(object): + """迭代器""" + + def __init__(self, num): + self.num = num + self.a, self.b = 0, 1 + self.idx = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.idx < self.num: + self.a, self.b = self.b, self.a + self.b + self.idx += 1 + return self.a + raise StopIteration() + ``` + +- 生成器是语法简化版的迭代器。 + + ```Python + def fib(num): + """生成器""" + a, b = 0, 1 + for _ in range(num): + a, b = b, a + b + yield a + ``` + +- 生成器进化为协程。 + + 生成器对象可以使用`send()`方法发送数据,发送的数据会成为生成器函数中通过`yield`表达式获得的值。这样,生成器就可以作为协程使用,协程简单的说就是可以相互协作的子程序。 + + ```Python + def calc_avg(): + """流式计算平均值""" + total, counter = 0, 0 + avg_value = None + while True: + value = yield avg_value + total, counter = total + value, counter + 1 + avg_value = total / counter + + + gen = calc_avg() + next(gen) + print(gen.send(10)) + print(gen.send(20)) + print(gen.send(30)) + ``` + +### 并发编程 + +Python中实现并发编程的三种方案:多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验;坏处在于并发的程序不容易开发和调试,同时对其他程序来说它并不友好。 + +- 多线程:Python中提供了`Thread`类并辅以`Lock`、`Condition`、`Event`、`Semaphore`和`Barrier`。Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。 + + ```Python + """ + 面试题:进程和线程的区别和联系? + 进程 - 操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程 + 线程 - 操作系统分配CPU的基本单位 + 并发编程(concurrent programming) + 1. 提升执行性能 - 让程序中没有因果关系的部分可以并发的执行 + 2. 改善用户体验 - 让耗时间的操作不会造成程序的假死 + """ + import glob + import os + import threading + + from PIL import Image + + PREFIX = 'thumbnails' + + + def generate_thumbnail(infile, size, format='PNG'): + """生成指定图片文件的缩略图""" + file, ext = os.path.splitext(infile) + file = file[file.rfind('/') + 1:] + outfile = f'{PREFIX}/{file}_{size[0]}_{size[1]}.{ext}' + img = Image.open(infile) + img.thumbnail(size, Image.ANTIALIAS) + img.save(outfile, format) + + + def main(): + """主函数""" + if not os.path.exists(PREFIX): + os.mkdir(PREFIX) + for infile in glob.glob('images/*.png'): + for size in (32, 64, 128): + # 创建并启动线程 + threading.Thread( + target=generate_thumbnail, + args=(infile, (size, size)) + ).start() + + + if __name__ == '__main__': + main() + ``` + + 多个线程竞争资源的情况。 + + ```Python + """ + 多线程程序如果没有竞争资源处理起来通常也比较简单 + 当多个线程竞争临界资源的时候如果缺乏必要的保护措施就会导致数据错乱 + 说明:临界资源就是被多个线程竞争的资源 + """ + import time + import threading + + from concurrent.futures import ThreadPoolExecutor + + + class Account(object): + """银行账户""" + + def __init__(self): + self.balance = 0.0 + self.lock = threading.Lock() + + def deposit(self, money): + # 通过锁保护临界资源 + with self.lock: + new_balance = self.balance + money + time.sleep(0.001) + self.balance = new_balance + + + def main(): + """主函数""" + account = Account() + # 创建线程池 + pool = ThreadPoolExecutor(max_workers=10) + futures = [] + for _ in range(100): + future = pool.submit(account.deposit, 1) + futures.append(future) + # 关闭线程池 + pool.shutdown() + for future in futures: + future.result() + print(account.balance) + + + if __name__ == '__main__': + main() + ``` + + 修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用`threading`模块的`Condition`来实现线程调度,该对象也是基于锁来创建的,代码如下所示: + + ```Python + """ + 多个线程竞争一个资源 - 保护临界资源 - 锁(Lock/RLock) + 多个线程竞争多个资源(线程数>资源数) - 信号量(Semaphore) + 多个线程的调度 - 暂停线程执行/唤醒等待中的线程 - Condition + """ + from concurrent.futures import ThreadPoolExecutor + from random import randint + from time import sleep + + import threading + + + class Account: + """银行账户""" + + def __init__(self, balance=0): + self.balance = balance + lock = threading.RLock() + self.condition = threading.Condition(lock) + + def withdraw(self, money): + """取钱""" + with self.condition: + while money > self.balance: + self.condition.wait() + new_balance = self.balance - money + sleep(0.001) + self.balance = new_balance + + def deposit(self, money): + """存钱""" + with self.condition: + new_balance = self.balance + money + sleep(0.001) + self.balance = new_balance + self.condition.notify_all() + + + def add_money(account): + while True: + money = randint(5, 10) + account.deposit(money) + print(threading.current_thread().name, + ':', money, '====>', account.balance) + sleep(0.5) + + + def sub_money(account): + while True: + money = randint(10, 30) + account.withdraw(money) + print(threading.current_thread().name, + ':', money, '<====', account.balance) + sleep(1) + + + def main(): + account = Account() + with ThreadPoolExecutor(max_workers=15) as pool: + for _ in range(5): + pool.submit(add_money, account) + for _ in range(10): + pool.submit(sub_money, account) + + + if __name__ == '__main__': + main() + ``` + +- 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是`Process`,其他辅助的类跟`threading`模块中的类似,进程间共享数据可以使用管道、套接字等,在`multiprocessing`模块中有一个`Queue`类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。 + + ```Python + """ + 多进程和进程池的使用 + 多线程因为GIL的存在不能够发挥CPU的多核特性 + 对于计算密集型任务应该考虑使用多进程 + time python3 example22.py + real 0m11.512s + user 0m39.319s + sys 0m0.169s + 使用多进程后实际执行时间为11.512秒,而用户时间39.319秒约为实际执行时间的4倍 + 这就证明我们的程序通过多进程使用了CPU的多核特性,而且这台计算机配置了4核的CPU + """ + import concurrent.futures + import math + + PRIMES = [ + 1116281, + 1297337, + 104395303, + 472882027, + 533000389, + 817504243, + 982451653, + 112272535095293, + 112582705942171, + 112272535095293, + 115280095190773, + 115797848077099, + 1099726899285419 + ] * 5 + + + def is_prime(n): + """判断素数""" + if n % 2 == 0: + return False + + sqrt_n = int(math.floor(math.sqrt(n))) + for i in range(3, sqrt_n + 1, 2): + if n % i == 0: + return False + return True + + + def main(): + """主函数""" + with concurrent.futures.ProcessPoolExecutor() as executor: + for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)): + print('%d is prime: %s' % (number, prime)) + + + if __name__ == '__main__': + main() + ``` + + > **重点**:**多线程和多进程的比较**。 + > + > 以下情况需要使用多线程: + > + > 1. 程序需要维护许多共享的状态(尤其是可变状态),Python中的列表、字典、集合都是线程安全的,所以使用线程而不是进程维护共享状态的代价相对较小。 + > 2. 程序会花费大量时间在I/O操作上,没有太多并行计算的需求且不需占用太多的内存。 + > + > 以下情况需要使用多进程: + > + > 1. 程序执行计算密集型任务(如:字节码操作、数据处理、科学计算)。 + > 2. 程序的输入可以并行的分成块,并且可以将运算结果合并。 + > 3. 程序在内存使用方面没有任何限制且不强依赖于I/O操作(如:读写文件、套接字等)。 + +- 异步处理:从调度程序的任务队列中挑选任务,该调度程序以交叉的形式执行这些任务,我们并不能保证任务将以某种顺序去执行,因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现,由于执行时间和顺序的不确定,因此需要通过回调式编程或者`future`对象来获取任务执行的结果。Python 3通过`asyncio`模块和`await`和`async`关键字(在Python 3.7中正式被列为关键字)来支持异步处理。 + + ```Python + """ + 异步I/O - async / await + """ + import asyncio + + + def num_generator(m, n): + """指定范围的数字生成器""" + yield from range(m, n + 1) + + + async def prime_filter(m, n): + """素数过滤器""" + primes = [] + for i in num_generator(m, n): + flag = True + for j in range(2, int(i ** 0.5 + 1)): + if i % j == 0: + flag = False + break + if flag: + print('Prime =>', i) + primes.append(i) + + await asyncio.sleep(0.001) + return tuple(primes) + + + async def square_mapper(m, n): + """平方映射器""" + squares = [] + for i in num_generator(m, n): + print('Square =>', i * i) + squares.append(i * i) + + await asyncio.sleep(0.001) + return squares + + + def main(): + """主函数""" + loop = asyncio.get_event_loop() + future = asyncio.gather(prime_filter(2, 100), square_mapper(1, 100)) + future.add_done_callback(lambda x: print(x.result())) + loop.run_until_complete(future) + loop.close() + + + if __name__ == '__main__': + main() + ``` + + > **说明**:上面的代码使用`get_event_loop`函数获得系统默认的事件循环,通过`gather`函数可以获得一个`future`对象,`future`对象的`add_done_callback`可以添加执行完成时的回调函数,`loop`对象的`run_until_complete`方法可以等待通过`future`对象获得协程执行结果。 + + Python中有一个名为`aiohttp`的三方库,它提供了异步的HTTP客户端和服务器,这个三方库可以跟`asyncio`模块一起工作,并提供了对`Future`对象的支持。Python 3.6中引入了`async`和`await`来定义异步执行的函数以及创建异步上下文,在Python 3.7中它们正式成为了关键字。下面的代码异步的从5个URL中获取页面并通过正则表达式的命名捕获组提取了网站的标题。 + + ```Python + import asyncio + import re + + import aiohttp + + PATTERN = re.compile(r'\(?P.*)\<\/title\>') + + + async def fetch_page(session, url): + async with session.get(url, ssl=False) as resp: + return await resp.text() + + + async def show_title(url): + async with aiohttp.ClientSession() as session: + html = await fetch_page(session, url) + print(PATTERN.search(html).group('title')) + + + def main(): + urls = ('https://www.python.org/', + 'https://git-scm.com/', + 'https://www.jd.com/', + 'https://www.taobao.com/', + 'https://www.douban.com/') + loop = asyncio.get_event_loop() + cos = [show_title(url) for url in urls] + loop.run_until_complete(asyncio.wait(cos)) + loop.close() + + + if __name__ == '__main__': + main() + ``` + + > **重点**:**异步I/O与多进程的比较**。 + > + > 当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,`asyncio`就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑`asyncio`,它很适合编写没有实时数据处理需求的Web应用服务器。 + + Python还有很多用于处理并行任务的三方库,例如:`joblib`、`PyMP`等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。 + + 要实现任务的异步化,可以使用名为`Celery`的三方库。`Celery`是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。 \ No newline at end of file diff --git "a/Day16-20/Python\350\257\255\350\250\200\350\277\233\351\230\266.md" "b/Day16-20/Python\350\257\255\350\250\200\350\277\233\351\230\266.md" deleted file mode 100644 index 5d5064629..000000000 --- "a/Day16-20/Python\350\257\255\350\250\200\350\277\233\351\230\266.md" +++ /dev/null @@ -1,2 +0,0 @@ -## Python进阶知识 - diff --git a/Day16-20/code/example01.py b/Day16-20/code/example01.py new file mode 100755 index 000000000..c8d806fd6 --- /dev/null +++ b/Day16-20/code/example01.py @@ -0,0 +1,69 @@ +""" +查找 - 顺序查找和二分查找 +算法:解决问题的方法(步骤) +评价一个算法的好坏主要有两个指标:渐近时间复杂度和渐近空间复杂度,通常一个算法很难做到时间复杂度和空间复杂度都很低(因为时间和空间是不可调和的矛盾) +表示渐近时间复杂度通常使用大O标记 +O(c):常量时间复杂度 - 哈希存储 / 布隆过滤器 +O(log_2 n):对数时间复杂度 - 折半查找 +O(n):线性时间复杂度 - 顺序查找 +O(n * log_2 n):- 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序) +O(n ** 2):平方时间复杂度 - 简单排序算法(冒泡排序、选择排序、插入排序) +O(n ** 3):立方时间复杂度 - Floyd算法 / 矩阵乘法运算 +也称为多项式时间复杂度 +O(2 ** n):几何级数时间复杂度 - 汉诺塔 +O(3 ** n):几何级数时间复杂度 +也称为指数时间复杂度 +O(n!):阶乘时间复杂度 - 旅行经销商问题 - NP +""" +from math import log2, factorial +from matplotlib import pyplot + +import numpy + + +def seq_search(items: list, elem) -> int: + """顺序查找""" + for index, item in enumerate(items): + if elem == item: + return index + return -1 + + +def bin_search(items, elem): + """二分查找""" + start, end = 0, len(items) - 1 + while start <= end: + mid = (start + end) // 2 + if elem > items[mid]: + start = mid + 1 + elif elem < items[mid]: + end = mid - 1 + else: + return mid + return -1 + + +def main(): + """主函数(程序入口)""" + num = 6 + styles = ['r-.', 'g-*', 'b-o', 'y-x', 'c-^', 'm-+', 'k-d'] + legends = ['对数', '线性', '线性对数', '平方', '立方', '几何级数', '阶乘'] + x_data = [x for x in range(1, num + 1)] + y_data1 = [log2(y) for y in range(1, num + 1)] + y_data2 = [y for y in range(1, num + 1)] + y_data3 = [y * log2(y) for y in range(1, num + 1)] + y_data4 = [y ** 2 for y in range(1, num + 1)] + y_data5 = [y ** 3 for y in range(1, num + 1)] + y_data6 = [3 ** y for y in range(1, num + 1)] + y_data7 = [factorial(y) for y in range(1, num + 1)] + y_datas = [y_data1, y_data2, y_data3, y_data4, y_data5, y_data6, y_data7] + for index, y_data in enumerate(y_datas): + pyplot.plot(x_data, y_data, styles[index]) + pyplot.legend(legends) + pyplot.xticks(numpy.arange(1, 7, step=1)) + pyplot.yticks(numpy.arange(0, 751, step=50)) + pyplot.show() + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example02.py b/Day16-20/code/example02.py new file mode 100755 index 000000000..f273fe28a --- /dev/null +++ b/Day16-20/code/example02.py @@ -0,0 +1,161 @@ +""" +排序 - 冒泡排序、选择排序、归并排序、快速排序 +冒泡排序 - O(n ** 2):两两比较,大的下沉 +35, 97, 12, 68, 55, 73, 81, 40 +35, 12, 68, 55, 73, 81, 40, [97] +12, 35, 55, 68, 73, 40, [81] +12, 35, 55, 68, 40, [73] +... +选择排序 - O(n ** 2):每次从剩下元素中选择最小 +----------------------------------------- +归并排序 - O(n * log_2 n) - 高级排序算法 +35, 97, 12, 68, 55, 73, 81, 40 +[35, 97, 12, 68], [55, 73, 81, 40] +[35, 97], [12, 68], [55, 73], [81, 40] +[35], [97], [12], [68], [55], [73], [81], [40] +[35, 97], [12, 68], [55, 73], [40, 81] +[12, 35, 68, 97], [40, 55, 73, 81] +[12, 35, 40, 55, 68, 73, 81, 97] +----------------------------------------- +快速排序 - 以枢轴为界将列表中的元素划分为两个部分,左边都比枢轴小,右边都比枢轴大 +35, 97, 12, 68, 55, 73, 81, 40 +35, 12, [40], 68, 55, 73, 81, 97 +[12], 35, [40], 68, 55, 73, 81, [97] +[12], 35, [40], 55, [68], 73, 81, [97] +[12], 35, [40], 55, [68], 73, [81], [97] +""" + + +class Person(object): + """人""" + + def __init__(self, name, age): + self.name = name + self.age = age + + # def __gt__(self, other): + # return self.name > other.name + + def __str__(self): + return f'{self.name}: {self.age}' + + def __repr__(self): + return self.__str__() + + +def select_sort(origin_items, comp=lambda x, y: x < y): + """简单选择排序""" + items = origin_items[:] + for i in range(len(items) - 1): + min_index = i + for j in range(i + 1, len(items)): + if comp(items[j], items[min_index]): + min_index = j + items[i], items[min_index] = items[min_index], items[i] + return items + + +# 函数的设计要尽量做到无副作用(不影响调用者) +# 9 1 2 3 4 5 6 7 8 +# 9 2 3 4 5 6 7 8 1 +# *前面的参数叫位置参数,传参时只需要对号入座即可 +# *后面的参数叫命名关键字参数,传参时必须给出参数名和参数值 +# *args - 可变参数 - 元组 +# **kwargs - 关键字参数 - 字典 +def bubble_sort(origin_items, *, comp=lambda x, y: x > y): + """冒泡排序""" + items = origin_items[:] + for i in range(1, len(items)): + swapped = False + for j in range(i - 1, len(items) - i): + if comp(items[j], items[j + 1]): + items[j], items[j + 1] = items[j + 1], items[j] + swapped = True + if swapped: + swapped = False + for j in range(len(items) - i - 1, i - 1, -1): + if comp(items[j - 1], items[j]): + items[j], items[j - 1] = items[j - 1], items[j] + swapped = True + if not swapped: + break + return items + + +def merge_sort(items, comp=lambda x, y: x <= y): + """归并排序""" + if len(items) < 2: + return items[:] + mid = len(items) // 2 + left = merge_sort(items[:mid], comp) + right = merge_sort(items[mid:], comp) + return merge(left, right, comp) + + +def merge(items1, items2, comp=lambda x, y: x <= y): + """合并(将两个有序列表合并成一个新的有序列表)""" + items = [] + index1, index2 = 0, 0 + while index1 < len(items1) and index2 < len(items2): + if comp(items1[index1], items2[index2]): + items.append(items1[index1]) + index1 += 1 + else: + items.append(items2[index2]) + index2 += 1 + items += items1[index1:] + items += items2[index2:] + return items + + +def quick_sort(origin_items, comp=lambda x, y: x <= y): + """快速排序""" + items = origin_items[:] + _quick_sort(items, 0, len(items) - 1, comp) + return items + + +def _quick_sort(items, start, end, comp): + """递归调用划分和排序""" + if start < end: + pos = _partition(items, start, end, comp) + _quick_sort(items, start, pos - 1, comp) + _quick_sort(items, pos + 1, end, comp) + + +def _partition(items, start, end, comp): + """划分""" + pivot = items[end] + i = start - 1 + for j in range(start, end): + if comp(items[j], pivot): + i += 1 + items[i], items[j] = items[j], items[i] + items[i + 1], items[end] = items[end], items[i + 1] + return i + 1 + + +def main(): + """主函数""" + items = [35, 97, 12, 68, 55, 73, 81, 40] + # print(bubble_sort(items)) + # print(select_sort(items)) + # print(merge_sort(items)) + print(quick_sort(items)) + items2 = [ + Person('Wang', 25), Person('Luo', 39), + Person('Zhang', 50), Person('He', 20) + ] + # print(bubble_sort(items2, comp=lambda p1, p2: p1.age > p2.age)) + # print(select_sort(items2, comp=lambda p1, p2: p1.name < p2.name)) + # print(merge_sort(items2, comp=lambda p1, p2: p1.age <= p2.age)) + print(quick_sort(items2, comp=lambda p1, p2: p1.age <= p2.age)) + items3 = ['apple', 'orange', 'watermelon', 'durian', 'pear'] + # print(bubble_sort(items3)) + # print(bubble_sort(items3, comp=lambda x, y: len(x) > len(y))) + # print(merge_sort(items3)) + print(merge_sort(items3)) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example03.py b/Day16-20/code/example03.py new file mode 100755 index 000000000..6f70cdc56 --- /dev/null +++ b/Day16-20/code/example03.py @@ -0,0 +1,76 @@ +""" +函数递归调用 - 函数直接或者间接的调用了自身 +1. 收敛条件 +2. 递归公式 + +n! = n * (n-1)! +f(n) = f(n-1) + f(n-2) +1 1 2 3 5 8 13 21 34 55 ... +""" +from contextlib import contextmanager +from time import perf_counter + + +def fac(num): + """求阶乘""" + assert num >= 0 + if num in (0, 1): + return 1 + return num * fac(num - 1) + + +def fib2(num): + """普通函数""" + a, b = 1, 1 + for _ in range(num - 1): + a, b = b, a + b + return a + + +def fib3(num): + """生成器""" + a, b = 0, 1 + for _ in range(num): + a, b = b, a + b + yield a + + +# 动态规划 - 保存可能进行重复运算的中间结果(空间换时间) +def fib(num, results={}): + """斐波拉切数""" + assert num > 0 + if num in (1, 2): + return 1 + try: + return results[num] + except KeyError: + results[num] = fib(num - 1) + fib(num - 2) + return results[num] + + +@contextmanager +def timer(): + try: + start = perf_counter() + yield + finally: + end = perf_counter() + print(f'{end - start}秒') + + +def main(): + """主函数""" + # for val in fib3(20): + # print(val) + # gen = fib3(20) + # for _ in range(10): + # print(next(gen)) + for num in range(1, 121): + with timer(): + print(f'{num}: {fib(num)}') + # print(fac(5)) + # print(fac(-5)) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example04.py b/Day16-20/code/example04.py new file mode 100755 index 000000000..172ffe88c --- /dev/null +++ b/Day16-20/code/example04.py @@ -0,0 +1,43 @@ +""" +贪婪法:在对问题求解时,总是做出在当前看来是最好的选择, +不追求最优解,快速找到满意解。 +""" +class Thing(object): + """物品""" + + def __init__(self, name, price, weight): + self.name = name + self.price = price + self.weight = weight + + @property + def value(self): + """价格重量比""" + return self.price / self.weight + + +def input_thing(): + """输入物品信息""" + name_str, price_str, weight_str = input().split() + return name_str, int(price_str), int(weight_str) + + +def main(): + """主函数""" + max_weight, num_of_things = map(int, input().split()) + all_things = [] + for _ in range(num_of_things): + all_things.append(Thing(*input_thing())) + all_things.sort(key=lambda x: x.value, reverse=True) + total_weight = 0 + total_price = 0 + for thing in all_things: + if total_weight + thing.weight <= max_weight: + print(f'小偷拿走了{thing.name}') + total_weight += thing.weight + total_price += thing.price + print(f'总价值: {total_price}美元') + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example05.py b/Day16-20/code/example05.py new file mode 100755 index 000000000..1b8055e83 --- /dev/null +++ b/Day16-20/code/example05.py @@ -0,0 +1,49 @@ +""" +递归回溯法:叫称为试探法,按选优条件向前搜索,当搜索到某一步, +发现原先选择并不优或达不到目标时,就退回一步重新选择。 +经典问题:骑士巡逻 +""" +import os +import sys +import time + +SIZE = 5 +total = 0 + + +def print_board(board): + # os.system('clear') + for row in board: + for col in row: + print(str(col).center(4), end='') + print() + + +def patrol(board, row, col, step=1): + if row >= 0 and row < SIZE and \ + col >= 0 and col < SIZE and \ + board[row][col] == 0: + board[row][col] = step + if step == SIZE * SIZE: + global total + total += 1 + print(f'第{total}种走法: ') + print_board(board) + patrol(board, row - 2, col - 1, step + 1) + patrol(board, row - 1, col - 2, step + 1) + patrol(board, row + 1, col - 2, step + 1) + patrol(board, row + 2, col - 1, step + 1) + patrol(board, row + 2, col + 1, step + 1) + patrol(board, row + 1, col + 2, step + 1) + patrol(board, row - 1, col + 2, step + 1) + patrol(board, row - 2, col + 1, step + 1) + board[row][col] = 0 + + +def main(): + board = [[0] * SIZE for _ in range(SIZE)] + patrol(board, SIZE - 1, SIZE - 1) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example06.py b/Day16-20/code/example06.py new file mode 100755 index 000000000..e03dfd48f --- /dev/null +++ b/Day16-20/code/example06.py @@ -0,0 +1,50 @@ +""" +编码和解码 - BASE64 +0-9A-Za-z+/ +1100 0101 1001 0011 0111 0110 +00110001 00011001 00001101 00110110 +base64 +b64encode / b64decode +------------------------------------- +序列化和反序列化 +序列化 - 将对象变成字节序列(bytes)或者字符序列(str) - 串行化/腌咸菜 +反序列化 - 把字节序列或者字符序列还原成对象 +Python标准库对序列化的支持: +json - 字符形式的序列化 +pickle - 字节形式的序列化 +dumps / loads +""" +import base64 +import json +import redis + +from example02 import Person + + +class PersonJsonEncoder(json.JSONEncoder): + + def default(self, o): + return o.__dict__ + + +def main(): + cli = redis.StrictRedis(host='120.77.222.217', port=6379, + password='123123') + data = base64.b64decode(cli.get('guido')) + with open('guido2.jpg', 'wb') as file_stream: + file_stream.write(data) + # with open('guido.jpg', 'rb') as file_stream: + # result = base64.b64encode(file_stream.read()) + # cli.set('guido', result) + # persons = [ + # Person('骆昊', 39), Person('王大锤', 18), + # Person('白元芳', 25), Person('狄仁杰', 37) + # ] + # persons = json.loads(cli.get('persons')) + # print(persons) + # cli.set('persons', json.dumps(persons, cls=PersonJsonEncoder)) + + +if __name__ == '__main__': + main() + diff --git a/Day16-20/code/example07.py b/Day16-20/code/example07.py new file mode 100755 index 000000000..20a91767b --- /dev/null +++ b/Day16-20/code/example07.py @@ -0,0 +1,52 @@ +""" +哈希摘要 - 数字签名/指纹 - 单向哈希函数(没有反函数不可逆) +应用领域: +1. 数据库中的用户敏感信息保存成哈希摘要 +2. 给数据生成签名验证数据没有被恶意篡改 +3. 云存储服务的秒传功能(去重功能) +""" + + +class StreamHasher(): + """摘要生成器""" + + def __init__(self, algorithm='md5', size=4096): + """初始化方法 + @params: + algorithm - 哈希摘要算法 + size - 每次读取数据的大小 + """ + self.size = size + cls = getattr(__import__('hashlib'), algorithm.lower()) + self.hasher = cls() + + + def digest(self, file_stream): + """生成十六进制的摘要字符串""" + # data = file_stream.read(self.size) + # while data: + # self.hasher.update(data) + # data = file_stream.read(self.size) + for data in iter(lambda: file_stream.read(self.size), b''): + self.hasher.update(data) + return self.hasher.hexdigest() + + def __call__(self, file_stream): + return self.digest(file_stream) + + +def main(): + """主函数""" + hasher1 = StreamHasher() + hasher2 = StreamHasher('sha1') + hasher3 = StreamHasher('sha256') + with open('Python-3.7.2.tar.xz', 'rb') as file_stream: + print(hasher1.digest(file_stream)) + file_stream.seek(0, 0) + print(hasher2.digest(file_stream)) + file_stream.seek(0, 0) + print(hasher3(file_stream)) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example08.py b/Day16-20/code/example08.py new file mode 100755 index 000000000..714d4322e --- /dev/null +++ b/Day16-20/code/example08.py @@ -0,0 +1,53 @@ +""" +加密和解密 +对称加密 - 加密和解密是同一个密钥 - DES / AES +非对称加密 - 加密和解密是不同的密钥 - RSA +pip install pycrypto +""" +import base64 + +from hashlib import md5 + +from Crypto.Cipher import AES +from Crypto import Random +from Crypto.PublicKey import RSA + +# # AES加密的密钥(长度32个字节) +# key = md5(b'1qaz2wsx').hexdigest() +# # AES加密的初始向量(随机生成) +# iv = Random.new().read(AES.block_size) + + +def main(): + """主函数""" + # 生成密钥对 + key_pair = RSA.generate(1024) + # 导入公钥 + pub_key = RSA.importKey(key_pair.publickey().exportKey()) + # 导入私钥 + pri_key = RSA.importKey(key_pair.exportKey()) + message1 = 'hello, world!' + # 加密数据 + data = pub_key.encrypt(message1.encode(), None) + # 对加密数据进行BASE64编码 + message2 = base64.b64encode(data[0]) + print(message2) + # 对加密数据进行BASE64解码 + data = base64.b64decode(message2) + # 解密数据 + message3 = pri_key.decrypt(data) + print(message3.decode()) + # # AES - 对称加密 + # str1 = '我爱你们!' + # cipher = AES.new(key, AES.MODE_CFB, iv) + # # 加密 + # str2 = cipher.encrypt(str1) + # print(str2) + # # 解密 + # cipher = AES.new(key, AES.MODE_CFB, iv) + # str3 = cipher.decrypt(str2) + # print(str3.decode()) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example09.py b/Day16-20/code/example09.py new file mode 100755 index 000000000..c2a488ff1 --- /dev/null +++ b/Day16-20/code/example09.py @@ -0,0 +1,66 @@ +""" +装饰器 - 装饰器中放置的通常都是横切关注(cross-concern)功能 +所谓横切关注功能就是很多地方都会用到但跟正常业务又逻辑没有必然联系的功能 +装饰器实际上是实现了设计模式中的代理模式 - AOP(面向切面编程) +""" +from functools import wraps +from random import randint +from time import time, sleep + +import pymysql + + +def record(output): + + def decorate(func): + + @wraps(func) + def wrapper(*args, **kwargs): + start = time() + ret_value = func(*args, **kwargs) + output(func.__name__, time() - start) + return ret_value + + return wrapper + + return decorate + + +def output_to_console(fname, duration): + print('%s: %.3f秒' % (fname, duration)) + + +def output_to_file(fname, duration): + with open('log.txt', 'a') as file_stream: + file_stream.write('%s: %.3f秒\n' % (fname, duration)) + + +def output_to_db(fname, duration): + con = pymysql.connect(host='localhost', port=3306, + database='test', charset='utf8', + user='root', password='123456', + autocommit=True) + try: + with con.cursor() as cursor: + cursor.execute('insert into tb_record values (default, %s, %s)', + (fname, '%.3f' % duration)) + finally: + con.close() + + +@record(output_to_console) +def random_delay(min, max): + sleep(randint(min, max)) + + +def main(): + for _ in range(3): + # print(random_delay.__name__) + random_delay(3, 5) + # for _ in range(3): + # # 取消掉装饰器 + # random_delay.__wrapped__(3, 5) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example10.py b/Day16-20/code/example10.py new file mode 100755 index 000000000..d86335142 --- /dev/null +++ b/Day16-20/code/example10.py @@ -0,0 +1,48 @@ +""" +装饰类的装饰器 - 单例模式 - 一个类只能创建出唯一的对象 +上下文语法: +__enter__ / __exit__ +""" +import threading + +from functools import wraps + + +def singleton(cls): + """单例装饰器""" + instances = {} + lock = threading.Lock() + + @wraps(cls) + def wrapper(*args, **kwargs): + if cls not in instances: + with lock: + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return wrapper + + +@singleton +class President(): + + def __init__(self, name, country): + self.name = name + self.country = country + + def __str__(self): + return f'{self.country}: {self.name}' + + +def main(): + print(President.__name__) + p1 = President('特朗普', '美国') + p2 = President('奥巴马', '美国') + print(p1 == p2) + print(p1) + print(p2) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example11.py b/Day16-20/code/example11.py new file mode 100755 index 000000000..af7ef19e8 --- /dev/null +++ b/Day16-20/code/example11.py @@ -0,0 +1,23 @@ +""" +变量的作用域以及Python搜索变量的顺序 +LEGB: Local --> Embedded --> Global --> Built-in +global - 声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域) +nonlocal - 声明使用嵌套作用域的变量(如果嵌套作用域没有对应的变量直接报错) +""" +x = 100 + + +def foo(): + global x + x = 200 + + def bar(): + x = 300 + print(x) + + bar() + print(x) + + +foo() +print(x) diff --git a/Day16-20/code/example12.py b/Day16-20/code/example12.py new file mode 100755 index 000000000..57340d40c --- /dev/null +++ b/Day16-20/code/example12.py @@ -0,0 +1,65 @@ +""" +面向对象的三大支柱:封装、继承、多态 +面向对象的设计原则:SOLID原则 +面向对象的设计模式:GoF设计模式(单例、工厂、代理、策略、迭代器) +月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成 +""" +from abc import ABCMeta, abstractmethod + + +class Employee(metaclass=ABCMeta): + """员工(抽象类)""" + + def __init__(self, name): + self.name = name + + @abstractmethod + def get_salary(self): + """结算月薪(抽象方法)""" + pass + + +class Manager(Employee): + """部门经理""" + + def get_salary(self): + return 15000.0 + + +class Programmer(Employee): + """程序员""" + + def __init__(self, name, working_hour=0): + self.working_hour = working_hour + super().__init__(name) + + def get_salary(self): + return 200.0 * self.working_hour + + +class Salesman(Employee): + """销售员""" + + def __init__(self, name, sales=0.0): + self.sales = sales + super().__init__(name) + + def get_salary(self): + return 1800.0 + self.sales * 0.05 + + +class EmployeeFactory(): + """创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)""" + + @staticmethod + def create(emp_type, *args, **kwargs): + """创建员工""" + emp_type = emp_type.upper() + emp = None + if emp_type == 'M': + emp = Manager(*args, **kwargs) + elif emp_type == 'P': + emp = Programmer(*args, **kwargs) + elif emp_type == 'S': + emp = Salesman(*args, **kwargs) + return emp diff --git a/Day16-20/code/example13.py b/Day16-20/code/example13.py new file mode 100755 index 000000000..e8fcd12b0 --- /dev/null +++ b/Day16-20/code/example13.py @@ -0,0 +1,17 @@ +from example12 import EmployeeFactory + + +def main(): + """主函数""" + emps = [ + EmployeeFactory.create('M', '曹操'), + EmployeeFactory.create('P', '荀彧', 120), + EmployeeFactory.create('P', '郭嘉', 85), + EmployeeFactory.create('S', '典韦', 123000), + ] + for emp in emps: + print('%s: %.2f元' % (emp.name, emp.get_salary())) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example14.py b/Day16-20/code/example14.py new file mode 100755 index 000000000..216aafd94 --- /dev/null +++ b/Day16-20/code/example14.py @@ -0,0 +1,97 @@ +""" +面向对象 +枚举 - 一个变量的值只有有限个选择,最适合的类型就是枚举 +通过枚举我们可以定义符号常量,符号常量优于字面常量 +""" +from enum import Enum, unique + +import random + + +@unique +class Suite(Enum): + """花色(枚举)""" + SPADE, HEART, CLUB, DIAMOND = range(4) + + def __lt__(self, other): + return self.value < other.value + + +class Card(): + """牌""" + + def __init__(self, suite, face): + self.suite = suite + self.face = face + + def __repr__(self): + return self.__str__() + + def __str__(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]}' + + +class Poker(): + """扑克""" + + def __init__(self): + self.index = 0 + self.cards = [Card(suite, face) + for suite in Suite + for face in range(1, 14)] + + def shuffle(self): + """洗牌""" + self.index = 0 + random.shuffle(self.cards) + + def deal(self): + """发牌""" + card = self.cards[self.index] + self.index += 1 + return card + + @property + def has_more(self): + """是否有更多的牌""" + return self.index < len(self.cards) + + +class Player(): + """玩家""" + + def __init__(self, name): + self.name = name + self.cards = [] + + def get_card(self, card): + """摸牌""" + self.cards.append(card) + + def arrange(self): + """整理手上的牌""" + self.cards.sort(key=lambda card: (card.suite, card.face)) + + +def main(): + """主函数""" + poker = Poker() + poker.shuffle() + players = [ + Player('东邪'), Player('西毒'), + Player('南帝'), Player('北丐') + ] + while poker.has_more: + for player in players: + player.get_card(poker.deal()) + for player in players: + player.arrange() + print(player.name, end=': ') + print(player.cards) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example15.py b/Day16-20/code/example15.py new file mode 100755 index 000000000..39d0e2034 --- /dev/null +++ b/Day16-20/code/example15.py @@ -0,0 +1,75 @@ +""" +迭代器 - __iter__ / __next__ +itertools - 生成可迭代序列的工具模块 +""" +import itertools + +from math import sqrt + + +def is_prime(num): + """判断素数""" + for factor in range(2, int(sqrt(num)) + 1): + if num % factor == 0: + return False + return True + + +class PrimeIter(object): + """素数迭代器""" + + def __init__(self, min_value, max_value): + assert 2 <= min_value <= max_value + self.min_value = min_value - 1 + self.max_value = max_value + + def __iter__(self): + return self + + def __next__(self): + self.min_value += 1 + while self.min_value <= self.max_value: + if is_prime(self.min_value): + return self.min_value + self.min_value += 1 + raise StopIteration() + + +class FibIter(object): + """斐波那契数迭代器""" + + def __init__(self, num): + self.num = num + self.a, self.b = 0, 1 + self.idx = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.idx < self.num: + self.a, self.b = self.b, self.a + self.b + self.idx += 1 + return self.a + raise StopIteration() + + +def main(): + # for val in itertools.permutations('ABCD'): + # print(val) + # for val in itertools.combinations('ABCDE', 3): + # print(val) + # for val in itertools.product('黑红梅方', range(1, 14)): + # print(val) + # fib_iter = FibIter(20) + # print('===>', next(fib_iter)) + # print('===>', next(fib_iter)) + # for val in fib_iter: + # print(val) + prime_iter = PrimeIter(2, 100000) + for val in prime_iter: + print(val) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example16.py b/Day16-20/code/example16.py new file mode 100755 index 000000000..bb8369420 --- /dev/null +++ b/Day16-20/code/example16.py @@ -0,0 +1,66 @@ +""" +魔术方法 +如果要把自定义对象放到set或者用作dict的键 +那么必须要重写__hash__和__eq__两个魔术方法 +前者用来计算对象的哈希码,后者用来判断两个对象是否相同 +哈希码不同的对象一定是不同的对象,但哈希码相同未必是相同的对象(哈希码冲撞) +所以在哈希码相同的时候还要通过__eq__来判定对象是否相同 +""" + + +class Student(): + __slots__ = ('stuid', 'name', 'gender') + + def __init__(self, stuid, name): + self.stuid = stuid + self.name = name + + def __hash__(self): + return hash(self.stuid) + hash(self.name) + + def __eq__(self, other): + return self.stuid == other.stuid and \ + self.name == other.name + + def __str__(self): + return f'{self.stuid}: {self.name}' + + def __repr__(self): + return self.__str__() + + +class School(): + + def __init__(self, name): + self.name = name + self.students = {} + + def __setitem__(self, key, student): + self.students[key] = student + + def __getitem__(self, key): + return self.students[key] + + +def main(): + # students = set() + # students.add(Student(1001, '王大锤')) + # students.add(Student(1001, '王大锤')) + # students.add(Student(1001, '白元芳')) + # print(len(students)) + # print(students) + stu = Student(1234, '骆昊') + stu.gender = 'Male' + # stu.birth = '1980-11-28' + print(stu.name, stu.birth) + school = School('千锋教育') + school[1001] = Student(1001, '王大锤') + school[1002] = Student(1002, '白元芳') + school[1003] = Student(1003, '白洁') + print(school[1002]) + print(school[1003]) + + +if __name__ == '__main__': + main() + diff --git a/Day16-20/code/example17.py b/Day16-20/code/example17.py new file mode 100755 index 000000000..4918b74f3 --- /dev/null +++ b/Day16-20/code/example17.py @@ -0,0 +1,54 @@ +""" +多重继承 - 一个类有两个或者两个以上的父类 +MRO - 方法解析顺序 - Method Resolution Order +当出现菱形继承(钻石继承)的时候,子类到底继承哪个父类的方法 +Python 2.x - 深度优先搜索 +Python 3.x - C3算法 - 类似于广度优先搜索 +""" +class A(): + + def say_hello(self): + print('Hello, A') + + +class B(A): + pass + + +class C(A): + + def say_hello(self): + print('Hello, C') + + +class D(B, C): + pass + + +class SetOnceMappingMixin(): + """自定义混入类""" + __slots__ = () + + def __setitem__(self, key, value): + if key in self: + raise KeyError(str(key) + ' already set') + return super().__setitem__(key, value) + + +class SetOnceDict(SetOnceMappingMixin, dict): + """自定义字典""" + pass + + +def main(): + print(D.mro()) + # print(D.__mro__) + D().say_hello() + print(SetOnceDict.__mro__) + my_dict= SetOnceDict() + my_dict['username'] = 'jackfrued' + my_dict['username'] = 'hellokitty' + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example18.py b/Day16-20/code/example18.py new file mode 100755 index 000000000..f016a6b24 --- /dev/null +++ b/Day16-20/code/example18.py @@ -0,0 +1,47 @@ +""" +元 - meta +元数据 - 描述数据的数据 - metadata +元类 - 描述类的类 - metaclass - 继承自type +""" +import threading + + +class SingletonMeta(type): + """自定义元类""" + + def __init__(cls, *args, **kwargs): + cls.__instance = None + cls.lock = threading.Lock() + super().__init__(*args, **kwargs) + + def __call__(cls, *args, **kwargs): + if cls.__instance is None: + with cls.lock: + if cls.__instance is None: + cls.__instance = super().__call__(*args, **kwargs) + return cls.__instance + + +class President(metaclass=SingletonMeta): + """总统(单例类)""" + + def __init__(self, name, country): + self.name = name + self.country = country + + def __str__(self): + return f'{self.country}: {self.name}' + + +def main(): + """主函数""" + p1 = President('特朗普', '美国') + p2 = President('奥巴马', '美国') + p3 = President.__call__('克林顿', '美国') + print(p1 == p2) + print(p1 == p3) + print(p1, p2, p3, sep='\n') + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example19.py b/Day16-20/code/example19.py new file mode 100755 index 000000000..f1deea051 --- /dev/null +++ b/Day16-20/code/example19.py @@ -0,0 +1,94 @@ +""" +扩展性系统性能 +- 垂直扩展 - 增加单节点处理能力 +- 水平扩展 - 将单节点变成多节点(读写分离/分布式集群) +并发编程 - 加速程序执行 / 改善用户体验 +耗时间的任务都尽可能独立的执行,不要阻塞代码的其他部分 +- 多线程 +1. 创建Thread对象指定target和args属性并通过start方法启动线程 +2. 继承Thread类并重写run方法来定义线程执行的任务 +3. 创建线程池对象ThreadPoolExecutor并通过submit来提交要执行的任务 +第3种方式可以通过Future对象的result方法在将来获得线程的执行结果 +也可以通过done方法判定线程是否执行结束 +- 多进程 +- 异步I/O +""" +import glob +import os +import time + +from concurrent.futures import ThreadPoolExecutor +from threading import Thread + +from PIL import Image + + +# class ThumbnailThread(Thread): + +# def __init__(self, infile): +# self.infile = infile +# super().__init__() + +# def run(self): +# file, ext = os.path.splitext(self.infile) +# filename = file[file.rfind('/') + 1:] +# for size in (32, 64, 128): +# outfile = f'thumbnails/{filename}_{size}_{size}.png' +# image = Image.open(self.infile) +# image.thumbnail((size, size)) +# image.save(outfile, format='PNG') + + +def gen_thumbnail(infile): + file, ext = os.path.splitext(infile) + filename = file[file.rfind('/') + 1:] + for size in (32, 64, 128): + outfile = f'thumbnails/{filename}_{size}_{size}.png' + image = Image.open(infile) + image.thumbnail((size, size)) + image.save(outfile, format='PNG') + + +# def main(): +# start = time.time() +# threads = [] +# for infile in glob.glob('images/*'): +# # t = Thread(target=gen_thumbnail, args=(infile, )) +# t = ThumbnailThread(infile) +# t.start() +# threads.append(t) +# for t in threads: +# t.join() +# end = time.time() +# print(f'耗时: {end - start}秒') + + +def main(): + pool = ThreadPoolExecutor(max_workers=30) + futures = [] + start = time.time() + for infile in glob.glob('images/*'): + # submit方法是非阻塞式的方法 + # 即便工作线程数已经用完,submit方法也会接受提交的任务 + future = pool.submit(gen_thumbnail, infile) + futures.append(future) + for future in futures: + # result方法是一个阻塞式的方法 如果线程还没有结束 + # 暂时取不到线程的执行结果 代码就会在此处阻塞 + future.result() + end = time.time() + print(f'耗时: {end - start}秒') + # shutdown也是非阻塞式的方法 但是如果已经提交的任务还没有执行完 + # 线程池是不会停止工作的 shutdown之后再提交任务就不会执行而且会产生异常 + pool.shutdown() + + +if __name__ == '__main__': + main() + + + + + + + diff --git a/Day16-20/code/example20.py b/Day16-20/code/example20.py new file mode 100755 index 000000000..06de05d4b --- /dev/null +++ b/Day16-20/code/example20.py @@ -0,0 +1,27 @@ +""" +线程间通信(共享数据)非常简单因为可以共享同一个进程的内存 +进程间通信(共享数据)比较麻烦因为操作系统会保护分配给进程的内存 +要实现多进程间的通信通常可以用系统管道、套接字、三方服务来实现 +multiprocessing.Queue +守护线程 - daemon thread +守护进程 - firewalld / httpd / mysqld +在系统停机的时候不保留的进程 - 不会因为进程还没有执行结束而阻碍系统停止 +""" +from threading import Thread +from time import sleep + + +def output(content): + while True: + print(content, end='') + + +def main(): + Thread(target=output, args=('Ping', ), daemon=True).start() + Thread(target=output, args=('Pong', ), daemon=True).start() + sleep(5) + print('bye!') + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example21.py b/Day16-20/code/example21.py new file mode 100755 index 000000000..002b7e86a --- /dev/null +++ b/Day16-20/code/example21.py @@ -0,0 +1,66 @@ +""" +多个线程竞争一个资源 - 保护临界资源 - 锁(Lock/RLock) +多个线程竞争多个资源(线程数>资源数) - 信号量(Semaphore) +多个线程的调度 - 暂停线程执行/唤醒等待中的线程 - Condition +""" +from concurrent.futures import ThreadPoolExecutor +from random import randint +from time import sleep + +import threading + + +class Account(): + """银行账户""" + + def __init__(self, balance=0): + self.balance = balance + lock = threading.Lock() + self.condition = threading.Condition(lock) + + def withdraw(self, money): + """取钱""" + with self.condition: + while money > self.balance: + self.condition.wait() + new_balance = self.balance - money + sleep(0.001) + self.balance = new_balance + + def deposit(self, money): + """存钱""" + with self.condition: + new_balance = self.balance + money + sleep(0.001) + self.balance = new_balance + self.condition.notify_all() + + +def add_money(account): + while True: + money = randint(5, 10) + account.deposit(money) + print(threading.current_thread().name, + ':', money, '====>', account.balance) + sleep(0.5) + + +def sub_money(account): + while True: + money = randint(10, 30) + account.withdraw(money) + print(threading.current_thread().name, + ':', money, '<====', account.balance) + sleep(1) + + +def main(): + account = Account() + with ThreadPoolExecutor(max_workers=10) as pool: + for _ in range(5): + pool.submit(add_money, account) + pool.submit(sub_money, account) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example22.py b/Day16-20/code/example22.py new file mode 100755 index 000000000..315679e30 --- /dev/null +++ b/Day16-20/code/example22.py @@ -0,0 +1,50 @@ +""" +多进程和进程池的使用 +多线程因为GIL的存在不能够发挥CPU的多核特性 +对于计算密集型任务应该考虑使用多进程 +time python3 example22.py +real 0m11.512s +user 0m39.319s +sys 0m0.169s +""" +import concurrent.futures +import math + +PRIMES = [ + 1116281, + 1297337, + 104395303, + 472882027, + 533000389, + 817504243, + 982451653, + 112272535095293, + 112582705942171, + 112272535095293, + 115280095190773, + 115797848077099, + 1099726899285419 +] * 5 + + +def is_prime(n): + """判断素数""" + if n % 2 == 0: + return False + + sqrt_n = int(math.floor(math.sqrt(n))) + for i in range(3, sqrt_n + 1, 2): + if n % i == 0: + return False + return True + + +def main(): + """主函数""" + with concurrent.futures.ProcessPoolExecutor() as executor: + for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)): + print('%d is prime: %s' % (number, prime)) + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example23.py b/Day16-20/code/example23.py new file mode 100755 index 000000000..51c539c9a --- /dev/null +++ b/Day16-20/code/example23.py @@ -0,0 +1,47 @@ +""" +协程(coroutine)- 可以在需要时进行切换的相互协作的子程序 +""" +import asyncio + +from example15 import is_prime + + +def num_generator(m, n): + """指定范围的数字生成器""" + yield from range(m, n + 1) + + +async def prime_filter(m, n): + """素数过滤器""" + primes = [] + for i in num_generator(m, n): + if is_prime(i): + print('Prime =>', i) + primes.append(i) + + await asyncio.sleep(0.001) + return tuple(primes) + + +async def square_mapper(m, n): + """平方映射器""" + squares = [] + for i in num_generator(m, n): + print('Square =>', i * i) + squares.append(i * i) + + await asyncio.sleep(0.001) + return squares + + +def main(): + """主函数""" + loop = asyncio.get_event_loop() + future = asyncio.gather(prime_filter(2, 100), square_mapper(1, 100)) + future.add_done_callback(lambda x: print(x.result())) + loop.run_until_complete(future) + loop.close() + + +if __name__ == '__main__': + main() diff --git a/Day16-20/code/example24.py b/Day16-20/code/example24.py new file mode 100755 index 000000000..2f9666ea4 --- /dev/null +++ b/Day16-20/code/example24.py @@ -0,0 +1,37 @@ +""" +aiohttp - 异步HTTP网络访问 +异步I/O(异步编程)- 只使用一个线程(单线程)来处理用户请求 +用户请求一旦被接纳,剩下的都是I/O操作,通过多路I/O复用也可以达到并发的效果 +这种做法与多线程相比可以让CPU利用率更高,因为没有线程切换的开销 +Redis/Node.js - 单线程 + 异步I/O +Celery - 将要执行的耗时间的任务异步化处理 +异步I/O事件循环 - uvloop +""" +import asyncio +import re + +import aiohttp + + +async def fetch(session, url): + async with session.get(url, ssl=False) as resp: + return await resp.text() + + +async def main(): + pattern = re.compile(r'\<title\>(?P<title>.*)\<\/title\>') + urls = ('https://www.python.org/', + 'https://git-scm.com/', + 'https://www.jd.com/', + 'https://www.taobao.com/', + 'https://www.douban.com/') + async with aiohttp.ClientSession() as session: + for url in urls: + html = await fetch(session, url) + print(pattern.search(html).group('title')) + + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + loop.close() diff --git a/Day16-20/code/test_example01.py b/Day16-20/code/test_example01.py new file mode 100755 index 000000000..e992c4e59 --- /dev/null +++ b/Day16-20/code/test_example01.py @@ -0,0 +1,51 @@ +""" +单元测试 - 针对程序中最小的功能模块(函数和方法)的测试 +测试方法: +- 白盒测试:程序自己写的测试 +- 黑盒测试:测试人员或QA,不知道代码实现细节,只关注功能 +编写Python单元测试 - 定义类继承TestCase,写测试方法(test_开头) +执行单元测试: +- unittest.main() +- python3 -m unittest test_example01.py +第三方库 - nose2 / pytest +pip install pytest pytest-cov +pytest -v --cov +------------------------------ +pip install nose2 cov-core +nose2 -v -C +""" +from unittest import TestCase + +from example01 import seq_search, bin_search + + +class TestExample01(TestCase): + """测试查找函数的测试用例""" + + # 执行每个测试函数之前要执行的方法 + def setUp(self): + self.data1 = [35, 97, 12, 68, 55, 73, 81, 40] + self.data2 = [12, 35, 40, 55, 68, 73, 81, 97] + + # 执行每个测试函数之后要执行的方法 + def tearDown(self): + pass + + def test_seq_search(self): + """测试顺序查找""" + self.assertEqual(0, seq_search(self.data1, 35)) + self.assertEqual(2, seq_search(self.data1, 12)) + self.assertEqual(6, seq_search(self.data1, 81)) + self.assertEqual(7, seq_search(self.data1, 40)) + self.assertEqual(-1, seq_search(self.data1, 99)) + self.assertEqual(-1, seq_search(self.data1, 7)) + + def test_bin_search(self): + """测试二分查找""" + self.assertEqual(1, bin_search(self.data2, 35)) + self.assertEqual(0, bin_search(self.data2, 12)) + self.assertEqual(6, bin_search(self.data2, 81)) + self.assertEqual(2, bin_search(self.data2, 40)) + self.assertEqual(7, bin_search(self.data2, 97)) + self.assertEqual(-1, bin_search(self.data2, 7)) + self.assertEqual(-1, bin_search(self.data2, 99)) diff --git a/Day16-20/code/test_example02.py b/Day16-20/code/test_example02.py new file mode 100755 index 000000000..09afa0a81 --- /dev/null +++ b/Day16-20/code/test_example02.py @@ -0,0 +1,24 @@ +from unittest import TestCase + +from example02 import select_sort, merge + + +class TestExample02(TestCase): + """测试排序函数的测试用例""" + + def setUp(self): + self.data1 = [35, 97, 12, 68, 55, 73, 81, 40] + self.items1 = [12, 35, 68, 97] + self.items2 = [40, 55, 73, 81] + + def test_merge(self): + items = merge(self.items1, self.items2) + for i in range(len(items) - 1): + self.assertLessEqual(items[i], items[i + 1]) + + def test_select_sort(self): + """测试顺序查找""" + items = select_sort(self.data1) + for i in range(len(items) - 1): + self.assertLessEqual(items[i], items[i + 1]) + \ No newline at end of file diff --git a/Day16-20/res/algorithm_complexity_1.png b/Day16-20/res/algorithm_complexity_1.png new file mode 100755 index 000000000..2d57aed9c Binary files /dev/null and b/Day16-20/res/algorithm_complexity_1.png differ diff --git a/Day16-20/res/algorithm_complexity_2.png b/Day16-20/res/algorithm_complexity_2.png new file mode 100755 index 000000000..0ff66f3f2 Binary files /dev/null and b/Day16-20/res/algorithm_complexity_2.png differ diff --git "a/Day21-30/21-30.Web\345\211\215\347\253\257\346\246\202\350\277\260.md" "b/Day21-30/21-30.Web\345\211\215\347\253\257\346\246\202\350\277\260.md" new file mode 100755 index 000000000..6d6fa23ce --- /dev/null +++ "b/Day21-30/21-30.Web\345\211\215\347\253\257\346\246\202\350\277\260.md" @@ -0,0 +1,912 @@ +## Web前端概述 + +> **说明**:本文使用的部分插图来自*Jon Duckett*先生的*[HTML and CSS: Design and Build Websites](https://www.amazon.cn/dp/1118008189/ref=sr_1_5?__mk_zh_CN=%E4%BA%9A%E9%A9%AC%E9%80%8A%E7%BD%91%E7%AB%99&keywords=html+%26+css&qid=1554609325&s=gateway&sr=8-5)*一书,这是一本非常棒的前端入门书,有兴趣的读者可以在亚马逊或者其他网站上找到该书的购买链接。 + +HTML 是用来描述网页的一种语言,全称是 Hyper-Text Markup Language,即超文本标记语言。我们浏览网页时看到的文字、按钮、图片、视频等元素,它们都是通过 HTML 书写并通过浏览器来呈现的。 + +### HTML简史 + +1. 1991年10月:一个非正式CERN([欧洲核子研究中心](https://zh.wikipedia.org/wiki/%E6%AD%90%E6%B4%B2%E6%A0%B8%E5%AD%90%E7%A0%94%E7%A9%B6%E7%B5%84%E7%B9%94))文件首次公开18个HTML标签,这个文件的作者是物理学家[蒂姆·伯纳斯-李](https://zh.wikipedia.org/wiki/%E8%92%82%E5%A7%86%C2%B7%E4%BC%AF%E7%BA%B3%E6%96%AF-%E6%9D%8E),因此他是[万维网](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91)的发明者,也是[万维网联盟](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91%E8%81%94%E7%9B%9F)的主席。 +2. 1995年11月:HTML 2.0标准发布(RFC 1866)。 +3. 1997年1月:HTML 3.2作为[W3C](https://zh.wikipedia.org/wiki/W3C)推荐标准发布。 +4. 1997年12月:HTML 4.0作为W3C推荐标准发布。 +5. 1999年12月:HTML4.01作为W3C推荐标准发布。 +6. 2008年1月:HTML5由W3C作为工作草案发布。 +7. 2011年5月:W3C将HTML5推进至“最终征求”(Last Call)阶段。 +8. 2012年12月:W3C指定HTML5作为“候选推荐”阶段。 +9. 2014年10月:HTML5作为稳定W3C推荐标准发布,这意味着HTML5的标准化已经完成。 + +#### HTML5新特性 + +1. 引入原生多媒体支持(audio和video标签) +2. 引入可编程内容(canvas标签) +3. 引入语义Web(article、aside、details、figure、footer、header、nav、section、summary等标签) +4. 引入新的表单控件(日历、邮箱、搜索、滑条等) +5. 引入对离线存储更好的支持(localStorage和sessionStorage) +6. 引入对定位、拖放、WebSocket、后台任务等的支持 + +### 使用标签承载内容 + +<img src="https://gitee.com/jackfrued/mypic/raw/master/20211107163448.png" style="zoom:35%"> + +<img src="https://gitee.com/jackfrued/mypic/raw/master/20211107163741.png" style="zoom:75%"> + +#### 结构 + +- html + - head + - title + - meta + - body + +#### 文本 + +- 标题(heading)和段落(paragraph) + - h1 ~ h6 + - p +- 上标(superscript)和下标(subscript) + - sup + - sub +- 空白(白色空间折叠) +- 折行(break)和水平标尺(horizontal ruler) + - br + - hr +- 语义化标签 + - 加粗和强调 - strong + - 引用 - blockquote + - 缩写词和首字母缩写词 - abbr / acronym + - 引文 - cite + - 所有者联系信息 - address + - 内容的修改 - ins / del + +#### 列表(list) + + - 有序列表(ordered list)- ol / li + - 无序列表(unordered list)- ul / li + - 定义列表(definition list)- dl / dt / dd + +#### 链接(anchor) + +- 页面链接 +- 锚链接 +- 功能链接 + +#### 图像(image) + +- 图像存储位置 + + ![](./res/相对路径.png) + +- 图像及其宽高 + +- 选择正确的图像格式 + - JPEG + - GIF + - PNG + +- 矢量图 + +- 语义化标签 - figure / figcaption + +#### 表格(table) + +- 基本的表格结构 - table / tr / td / th +- 表格的标题 - caption +- 跨行和跨列 - rowspan属性 / colspan属性 +- 长表格 - thead / tbody / tfoot + +#### 表单(form) + +- 重要属性 - action / method / enctype +- 表单控件(input)- type属性 + - 文本框 - `text` / 密码框 - `password` / 数字框 - `number` + - 邮箱 - `email` / 电话 - `tel` / 日期 - `date` / 滑条 - `range` / URL - `url` / 搜索 - `search` + - 单选按钮 - `radio` / 复选按钮 - `checkbox` + - 文件上传 - `file` / 隐藏域 - `hidden` + - 提交按钮 - `submit` / 图像按钮 - `image` / 重置按钮 - `reset` +- 下拉列表 - select / option +- 文本域(多行文本)- textarea +- 组合表单元素 - fieldset / legend + +#### 音视频(audio / video) + +- 视频格式和播放器 +- 视频托管服务 +- 添加视频的准备工作 +- video标签和属性 - autoplay / controls / loop / muted / preload / src +- audio标签和属性 - autoplay / controls / loop / muted / preload / src / width / height / poster + +#### 窗口(frame) + +- 框架集(过时,不建议使用) - frameset / frame + +- 内嵌窗口 - iframe + +#### 其他 + +- 文档类型 + + ```HTML + <!doctype html> + ``` + + ```HTML + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> + ``` + + ```HTML + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> + ``` + +- 注释 + + ```HTML + <!-- 这是一段注释,注释不能够嵌套 --> + ``` + +- 属性 + - id:唯一标识 + - class:元素所属的类,用于区分不同的元素 + - title:元素的额外信息(鼠标悬浮时会显示工具提示文本) + - tabindex:Tab键切换顺序 + - contenteditable:元素是否可编辑 + - draggable:元素是否可拖拽 + +- 块级元素 / 行级元素 + +- 字符实体(实体替换符) + + ![](./res/字符实体.png) + +### 使用CSS渲染页面 + +#### 简介 + +- CSS的作用 + +- CSS的工作原理 + +- 规则、属性和值 + + ![](./res/选择器语法.png) + +- 常用选择器 + + ![](./res/常用选择器.png) + +#### 颜色(color) + +- 如何指定颜色 +- 颜色术语和颜色对比 +- 背景色 + +#### 文本(text / font) + +- 文本的大小和字型(font-size / font-family) + + ![](./res/尺寸单位.png) + + ![](./res/衬线字体+非衬线字体+等宽字体.png) + +- 粗细、样式、拉伸和装饰(font-weight / font-style / font-stretch / text-decoration) + + ![](./res/字体样式.png) + +- 行间距(line-height)、字母间距(letter-spacing)和单词间距(word-spacing) + +- 对齐(text-align)方式和缩进(text-ident) + +- 链接样式(:link / :visited / :active / :hover) + +- CSS3新属性 + - 阴影效果 - text-shadow + - 首字母和首行文本(:first-letter / :first-line) + - 响应用户 + +#### 盒子(box model) + +- 盒子大小的控制(width / height) + + ![](./res/尺寸单位.png) + +- 盒子的边框、外边距和内边距(border / margin / padding) + + ![](./res/盒子模型.png) + +- 盒子的显示和隐藏(display / visibility) + +- CSS3新属性 + - 边框图像(border-image) + - 投影(border-shadow) + - 圆角(border-radius) + +#### 列表、表格和表单 + +- 列表的项目符号(list-style) +- 表格的边框和背景(border-collapse) +- 表单控件的外观 +- 表单控件的对齐 +- 浏览器的开发者工具 + +#### 图像 + +- 控制图像的大小(display: inline-block) +- 对齐图像 +- 背景图像(background / background-image / background-repeat / background-position) + +#### 布局 + +- 控制元素的位置(position / z-index) + - 普通流 + - 相对定位 + - 绝对定位 + - 固定定位 + - 浮动元素(float / clear) +- 网站布局 + + - HTML5布局 + + ![](./res/经典布局-2.png) +- 适配屏幕尺寸 + - 固定宽度布局 + - 流体布局 + - 布局网格 + +### 使用JavaScript控制行为 + +#### JavaScript基本语法 + +- 语句和注释 +- 变量和数据类型 + - 声明和赋值 + - 简单数据类型和复杂数据类型 + - 变量的命名规则 +- 表达式和运算符 + - 赋值运算符 + - 算术运算符 + - 比较运算符 + - 逻辑运算符:`&&`、`||`、`!` +- 分支结构 + - `if...else...` + - `switch...cas...default...` +- 循环结构 + - `for`循环 + - `while`循环 + - `do...while`循环 +- 数组 + - 创建数组 + - 操作数组中的元素 +- 函数 + - 声明函数 + - 调用函数 + - 参数和返回值 + - 匿名函数 + - 立即调用函数 + +#### 面向对象 + + - 对象的概念 + - 创建对象的字面量语法 + - 访问成员运算符 + - 创建对象的构造函数语法 + - `this`关键字 + - 添加和删除属性 + - `delete`关键字 + - 标准对象 + - `Number` / `String` / `Boolean` / `Symbol` / `Array` / `Function` + - `Date` / `Error` / `Math` / `RegExp` / `Object` / `Map` / `Set` + - `JSON` / `Promise` / `Generator` / `Reflect` / `Proxy` + +#### BOM + + - `window`对象的属性和方法 + - `history`对象 + - `forward()` / `back()` / `go()` + - `location`对象 + - `navigator`对象 + - `screen`对象 + +#### DOM + + - DOM树 + - 访问元素 + - `getElementById()` / `querySelector()` + - `getElementsByClassName()` / `getElementsByTagName()` / `querySelectorAll()` + - `parentNode` / `previousSibling` / `nextSibling` / `children` / `firstChild` / `lastChild` +- 操作元素 + - `nodeValue` + - `innerHTML` / `textContent` / `createElement()` / `createTextNode()` / `appendChild()` / `insertBefore()` / `removeChild()` + - `className` / `id` / `hasAttribute()` / `getAttribute()` / `setAttribute()` / `removeAttribute()` +- 事件处理 + - 事件类型 + - UI事件:`load` / `unload` / `error` / `resize` / `scroll` + - 键盘事件:`keydown` / `keyup` / `keypress` + - 鼠标事件:`click` / `dbclick` / `mousedown` / `mouseup` / `mousemove` / `mouseover` / `mouseout` + - 焦点事件:`focus` / `blur` + - 表单事件:`input` / `change` / `submit` / `reset` / `cut` / `copy` / `paste` / `select` + - 事件绑定 + - HTML事件处理程序(不推荐使用,因为要做到标签与代码分离) + - 传统的DOM事件处理程序(只能附加一个回调函数) + - 事件监听器(旧的浏览器中不被支持) + - 事件流:事件捕获 / 事件冒泡 + - 事件对象(低版本IE中的window.event) + - `target`(有些浏览器使用srcElement) + - `type` + - `cancelable` + - `preventDefault()` + - `stopPropagation()`(低版本IE中的cancelBubble) + - 鼠标事件 - 事件发生的位置 + - 屏幕位置:`screenX`和`screenY` + - 页面位置:`pageX`和`pageY` + - 客户端位置:`clientX`和`clientY` + - 键盘事件 - 哪个键被按下了 + - `keyCode`属性(有些浏览器使用`which`) + - `String.fromCharCode(event.keyCode)` + - HTML5事件 + - `DOMContentLoaded` + - `hashchange` + - `beforeunload` + +#### JavaScript API + +- 客户端存储 - `localStorage`和`sessionStorage` + + ```JavaScript + localStorage.colorSetting = '#a4509b'; + localStorage['colorSetting'] = '#a4509b'; + localStorage.setItem('colorSetting', '#a4509b'); + ``` + +- 获取位置信息 - `geolocation` + + ```JavaScript + navigator.geolocation.getCurrentPosition(function(pos) { + console.log(pos.coords.latitude) + console.log(pos.coords.longitude) + }) + ``` + +- 从服务器获取数据 - Fetch API +- 绘制图形 - `<canvas>`的API +- 音视频 - `<audio>`和`<video>`的API + +### 使用jQuery + +#### jQuery概述 + +1. Write Less Do More(用更少的代码来完成更多的工作) +2. 使用CSS选择器来查找元素(更简单更方便) +3. 使用jQuery方法来操作元素(解决浏览器兼容性问题、应用于所有元素并施加多个方法) + +#### 引入jQuery + +- 下载jQuery的开发版和压缩版 +- 从CDN加载jQuery + +```HTML +<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> +<script> + window.jQuery || + document.write('<script src="js/jquery-3.3.1.min.js"></script>') +</script> +``` + +#### 查找元素 + +- 选择器 + - \* / element / #id / .class / selector1, selector2 + - ancestor descendant / parent>child / previous+next / previous~siblings +- 筛选器 + - 基本筛选器::not(selector) / :first / :last / :even / :odd / :eq(index) / :gt(index) / :lt(index) / :animated / :focus + - 内容筛选器::contains('…') / :empty / :parent / :has(selector) + - 可见性筛选器::hidden / :visible + - 子节点筛选器::nth-child(expr) / :first-child / :last-child / :only-child + - 属性筛选器:[attribute] / [attribute='value'] / [attribute!='value'] / [attribute^='value'] / [attribute$='value'] / [attribute|='value'] / [attribute~='value'] +- 表单::input / :text / :password / :radio / :checkbox / :submit / :image / :reset / :button / :file / :selected / :enabled / :disabled / :checked + +#### 执行操作 + +- 内容操作 + - 获取/修改内容:`html()` / `text()` / `replaceWith()` / `remove()` + - 获取/设置元素:`before()` / `after()` / `prepend()` / `append()` / `remove()` / `clone()` / `unwrap()` / `detach()` / `empty()` / `add()` + - 获取/修改属性:`attr()` / `removeAttr()` / `addClass()` / `removeClass()` / `css()` + - 获取/设置表单值:`val()` +- 查找操作 + - 查找方法:`find()` / `parent()` / `children()` / `siblings()` / `next()` / `nextAll()` / `prev()` / `prevAll()` + - 筛选器:`filter()` / `not()` / `has()` / `is()` / `contains()` + - 索引编号:`eq()` +- 尺寸和位置 + - 尺寸相关:`height()` / `width()` / `innerHeight()` / `innerWidth()` / `outerWidth()` / `outerHeight()` + - 位置相关:`offset()` / `position()` / `scrollLeft()` / `scrollTop()` +- 特效和动画 + - 基本动画:`show()` / `hide()` / `toggle()` + - 消失出现:`fadeIn()` / `fadeOut()` / `fadeTo()` / `fadeToggle()` + - 滑动效果:`slideDown()` / `slideUp()` / `slideToggle()` + - 自定义:`delay()` / `stop()` / `animate()` +- 事件 + - 文档加载:`ready()` / `load()` + - 用户交互:`on()` / `off()` + +#### 链式操作 + +#### 检测页面是否可用 + +```HTML +<script> + $(document).ready(function() { + + }); +</script> +``` + +```HTML +<script> + $(function() { + + }); +</script> +``` + +#### jQuery插件 + +- jQuery Validation +- jQuery Treeview +- jQuery Autocomplete +- jQuery UI + +#### 避免和其他库的冲突 + +先引入其他库再引入jQuery的情况。 + +```HTML +<script src="other.js"></script> +<script src="jquery.js"></script> +<script> + jQuery.noConflict(); + jQuery(function() { + jQuery('div').hide(); + }); +</script> +``` + +先引入jQuery再引入其他库的情况。 + +```HTML + +<script src="jquery.js"></script> +<script src="other.js"></script> +<script> + jQuery(function() { + jQuery('div').hide(); + }); +</script> +``` + +#### 使用Ajax + +Ajax是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。 + +- 原生的Ajax +- 基于jQuery的Ajax + - 加载内容 + - 提交表单 + +### 前端框架 + +#### 渐进式框架 - [Vue.js](<https://cn.vuejs.org/>) + +前后端分离开发(前端渲染)必选框架。 + +##### 快速上手 + +1. 引入Vue的JavaScript文件,我们仍然推荐从CDN服务器加载它。 + + ```HTML + <script src="https://cdn.jsdelivr.net/npm/vue"></script> + ``` + +2. 数据绑定(声明式渲染 )。 + + ```HTML + <div id="app"> + <h1>{{ product }}库存信息</h1> + </div> + + <script src="https://cdn.jsdelivr.net/npm/vue"></script> + <script> + const app = new Vue({ + el: '#app', + data: { + product: 'iPhone X' + } + }); + </script> + ``` + +3. 条件与循环。 + + ```HTML + <div id="app"> + <h1>库存信息</h1> + <hr> + <ul> + <li v-for="product in products"> + {{ product.name }} - {{ product.quantity }} + <span v-if="product.quantity === 0"> + 已经售罄 + </span> + </li> + </ul> + </div> + + <script src="https://cdn.jsdelivr.net/npm/vue"></script> + <script> + const app = new Vue({ + el: '#app', + data: { + products: [ + {"id": 1, "name": "iPhone X", "quantity": 20}, + {"id": 2, "name": "华为 Mate20", "quantity": 0}, + {"id": 3, "name": "小米 Mix3", "quantity": 50} + ] + } + }); + </script> + ``` + +4. 计算属性。 + + ```HTML + <div id="app"> + <h1>库存信息</h1> + <hr> + <ul> + <li v-for="product in products"> + {{ product.name }} - {{ product.quantity }} + <span v-if="product.quantity === 0"> + 已经售罄 + </span> + </li> + </ul> + <h2>库存总量:{{ totalQuantity }}台</h2> + </div> + + <script src="https://cdn.jsdelivr.net/npm/vue"></script> + <script> + const app = new Vue({ + el: '#app', + data: { + products: [ + {"id": 1, "name": "iPhone X", "quantity": 20}, + {"id": 2, "name": "华为 Mate20", "quantity": 0}, + {"id": 3, "name": "小米 Mix3", "quantity": 50} + ] + }, + computed: { + totalQuantity() { + return this.products.reduce((sum, product) => { + return sum + product.quantity + }, 0); + } + } + }); + </script> + ``` + +5. 处理事件。 + + ```HTML + <div id="app"> + <h1>库存信息</h1> + <hr> + <ul> + <li v-for="product in products"> + {{ product.name }} - {{ product.quantity }} + <span v-if="product.quantity === 0"> + 已经售罄 + </span> + <button @click="product.quantity += 1"> + 增加库存 + </button> + </li> + </ul> + <h2>库存总量:{{ totalQuantity }}台</h2> + </div> + + <script src="https://cdn.jsdelivr.net/npm/vue"></script> + <script> + const app = new Vue({ + el: '#app', + data: { + products: [ + {"id": 1, "name": "iPhone X", "quantity": 20}, + {"id": 2, "name": "华为 Mate20", "quantity": 0}, + {"id": 3, "name": "小米 Mix3", "quantity": 50} + ] + }, + computed: { + totalQuantity() { + return this.products.reduce((sum, product) => { + return sum + product.quantity + }, 0); + } + } + }); + </script> + ``` + +6. 用户输入。 + + ```HTML + <div id="app"> + <h1>库存信息</h1> + <hr> + <ul> + <li v-for="product in products"> + {{ product.name }} - + <input type="number" v-model.number="product.quantity" min="0"> + <span v-if="product.quantity === 0"> + 已经售罄 + </span> + <button @click="product.quantity += 1"> + 增加库存 + </button> + </li> + </ul> + <h2>库存总量:{{ totalQuantity }}台</h2> + </div> + + <script src="https://cdn.jsdelivr.net/npm/vue"></script> + <script> + const app = new Vue({ + el: '#app', + data: { + products: [ + {"id": 1, "name": "iPhone X", "quantity": 20}, + {"id": 2, "name": "华为 Mate20", "quantity": 0}, + {"id": 3, "name": "小米 Mix3", "quantity": 50} + ] + }, + computed: { + totalQuantity() { + return this.products.reduce((sum, product) => { + return sum + product.quantity + }, 0); + } + } + }); + </script> + ``` + +7. 通过网络加载JSON数据。 + + ```HTML + <div id="app"> + <h2>库存信息</h2> + <ul> + <li v-for="product in products"> + {{ product.name }} - {{ product.quantity }} + <span v-if="product.quantity === 0"> + 已经售罄 + </span> + </li> + </ul> + </div> + + <script src="https://cdn.jsdelivr.net/npm/vue"></script> + <script> + const app = new Vue({ + el: '#app', + data: { + products: [] + }, + created() { + fetch('https://jackfrued.top/api/products') + .then(response => response.json()) + .then(json => { + this.products = json + }); + } + }); + </script> + ``` + +##### 使用脚手架 - vue-cli + +Vue为商业项目开发提供了非常便捷的脚手架工具vue-cli,通过工具可以省去手工配置开发环境、测试环境和运行环境的步骤,让开发者只需要关注要解决的问题。 + +1. 安装脚手架。 +2. 创建项目。 +3. 安装依赖包。 +4. 运行项目。 + + +#### UI框架 - [Element](<http://element-cn.eleme.io/#/zh-CN>) + +基于Vue 2.0的桌面端组件库,用于构造用户界面,支持响应式布局。 + +1. 引入Element的CSS和JavaScript文件。 + + ```HTML + <!-- 引入样式 --> + <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> + <!-- 引入组件库 --> + <script src="https://unpkg.com/element-ui/lib/index.js"></script> + ``` + +2. 一个简单的例子。 + + ```HTML + <!DOCTYPE html> + <html> + <head> + <meta charset="UTF-8"> + <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> + </head> + <body> + <div id="app"> + <el-button @click="visible = true">点我</el-button> + <el-dialog :visible.sync="visible" title="Hello world"> + <p>开始使用Element吧</p> + </el-dialog> + </div> + </body> + <script src="https://unpkg.com/vue/dist/vue.js"></script> + <script src="https://unpkg.com/element-ui/lib/index.js"></script> + <script> + new Vue({ + el: '#app', + data: { + visible: false, + } + }) + </script> + </html> + ``` + +3. 使用组件。 + + ```HTML + <!DOCTYPE html> + <html> + <head> + <meta charset="UTF-8"> + <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> + </head> + <body> + <div id="app"> + <el-table :data="tableData" stripe style="width: 100%"> + <el-table-column prop="date" label="日期" width="180"> + </el-table-column> + <el-table-column prop="name" label="姓名" width="180"> + </el-table-column> + <el-table-column prop="address" label="地址"> + </el-table-column> + </el-table> + </div> + </body> + <script src="https://unpkg.com/vue/dist/vue.js"></script> + <script src="https://unpkg.com/element-ui/lib/index.js"></script> + <script> + new Vue({ + el: '#app', + data: { + tableData: [ + { + date: '2016-05-02', + name: '王一霸', + address: '上海市普陀区金沙江路 1518 弄' + }, + { + date: '2016-05-04', + name: '刘二狗', + address: '上海市普陀区金沙江路 1517 弄' + }, + { + date: '2016-05-01', + name: '杨三萌', + address: '上海市普陀区金沙江路 1519 弄' + }, + { + date: '2016-05-03', + name: '陈四吹', + address: '上海市普陀区金沙江路 1516 弄' + } + ] + } + }) + </script> + </html> + ``` + + +#### 报表框架 - [ECharts](<https://echarts.baidu.com>) + +百度出品的开源可视化库,常用于生成各种类型的报表。 + +![](./res/baidu_echarts.png) + +#### 基于弹性盒子的CSS框架 - [Bulma](<https://bulma.io/>) + +Bulma是一个基于Flexbox的现代化的CSS框架,其初衷就是移动优先(Mobile First),模块化设计,可以轻松用来实现各种简单或者复杂的内容布局,即使不懂CSS的开发者也能够使用它定制出漂亮的页面。 + +```HTML +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Bulma + + + + +
+
1
+
2
+
3
+
4
+
+ +
+ 60% +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
OneTwo
ThreeFour
FiveSix
SevenEight
NineTen
ElevenTwelve
+
+ + +``` + +#### 响应式布局框架 - [Bootstrap]() + +用于快速开发Web应用程序的前端框架,支持响应式布局。 + +1. 特点 + - 支持主流的浏览器和移动设备 + - 容易上手 + - 响应式设计 + +2. 内容 + - 网格系统 + - 封装的CSS + - 现成的组件 + - JavaScript插件 + +3. 可视化 + + ![](./res/bootstrap-layoutit.png) \ No newline at end of file diff --git "a/Day21-30/Web\345\211\215\347\253\257\346\246\202\350\277\260.md" "b/Day21-30/Web\345\211\215\347\253\257\346\246\202\350\277\260.md" deleted file mode 100644 index 088b2680c..000000000 --- "a/Day21-30/Web\345\211\215\347\253\257\346\246\202\350\277\260.md" +++ /dev/null @@ -1,407 +0,0 @@ -## Web前端概述 - -### HTML简史 - -1. 1991年10月:一个非正式CERN([欧洲核子研究中心](https://zh.wikipedia.org/wiki/%E6%AD%90%E6%B4%B2%E6%A0%B8%E5%AD%90%E7%A0%94%E7%A9%B6%E7%B5%84%E7%B9%94))文件首次公开18个HTML标签,这个文件的作者是物理学家[蒂姆·伯纳斯-李](https://zh.wikipedia.org/wiki/%E8%92%82%E5%A7%86%C2%B7%E4%BC%AF%E7%BA%B3%E6%96%AF-%E6%9D%8E),因此他是[万维网](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91)的发明者,也是[万维网联盟](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91%E8%81%94%E7%9B%9F)的主席。 -2. 1995年11月:HTML 2.0标准发布(RFC 1866)。 -3. 1997年1月:HTML 3.2作为[W3C](https://zh.wikipedia.org/wiki/W3C)推荐标准发布。 -4. 1997年12月:HTML 4.0作为W3C推荐标准发布。 -5. 1999年12月:HTML4.01作为W3C推荐标准发布。 -6. 2008年1月:HTML5由W3C作为工作草案发布。 -7. 2011年5月:W3C将HTML5推进至“最终征求”(Last Call)阶段。 -8. 2012年12月:W3C指定HTML5作为“候选推荐”阶段。 -9. 2014年10月:HTML5作为稳定W3C推荐标准发布,这意味着HTML5的标准化已经完成。 - -#### HTML5新特性 - -1. 引入原生多媒体支持(audio和video标签) -2. 引入可编程内容(canvas标签) -3. 引入语义Web(article、aside、details、figure、footer、header、nav、section、summary等标签) -4. 引入新的表单控件(日历、邮箱、搜索等) -5. 引入对离线存储更好的支持 -6. 引入对定位、拖放、WebSocket、后台任务等的支持 - -### 使用标签承载内容 - -#### 结构 - -- head - - title - - meta -- body - -#### 文本 - -- 标题和段落 -- 粗体和斜体 -- 上标和下标 -- 空白(白色空间折叠) -- 折行和水平标尺 -- 语义化标记 - - 加粗和强调 - - 引用 - - 缩写词和首字母缩写词 - - 引文 - - 所有者联系信息 - - 内容的修改 - -#### 列表(list) - - - 有序列表(ordered list) - - 无序列表(unordered list) - - 定义列表(definition list) - -#### 链接(anchor) - -- 页面链接 -- 锚链接 -- 功能链接 - -#### 图像(image) - -- 图像存储位置 -- 图像及其宽高 -- 选择正确的图像格式 - - JPEG - - GIF - - PNG -- 矢量图 -- figure标签 - -#### 表格(table) - -- 基本的表格结构 -- 表格的标题 -- 跨行和跨列 -- 长表格 - -#### 表单(form) - -- 如何收集信息 -- 表单控件(input) - - 文本框 / 密码框 / 文本域 - - 单选按钮 / 复选按钮 / 下拉列表 - - 提交按钮 / 图像按钮 / 文件上传 -- 组合表单元素 - - fieldset / legend -- HTML5的表单控件 - - 日期 - - 电子邮件 / URL - - 搜索 - -#### 音视频(audio / video) - -- 视频格式和播放器 -- 视频托管服务 -- 添加视频的准备工作 -- video标签和属性 -- audio标签和属性 - -#### 其他 - -- 文档类型 -- 注释 -- 属性 - - id - - class -- 块级元素 / 行级元素 -- 内联框架(internal frame) -- 页面信息(meta) -- 转义字符(实体替换符) - -### 使用CSS渲染页面 - -#### 简介 - -- CSS的作用 -- CSS的工作原理 -- 规则、属性和值 - -#### 颜色(color) - -- 如何指定颜色 -- 颜色术语和颜色对比 -- 背景色 - -#### 文本(text / font) - -- 文本的大小和字型(font-size / font-family) -- 斜体、粗体、大写和下划线(font-weight / font-style / text-decoration) -- 行间距(line-height)、字母间距(letter-spacing)和单词间距(word-spacing) -- 对齐(text-align)方式和缩进(text-ident) -- 链接样式(:link / :visited / :active / :hover) -- CSS3新属性 - - 投影 - - 首字母和首行文本(p:first-letter / p:first-line) - - 响应用户 - -#### 盒子(box model) - -- 盒子大小的控制(width / height) -- 盒子的边框、外边距和内边距(border / margin / padding) -- 盒子的显示和隐藏(display / visibility) -- CSS3新属性 - - 边框图像(border-image) - - 投影(border-shadow) - - 圆角(border-radius) - -#### 列表、表格和表单 - -- 列表的项目符号(list-style) -- 表格的边框和背景(border-collapse) -- 表单控件的外观 -- 表单控件的对齐 -- 浏览器的开发者工具 - -#### 图像 - -- 控制图像的大小(display: inline-block) -- 对齐图像 -- 背景图像(background / background-image / background-repeat / background-position) - -#### 布局 - -- 控制元素的位置(position / z-index) - - 普通流 - - 相对定位 - - 绝对定位 - - 固定定位 - - 浮动元素(float / clear) -- 网站布局 - - HTML5布局 -- 适配屏幕尺寸 - - 固定宽度布局 - - 流体布局 - - 布局网格 - -### 使用JavaScript控制行为 - -#### JavaScript基本语法 - -- 语句和注释 -- 变量和数据类型 - - 声明和赋值 - - 简单数据类型和复杂数据类型 - - 变量的命名规则 -- 表达式和运算符 - - 赋值运算符 - - 算术运算符 - - 比较运算符 - - 逻辑运算符 -- 分支结构 - - if…else... - - switch…case…default... -- 循环结构 - - for循环 - - while循环 - - do…while循环 -- 数组 - - 创建数组 - - 操作数组中的元素 -- 函数 - - 声明函数 - - 调用函数 - - 参数和返回值 - - 匿名函数 - - 立即调用函数 - -#### 面向对象 - - - 对象的概念 - - 创建对象的字面量语法 - - 访问成员运算符 - - 创建对象的构造函数语法 - - this关键字 - - 添加和删除属性 - - delete关键字 - - 全局对象 - - Number / String / Boolean - - Date / Math / RegEx / Array - -#### BOM - - - window对象的属性和方法 - - history对象 - - forward() / back() / go() - - location对象 - - navigator对象 - - screen对象 - -#### DOM - - - DOM树 - - 访问元素 - - getElementById() / querySelector() - - getElementsByClassName() / getElementsByTagName() / querySelectorAll() - - parentNode / previousSibling / nextSibling / firstChild / lastChild -- 操作元素 - - nodeValue - - innerHTML / textContent / createElement() / createTextNode() / appendChild() / removeChild() - - className / id / hasAttribute() / getAttribute() / setAttribute() / removeAttribute() -- 事件处理 - - 事件类型 - - UI事件:load / unload / error / resize / scroll - - 键盘事件:keydown / keyup / keypress - - 鼠标事件:click / dbclick / mousedown / mouseup / mousemove / mouseover / mouseout - - 焦点事件:focus / blur - - 表单事件:input / change / submit / reset / cut / copy / paste / select - - 事件绑定 - - HTML事件处理程序(不推荐使用,因为要做到标签与代码分离) - - 传统的DOM事件处理程序(只能附加一个回调函数) - - 事件监听器(旧的浏览器中不被支持) - - 事件流:事件捕获 / 事件冒泡 - - 事件对象(低版本IE中的window.event) - - target(低版本IE中的srcElement) - - type - - cancelable - - preventDefault() - - stopPropagation()(低版本IE中的cancelBubble) - - 鼠标事件 - 事件发生的位置 - - 屏幕位置:screenX和screenY - - 页面位置:pageX和pageY - - 客户端位置:clientX和clientY - - 键盘事件 - 哪个键被按下了 - - keyCode属性 - - String.fromCharCode(event.keyCode) - - HTML5事件 - - DOMContentLoaded - - hashchange - - beforeunload - -#### JavaScript API - -- HTML5中的API:geolocation / localStorage / sessionStorage / history - -### 使用jQuery - -#### jQuery概述 - -1. Write Less Do More(用更少的代码来完成更多的工作) -2. 使用CSS选择器来查找元素(更简单更方便) -3. 使用jQuery方法来操作元素(解决浏览器兼容性问题、应用于所有元素并施加多个方法) - -#### 引入jQuery - -- 下载jQuery的开发版和压缩版 -- 从CDN加载jQuery - -```HTML - -') - -``` - -#### 查找元素 - -- 选择器 - - \* / element / #id / .class / selector1, selector2 - - ancestor descendant / parent>child / previous+next / previous~siblings -- 筛选器 - - 基本筛选器::not(selector) / :first / :last / :even / :odd / :eq(index) / :gt(index) / :lt(index) / :animated / :focus - - 内容筛选器::contains('…') / :empty / :parent / :has(selector) - - 可见性筛选器::hidden / :visible - - 子节点筛选器::nth-child(expr) / :first-child / :last-child / :only-child - - 属性筛选器:[attribute] / [attribute='value'] / [attribute!='value'] / [attribute^='value'] / [attribute$='value'] / [attribute|='value'] / [attribute~='value'] -- 表单::input / :text / :password / :radio / :checkbox / :submit / :image / :reset / :button / :file / :selected / :enabled / :disabled / :checked - -#### 执行操作 - -- 内容操作 - - 获取/修改内容:html() / text() / replaceWith() / remove() - - 获取/设置元素:before() / after() / prepend() / append() / remove() / clone() / unwrap() / detach() / empty() / add() - - 获取/修改属性:attr() / removeAttr() / addClass() / removeClass() / css() - - 获取/设置表单值:val() -- 查找操作 - - 查找方法:find() / parent() / children() / siblings() / next() / nextAll() / prev() / prevAll() - - 筛选器:filter() / not() / has() / is() / contains() - - 索引编号:eq() -- 尺寸和位置 - - 尺寸相关:height() / width() / innerHeight() / innerWidth() / outerWidth() / outerHeight() - - 位置相关:offset() / position() / scrollLeft() / scrollTop() -- 特效和动画 - - 基本动画:show() / hide() / toggle() - - 消失出现:fadeIn() / fadeOut() / fadeTo() / fadeToggle() - - 滑动效果:slideDown() / slideUp() / slideToggle() - - 自定义:delay() / stop() / animate() -- 事件 - - 文档加载:ready() / load() - - 用户交互:on() / off() - -#### 链式操作 - -#### 检测页面是否可用 - -```HTML - -``` - -```HTML - -``` - -#### jQuery插件 - -- jQuery Validation -- jQuery Treeview -- jQuery Autocomplete -- jQuery UI - -#### 避免和其他库的冲突 - -先引入其他库再引入jQuery的情况。 - -```HTML - - - -``` - -先引入jQuery再引入其他库的情况。 - -```HTML - - - -``` - -#### 使用Ajax - -- 原生的Ajax -- 基于jQuery的Ajax - - 加载内容 - - 提交表单 - -### 使用Bootstrap - -#### 特点 - -1. 支持主流的浏览器和移动设备 -2. 容易上手 -3. 响应式设计 - -#### 内容 - -1. 网格系统 -2. 封装的CSS -3. 现成的组件 -4. JavaScript插件 - diff --git a/Day21-30/code/list_by_javascript.html b/Day21-30/code/list_by_javascript.html new file mode 100755 index 000000000..ade42f100 --- /dev/null +++ b/Day21-30/code/list_by_javascript.html @@ -0,0 +1,119 @@ + + + + + 动态列表 + + + +
+
    +
  • 苹果×
  • +
  • 香蕉×
  • +
  • 榴莲×
  • +
  • 火龙果×
  • +
+
+ + +
+
+ + + \ No newline at end of file diff --git a/Day21-30/code/list_by_jquery.html b/Day21-30/code/list_by_jquery.html new file mode 100755 index 000000000..d6fbc1f63 --- /dev/null +++ b/Day21-30/code/list_by_jquery.html @@ -0,0 +1,114 @@ + + + + + 动态列表 + + + +
+
    +
  • 苹果×
  • +
  • 香蕉×
  • +
  • 榴莲×
  • +
  • 火龙果×
  • +
+
+ + +
+
+ + + + \ No newline at end of file diff --git a/Day21-30/code/list_by_vue.html b/Day21-30/code/list_by_vue.html new file mode 100755 index 000000000..8e944226d --- /dev/null +++ b/Day21-30/code/list_by_vue.html @@ -0,0 +1,100 @@ + + + + + 动态列表 + + + +
+
    +
  • + {{ fruit }} + × +
  • +
+
+ + +
+
+ + + + \ No newline at end of file diff --git a/Day21-30/code/new/vue/vue.demo.html b/Day21-30/code/new/vue/vue.demo.html new file mode 100755 index 000000000..fc09e8002 --- /dev/null +++ b/Day21-30/code/new/vue/vue.demo.html @@ -0,0 +1,40 @@ + + +
+

库存信息

+
+
    +
  • + {{ product.name }} - + + + 已经售罄 + + +
  • +
+

库存总量:{{ totalQuantity }}台

+
+ + + \ No newline at end of file diff --git a/Day21-30/code/new/web1901/audio/test-audio.mp3 b/Day21-30/code/new/web1901/audio/test-audio.mp3 new file mode 100755 index 000000000..63fab14ff Binary files /dev/null and b/Day21-30/code/new/web1901/audio/test-audio.mp3 differ diff --git a/Day21-30/code/new/web1901/audio/test-audio.ogg b/Day21-30/code/new/web1901/audio/test-audio.ogg new file mode 100755 index 000000000..7c3fe6301 Binary files /dev/null and b/Day21-30/code/new/web1901/audio/test-audio.ogg differ diff --git a/Day21-30/code/new/web1901/css/style.css b/Day21-30/code/new/web1901/css/style.css new file mode 100755 index 000000000..90ab13691 --- /dev/null +++ b/Day21-30/code/new/web1901/css/style.css @@ -0,0 +1,56 @@ +/* 通配符选择器 */ +* { + margin: 0; + padding: 0; +} +/* 标签选择器 */ +h1 { + width: 960px; + height: 40px; + margin: 5px auto; +} +/* 类选择器 */ +.a { + background-color: red; +} +.b { + background-color: orange; +} +.c { + background-color: yellow; +} +.d { + background-color: green; +} +.e { + background-color: cyan; +} +.f { + background-color: blue; +} +.g { + background-color: purple; +} +.h { + color: blue; + text-align: center; + width: 100px; + height: 38px; + overflow: hidden; +} +.big { + font-size: 32px; +} +.normal { + font-size: 18px; +} +.small { + font-size: 12px; +} +/* ID选择器 */ +#header, #footer { + width: 800px; + height: 120px; + border: 1px solid red; + margin: 10px auto; +} \ No newline at end of file diff --git a/Day21-30/code/new/web1901/css_practice_1.html b/Day21-30/code/new/web1901/css_practice_1.html new file mode 100755 index 000000000..2e3ea202a --- /dev/null +++ b/Day21-30/code/new/web1901/css_practice_1.html @@ -0,0 +1,81 @@ + + + + Boxes + + + +
+ + +

+ Fender Rhodes, Hohner Clavinet, and Wurlitzer EP200 +

+

+ We specialize in the sale and repair of classic keyboards, in particular the Fender Rhodes, Wurlitzer EP200, and Hohner Clavinet. +

+
+ + \ No newline at end of file diff --git a/Day21-30/code/new/web1901/css_practice_1.result.html b/Day21-30/code/new/web1901/css_practice_1.result.html new file mode 100755 index 000000000..04aafb34d --- /dev/null +++ b/Day21-30/code/new/web1901/css_practice_1.result.html @@ -0,0 +1,74 @@ + + + + Boxes + + + +
+ + +

+ Fender Rhodes, Hohner Clavinet, and Wurlitzer EP200 +

+

+ We specialize in the sale and repair of classic keyboards, in particular the Fender Rhodes, Wurlitzer EP200, and Hohner Clavinet. +

+
+ + \ No newline at end of file diff --git a/Day21-30/code/new/web1901/css_practice_2.html b/Day21-30/code/new/web1901/css_practice_2.html new file mode 100755 index 000000000..04c7cfa43 --- /dev/null +++ b/Day21-30/code/new/web1901/css_practice_2.html @@ -0,0 +1,101 @@ + + + + Lists, Tables and Forms + + + +

Poetry Workshops

+

We will be conducting a number of poetry workshops and symposiums throughout the year.

+

Please note that the following events are free to members:

+
    +
  • A Poetic Perspective
  • +
  • Walt Whitman at War
  • +
  • Found Poems and Outsider Poetry
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
New YorkChicagoSan Francisco
A Poetic PerspectiveSat, 4 Feb 2012
11am - 2pm
Sat, 3 Mar 2012
11am - 2pm
Sat, 17 Mar 2012
11am - 2pm
Walt Whitman at WarSat, 7 Apr 2012
11am - 1pm
Sat, 5 May 2012
11am - 1pm
Sat, 19 May 2012
11am - 1pm
Found Poems & Outsider PoetrySat, 9 Jun 2012
11am - 2pm
Sat, 7 Jul 2012
11am - 2pm
Sat, 21 Jul 2012
11am - 2pm
Natural Death: An ExplorationSat, 4 Aug 2012
11am - 4pm
Sat, 8 Sep 2012
11am - 4pm
Sat, 15 Sep 2012
11am - 4pm
+
+
+ Register your interest +

+ +
+ + +

+

+ + +

+ + Yes + No +
+
+ +
+
+ + diff --git a/Day21-30/code/new/web1901/css_practice_2.result.html b/Day21-30/code/new/web1901/css_practice_2.result.html new file mode 100755 index 000000000..c9b2e0904 --- /dev/null +++ b/Day21-30/code/new/web1901/css_practice_2.result.html @@ -0,0 +1,175 @@ + + + + Lists, Tables and Forms + + + +

Poetry Workshops

+

We will be conducting a number of poetry workshops and symposiums throughout the year.

+

Please note that the following events are free to members:

+
    +
  • A Poetic Perspective
  • +
  • Walt Whitman at War
  • +
  • Found Poems and Outsider Poetry
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
New YorkChicagoSan Francisco
A Poetic PerspectiveSat, 4 Feb 2012
11am - 2pm
Sat, 3 Mar 2012
11am - 2pm
Sat, 17 Mar 2012
11am - 2pm
Walt Whitman at WarSat, 7 Apr 2012
11am - 1pm
Sat, 5 May 2012
11am - 1pm
Sat, 19 May 2012
11am - 1pm
Found Poems & Outsider PoetrySat, 9 Jun 2012
11am - 2pm
Sat, 7 Jul 2012
11am - 2pm
Sat, 21 Jul 2012
11am - 2pm
Natural Death: An ExplorationSat, 4 Aug 2012
11am - 4pm
Sat, 8 Sep 2012
11am - 4pm
Sat, 15 Sep 2012
11am - 4pm
+
+
+ Register your interest +

+ +
+ + +

+

+ + +

+ Are you a member? + + +
+
+ +
+
+ + diff --git a/Day21-30/code/new/web1901/css_practice_3.html b/Day21-30/code/new/web1901/css_practice_3.html new file mode 100755 index 000000000..ee966fa6c --- /dev/null +++ b/Day21-30/code/new/web1901/css_practice_3.html @@ -0,0 +1,73 @@ + + + + HTML5 Layout + + +
+
+

Yoko's Kitchen

+ +
+
+
+
+ Bok Choi +
Bok Choi
+
+
+

Japanese Vegetarian

+

Five week course in London

+
+

+ A five week introduction to traditional Japanese vegetarian meals, teaching you a selection of rice and noodle + dishes. +

+
+
+
+ Teriyaki sauce +
Teriyaki Sauce
+
+
+

Sauces Masterclass

+

One day workshop

+
+

+ An intensive one-day course looking at how to create the most delicious sauces for use in a range of Japanese + cookery. +

+
+
+ +
+ © 2011 Yoko's Kitchen +
+
+ + diff --git a/Day21-30/code/new/web1901/css_practice_3.result.html b/Day21-30/code/new/web1901/css_practice_3.result.html new file mode 100755 index 000000000..d5729b5e1 --- /dev/null +++ b/Day21-30/code/new/web1901/css_practice_3.result.html @@ -0,0 +1,222 @@ + + + + HTML5 Layout + + + + +
+
+

Yoko's Kitchen

+ +
+
+
+
+ Bok Choi +
Bok Choi
+
+
+

Japanese Vegetarian

+

Five week course in London

+
+

+ A five week introduction to traditional Japanese vegetarian meals, teaching you a selection of rice and noodle + dishes. +

+
+
+
+ Teriyaki sauce +
Teriyaki Sauce
+
+
+

Sauces Masterclass

+

One day workshop

+
+

+ An intensive one-day course looking at how to create the most delicious sauces for use in a range of Japanese + cookery. +

+
+
+ +
+ © 2011 Yoko's Kitchen +
+
+ + diff --git a/Day21-30/code/new/web1901/example_of_anchor.html b/Day21-30/code/new/web1901/example_of_anchor.html new file mode 100755 index 000000000..c72126d96 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_anchor.html @@ -0,0 +1,75 @@ + + + + + 锚点连接 + + + +
+ 此广告位招租 + +
+

坚持中国特色社会主义

+
+

+ 第一,中国特色社会主义是社会主义而不是其他什么主义,科学社会主义基本原则不能丢,丢了就不是社会主义。我们党始终强调,中国特色社会主义,既坚持了科学社会主义基本原则,又根据时代条件赋予其鲜明的中国特色。这就是说,中国特色社会主义是社会主义,不是别的什么主义。一个国家实行什么样的主义,关键要看这个主义能否解决这个国家面临的历史性课题。在中华民族积贫积弱、任人宰割的时期,各种主义和思潮都进行过尝试,资本主义道路没有走通,改良主义、自由主义、社会达尔文主义、无政府主义、实用主义、民粹主义、工团主义等也都“你方唱罢我登场”,但都没能解决中国的前途和命运问题。是马克思列宁主义、毛泽东思想引导中国人民走出了漫漫长夜、建立了新中国,是中国特色社会主义使中国快速发展起来了。不说更早的时期,就从改革开放开始,特别是苏联解体、东欧剧变以后,唱衰中国的舆论在国际上不绝于耳,各式各样的“中国崩溃论”从来没有中断过。但是,中国非但没有崩溃,反而综合国力与日俱增,人民生活水平不断提高,“风景这边独好”。历史和现实都告诉我们,只有社会主义才能救中国,只有中国特色社会主义才能发展中国,这是历史的结论、人民的选择。 +

+

+ 近些年来,国内外有些舆论提出中国现在搞的究竟还是不是社会主义的疑问,有人说是“资本社会主义”,还有人干脆说是“国家资本主义”、“新官僚资本主义”。这些都是完全错误的。我们说中国特色社会主义是社会主义,那就是不论怎么改革、怎么开放,我们都始终要坚持中国特色社会主义道路、中国特色社会主义理论体系、中国特色社会主义制度,坚持党的十八大提出的夺取中国特色社会主义新胜利的基本要求。这就包括在中国共产党领导下,立足基本国情,以经济建设为中心,坚持四项基本原则,坚持改革开放,解放和发展社会生产力,建设社会主义市场经济、社会主义民主政治、社会主义先进文化、社会主义和谐社会、社会主义生态文明,促进人的全面发展,逐步实现全体人民共同富裕,建设富强民主文明和谐的社会主义现代化国家;包括坚持人民代表大会制度的根本政治制度,中国共产党领导的多党合作和政治协商制度、民族区域自治制度以及基层群众自治制度等基本政治制度,中国特色社会主义法律体系,公有制为主体、多种所有制经济共同发展的基本经济制度。这些都是在新的历史条件下体现科学社会主义基本原则的内容,如果丢掉了这些,那就不成其为社会主义了。 +

+

+ 邓小平同志曾经深刻地、总结性地指出:“我们的现代化建设,必须从中国的实际出发。无论是革命还是建设,都要注意学习和借鉴外国经验。但是,照抄照搬别国经验、别国模式,从来不能得到成功。这方面我们有过不少教训。”过去不能搞全盘苏化,现在也不能搞全盘西化或者其他什么化。冷战结束后,不少发展中国家被迫采纳了西方模式,结果党争纷起、社会动荡、人民流离失所,至今都难以稳定下来。《庄子·秋水》中写道:“且子独不闻夫寿陵余子之学行于邯郸与?未得国能,又失其故行矣,直匍匐而归耳。”我们千万不能“邯郸学步,失其故行”。我们就是把马克思主义中国化,就是搞中国特色社会主义。近年来,随着我国综合国力和国际地位上升,国际上关于“北京共识”、“中国模式”、“中国道路”等议论和研究也多了起来,其中不乏赞扬者。一些外国学者认为,中国的快速发展,导致一些西方理论正在被质疑,一种新版的马克思主义理论正在颠覆西方的传统理论。我们始终认为,各国的发展道路应由各国人民选择。所谓的“中国模式”是中国人民在自己的奋斗实践中创造的中国特色社会主义道路。我们坚信,随着中国特色社会主义不断发展,我们的制度必将越来越成熟,我国社会主义制度的优越性必将进一步显现,我们的道路必将越走越宽广,我国发展道路对世界的影响必将越来越大。我们就是要有这样的道路自信、理论自信、制度自信,真正做到“千磨万击还坚劲,任尔东西南北风”。 +

+

+ 第二,我们党领导人民进行社会主义建设,有改革开放前和改革开放后两个历史时期,这是两个相互联系又有重大区别的时期,但本质上都是我们党领导人民进行社会主义建设的实践探索。中国特色社会主义是在改革开放历史新时期开创的,但也是在新中国已经建立起社会主义基本制度并进行了20多年建设的基础上开创的。正确认识这个问题,要把握3个方面。一是,如果没有1978年我们党果断决定实行改革开放,并坚定不移推进改革开放,坚定不移把握改革开放的正确方向,社会主义中国就不可能有今天这样的大好局面,就可能面临严重危机,就可能遇到像苏联、东欧国家那样的亡党亡国危机。同时,如果没有1949年建立新中国并进行社会主义革命和建设,积累了重要的思想、物质、制度条件,积累了正反两方面经验,改革开放也很难顺利推进。二是,虽然这两个历史时期在进行社会主义建设的思想指导、方针政策、实际工作上有很大差别,但两者决不是彼此割裂的,更不是根本对立的。我们党在社会主义建设实践中提出了许多正确主张,当时没有真正落实,改革开放后得到了真正贯彻,将来也还是要坚持和发展的。马克思早就说过:“人们自己创造自己的历史,但是他们并不是随心所欲地创造,并不是在他们自己选定的条件下创造,而是在直接碰到的、既定的、从过去承继下来的条件下创造。”三是,对改革开放前的历史时期要正确评价,不能用改革开放后的历史时期否定改革开放前的历史时期,也不能用改革开放前的历史时期否定改革开放后的历史时期。改革开放前的社会主义实践探索为改革开放后的社会主义实践探索积累了条件,改革开放后的社会主义实践探索是对前一个时期的坚持、改革、发展。对改革开放前的社会主义实践探索,要坚持实事求是的思想路线,分清主流和支流,坚持真理,修正错误,发扬经验,吸取教训,在这个基础上把党和人民事业继续推向前进。 + 我之所以强调这个问题,是因为这个重大政治问题处理不好,就会产生严重政治后果。古人说:“灭人之国,必先去其史。”国内外敌对势力往往就是拿中国革命史、新中国历史来做文章,竭尽攻击、丑化、污蔑之能事,根本目的就是要搞乱人心,煽动推翻中国共产党的领导和我国社会主义制度。苏联为什么解体?苏共为什么垮台?一个重要原因就是意识形态领域的斗争十分激烈,全面否定苏联历史、苏共历史,否定列宁,否定斯大林,搞历史虚无主义,思想搞乱了,各级党组织几乎没任何作用了,军队都不在党的领导之下了。最后,苏联共产党偌大一个党就作鸟兽散了,苏联偌大一个社会主义国家就分崩离析了。这是前车之鉴啊!邓小平同志指出:“毛泽东思想这个旗帜丢不得。丢掉了这个旗帜,实际上就否定了我们党的光辉历史。总的来说,我们党的历史还是光辉的历史。虽然我们党在历史上,包括建国以后的30年中,犯过一些大错误,甚至犯过搞‘文化大革命’这样的大错误,但是我们党终究把革命搞成功了。中国在世界上的地位,是在中华人民共和国成立以后才大大提高的。只有中华人民共和国的成立,才使我们这个人口占世界总人口近1/4的大国,在世界上站起来,而且站住了。”他还强调:“对毛泽东同志的评价,对毛泽东思想的阐述,不是仅仅涉及毛泽东同志个人的问题,这同我们党、我们国家的整个历史是分不开的。要看到这个全局。”“这不只是个理论问题,尤其是个政治问题,是国际国内的很大的政治问题。”这就是一个伟大马克思主义政治家的眼界和胸怀。试想一下,如果当时全盘否定了毛泽东同志,那我们党还能站得住吗?我们国家的社会主义制度还能站得住吗?那就站不住了,站不住就会天下大乱。所以,正确处理改革开放前后的社会主义实践探索的关系,不只是一个历史问题,更主要的是一个政治问题。建议大家把《关于建国以来党的若干历史问题的决议》找出来再看看。 +

+

+ 第三,马克思主义必定随着时代、实践和科学的发展而不断发展,不可能一成不变,社会主义从来都是在开拓中前进的。坚持和发展中国特色社会主义是一篇大文章,邓小平同志为它确定了基本思路和基本原则,以江泽民同志为核心的党的第三代中央领导集体、以胡锦涛同志为总书记的党中央在这篇大文章上都写下了精彩的篇章。现在,我们这一代共产党人的任务,就是继续把这篇大文章写下去。30多年来,中国特色社会主义取得了巨大成就,加之新中国成立以后打下的基础,这是它得以站得住、行得远的重要基础。我们对社会主义的认识,对中国特色社会主义规律的把握,已经达到了一个前所未有的新的高度,这一点不容置疑。同时,也要看到,我国社会主义还处在初级阶段,我们还面临很多没有弄清楚的问题和待解的难题,对许多重大问题的认识和处理都还处在不断深化的过程之中,这一点也不容置疑。对事物的认识是需要一个过程的,而对社会主义这个我们只搞了几十年的东西,我们的认识和把握也还是非常有限的,还需要在实践中不断深化和发展。 +

+ +

+ 坚持马克思主义,坚持社会主义,一定要有发展的观点,一定要以我国改革开放和现代化建设的实际问题、以我们正在做的事情为中心,着眼于马克思主义理论的运用,着眼于对实际问题的理论思考,着眼于新的实践和新的发展。我们说过,世界上没有放之四海而皆准的发展道路和发展模式,也没有一成不变的发展道路和发展模式。我们过去取得的实践和理论成果,能够帮助我们更好面对和解决前进中的问题,但不能成为我们骄傲自满的理由,更不能成为我们继续前进的包袱。我们的事业越前进、越发展,新情况新问题就会越多,面临的风险和挑战就会越多,面对的不可预料的事情就会越多。我们必须增强忧患意识,做到居安思危。解放思想、实事求是、与时俱进,是马克思主义活的灵魂,是我们适应新形势、认识新事物、完成新任务的根本思想武器。全党同志首先是各级领导干部必须坚持马克思主义的发展观点,坚持实践是检验真理的唯一标准,发挥历史的主动性和创造性,清醒认识世情、国情、党情的变和不变,永远要有逢山开路、遇河架桥的精神,锐意进取,大胆探索,敢于和善于分析回答现实生活中和群众思想上迫切需要解决的问题,不断深化改革开放,不断有所发现、有所创造、有所前进,不断推进理论创新、实践创新、制度创新。 +

+

+ 第四,我们党始终坚持共产主义远大理想,共产党员特别是党员领导干部要做共产主义远大理想和中国特色社会主义共同理想的坚定信仰者和忠实践行者。对马克思主义的信仰,对社会主义和共产主义的信念,是共产党人的政治灵魂,是共产党人经受住任何考验的精神支柱。党章明确规定,党的最高理想和最终目标是实现共产主义。党章同时明确规定,中国共产党人追求的共产主义最高理想,只有在社会主义社会充分发展和高度发达的基础上才能实现。想一下子、两下子就进入共产主义,那是不切实际的。邓小平同志说,巩固和发展社会主义制度,还需要一个很长的历史阶段,需要我们几代人、十几代人、甚至几十代人坚持不懈地努力奋斗。几十代人,那是多么长啊!从孔老夫子到现在也不过七十几代人。这样看问题,充分说明了我们中国共产党人政治上的清醒。必须认识到,我们现在的努力以及将来多少代人的持续努力,都是朝着最终实现共产主义这个大目标前进的。同时,必须认识到,实现共产主义是一个非常漫长的历史过程,我们必须立足党在现阶段的奋斗目标,脚踏实地推进我们的事业。如果丢失了我们共产党人的远大目标,就会迷失方向,变成功利主义、实用主义。中国特色社会主义是党的最高纲领和基本纲领的统一。中国特色社会主义的基本纲领,概言之,就是建立富强民主文明和谐的社会主义现代化国家。这既是从我国正处于并将长期处于社会主义初级阶段的基本国情出发的,也没有脱离党的最高理想。我们既要坚定走中国特色社会主义道路的信念,也要胸怀共产主义的崇高理想,矢志不移贯彻执行党在社会主义初级阶段的基本路线和基本纲领,做好当前每一项工作。 +

+ + 返回 +

+ 革命理想高于天。没有远大理想,不是合格的共产党员;离开现实工作而空谈远大理想,也不是合格的共产党员。在我们党90多年的历史中,一代又一代共产党人为了追求民族独立和人民解放,不惜流血牺牲,靠的就是一种信仰,为的就是一个理想。尽管他们也知道,自己追求的理想并不会在自己手中实现,但他们坚信,只要一代又一代人为之持续努力,一代又一代人为此作出牺牲,崇高的理想就一定能实现,正所谓“砍头不要紧,只要主义真”。今天,衡量一名共产党员、一名领导干部是否具有共产主义远大理想,是有客观标准的,那就要看他能否坚持全心全意为人民服务的根本宗旨,能否吃苦在前、享受在后,能否勤奋工作、廉洁奉公,能否为理想而奋不顾身去拼搏、去奋斗、去献出自己的全部精力乃至生命。一切迷惘迟疑的观点,一切及时行乐的思想,一切贪图私利的行为,一切无所作为的作风,都是与此格格不入的。一些人认为共产主义是可望而不可及的,甚至认为是望都望不到、看都看不见的,是虚无缥缈的。这就涉及是唯物史观还是唯心史观的世界观问题。我们一些同志之所以理想渺茫、信仰动摇,根本的就是历史唯物主义观点不牢固。要教育引导广大党员、干部把践行中国特色社会主义共同理想和坚定共产主义远大理想统一起来,做到虔诚而执着、至信而深厚。有了坚定的理想信念,站位就高了,眼界就宽了,心胸就开阔了,就能坚持正确政治方向,在胜利和顺境时不骄傲不急躁,在困难和逆境时不消沉不动摇,经受住各种风险和困难考验,自觉抵御各种腐朽思想的侵蚀,永葆共产党人政治本色。 +

+ + + \ No newline at end of file diff --git a/Day21-30/code/new/web1901/example_of_audio_video.html b/Day21-30/code/new/web1901/example_of_audio_video.html new file mode 100755 index 000000000..26db8fed7 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_audio_video.html @@ -0,0 +1,15 @@ + + + + + 音视频 + + + + + + diff --git a/Day21-30/code/new/web1901/example_of_bom_1.html b/Day21-30/code/new/web1901/example_of_bom_1.html new file mode 100755 index 000000000..5a8d9b521 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_bom_1.html @@ -0,0 +1,33 @@ + + + + + BOM - 延迟跳转 + + +

5秒钟以后自动跳转到百度

+ + + + + diff --git a/Day21-30/code/new/web1901/example_of_bootstrap.html b/Day21-30/code/new/web1901/example_of_bootstrap.html new file mode 100755 index 000000000..64ce1e7a7 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_bootstrap.html @@ -0,0 +1,111 @@ + + + + + + + + +
+
+
+ 140x140 +
+
+

+ h3. 这是一套可视化布局系统. +

+
+
+
+
+ +
+
+
+
+ +
+
+
+ + + + diff --git a/Day21-30/code/new/web1901/example_of_css_1.html b/Day21-30/code/new/web1901/example_of_css_1.html new file mode 100755 index 000000000..b798c8b30 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_css_1.html @@ -0,0 +1,29 @@ + + + + + CSS - 内部和外部样式表 + + + + + + + + +

+

+

+

+

+

+

+

静夜思 - 李白

+ 床前明月光 + + + \ No newline at end of file diff --git a/Day21-30/code/new/web1901/example_of_css_2.html b/Day21-30/code/new/web1901/example_of_css_2.html new file mode 100755 index 000000000..5b34be7b4 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_css_2.html @@ -0,0 +1,35 @@ + + + + + CSS - 优先级 + + + + + + + + + + + +

Hello, world!

+ +

Goodbye world!

+ + diff --git a/Day21-30/code/new/web1901/example_of_css_3.html b/Day21-30/code/new/web1901/example_of_css_3.html new file mode 100755 index 000000000..db582b033 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_css_3.html @@ -0,0 +1,109 @@ + + + + + CSS - 盒子模型 + + + +
+
+ +

Hello, world!

+

静夜思

+
+
+

床前明月光

+

疑似地上霜

+

举头望明月

+

低头思故乡

+
+ + diff --git a/Day21-30/code/new/web1901/example_of_css_4.html b/Day21-30/code/new/web1901/example_of_css_4.html new file mode 100755 index 000000000..ca0eeea89 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_css_4.html @@ -0,0 +1,37 @@ + + + + + CSS - 定位 + + + +
+
+
+ + diff --git a/Day21-30/code/new/web1901/example_of_css_5.html b/Day21-30/code/new/web1901/example_of_css_5.html new file mode 100755 index 000000000..e9486fb72 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_css_5.html @@ -0,0 +1,43 @@ + + + + + CSS - 动画效果 + + + +
+
+
+ + diff --git a/Day21-30/code/new/web1901/example_of_form.html b/Day21-30/code/new/web1901/example_of_form.html new file mode 100755 index 000000000..0a9648916 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_form.html @@ -0,0 +1,106 @@ + + + + + 表单的例子 - 注册 + + + +
+ +
图1. 这是一个图片
+
+
+
+ 用户基本信息 +

+ 用户名: + +

+

+ 密码: + +

+

+ 确认密码: + +

+

+ 性别: + 男 + 女 +

+

+ 爱好: + 阅读 + 旅游 + 美食 + 运动 +

+

+ 省份: + +

+

+ 生日: + +

+
+
+ 用户附加信息 +

+ 邮箱: + +

+

+ 头像: + +

+

+ 自我介绍: + +

+

+ + +

+
+
+

+ +

+ + diff --git a/Day21-30/code/new/web1901/example_of_iframe.html b/Day21-30/code/new/web1901/example_of_iframe.html new file mode 100755 index 000000000..f1e528572 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_iframe.html @@ -0,0 +1,31 @@ + + + + + 内部窗口 + + + + + + + + + + + + +

1

+

2

+

3

+
1
+
2
+
3
+ 1 + 2 + 3 + + + 百度  京东 + + diff --git a/Day21-30/code/new/web1901/example_of_jquery_1.html b/Day21-30/code/new/web1901/example_of_jquery_1.html new file mode 100755 index 000000000..bb542adaf --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_jquery_1.html @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
数据统计表
姓名年龄性别身高体重
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
+
+ + + + +
+ + + + + + diff --git a/Day21-30/code/new/web1901/example_of_jquery_2.html b/Day21-30/code/new/web1901/example_of_jquery_2.html new file mode 100755 index 000000000..bb9100c63 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_jquery_2.html @@ -0,0 +1,86 @@ + + + + + + + + +
+
    +
  • 苹果×
  • +
  • 香蕉×
  • +
  • 火龙果×
  • +
  • 西瓜×
  • +
+ + +
+ + + + diff --git a/Day21-30/code/new/web1901/example_of_jquery_3.html b/Day21-30/code/new/web1901/example_of_jquery_3.html new file mode 100755 index 000000000..58241ae99 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_jquery_3.html @@ -0,0 +1,41 @@ + + + + + Ajax请求 + + + +
+ + + diff --git a/Day21-30/code/new/web1901/example_of_jquery_4.html b/Day21-30/code/new/web1901/example_of_jquery_4.html new file mode 100755 index 000000000..8ee56e8f7 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_jquery_4.html @@ -0,0 +1,45 @@ + + + + + Ajax请求 + + + +
+ + + + diff --git a/Day21-30/code/new/web1901/example_of_jquery_5.html b/Day21-30/code/new/web1901/example_of_jquery_5.html new file mode 100755 index 000000000..b000bec51 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_jquery_5.html @@ -0,0 +1,89 @@ + + + + + Ajax请求 + + + + + + + + + + + + + + + + +
编号姓名职位工资补贴所在部门
+
+    + +
+ + + + diff --git a/Day21-30/code/new/web1901/example_of_js_1.html b/Day21-30/code/new/web1901/example_of_js_1.html new file mode 100755 index 000000000..b79cbe30a --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_js_1.html @@ -0,0 +1,28 @@ + + + + + 猜数字 + + + + + diff --git a/Day21-30/code/new/web1901/example_of_js_2.html b/Day21-30/code/new/web1901/example_of_js_2.html new file mode 100755 index 000000000..ed6ea1363 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_js_2.html @@ -0,0 +1,34 @@ + + + + + 判断闰年 + + + + + diff --git a/Day21-30/code/new/web1901/example_of_js_3.html b/Day21-30/code/new/web1901/example_of_js_3.html new file mode 100755 index 000000000..a5c16a62d --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_js_3.html @@ -0,0 +1,56 @@ + + + + + 双色球随机选号 + + + + + + + + + + diff --git a/Day21-30/code/new/web1901/example_of_js_4.html b/Day21-30/code/new/web1901/example_of_js_4.html new file mode 100755 index 000000000..74782efe7 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_js_4.html @@ -0,0 +1,53 @@ + + + + + 显示时间日期 + + + +
+ + + diff --git a/Day21-30/code/new/web1901/example_of_js_5.html b/Day21-30/code/new/web1901/example_of_js_5.html new file mode 100755 index 000000000..7b7915d54 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_js_5.html @@ -0,0 +1,76 @@ + + + + + 类和对象 + + + + + diff --git a/Day21-30/code/new/web1901/example_of_js_6.html b/Day21-30/code/new/web1901/example_of_js_6.html new file mode 100755 index 000000000..0ffffb0a5 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_js_6.html @@ -0,0 +1,115 @@ + + + + + + + + +
+
    +
  • 苹果×
  • +
  • 香蕉×
  • +
  • 火龙果×
  • +
  • 西瓜×
  • +
+ + +
+ + + diff --git a/Day21-30/code/new/web1901/example_of_js_7.html b/Day21-30/code/new/web1901/example_of_js_7.html new file mode 100755 index 000000000..a8b19c07b --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_js_7.html @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/Day21-30/code/new/web1901/example_of_layout.html b/Day21-30/code/new/web1901/example_of_layout.html new file mode 100755 index 000000000..59415f392 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_layout.html @@ -0,0 +1,72 @@ + + + + + 经典布局 + + + +
+ +
+ + +
+ +
+
+
文章1
+
文章2
+
文章3
+
+ +
+ +
页脚
+
+ + diff --git a/Day21-30/code/new/web1901/example_of_table.html b/Day21-30/code/new/web1901/example_of_table.html new file mode 100755 index 000000000..20daeb353 --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_table.html @@ -0,0 +1,110 @@ + + + + + 表格 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
学生考试成绩表
姓名语文数学英语体育
王大锤908060
王大锤907060
王大锤90807060
+
    +
  • One
  • +
  • Two
  • +
  • Three
  • +
  • Four
  • +
  • Five
  • +
+ + diff --git a/Day21-30/code/new/web1901/example_of_vue_element.html b/Day21-30/code/new/web1901/example_of_vue_element.html new file mode 100755 index 000000000..999cf185f --- /dev/null +++ b/Day21-30/code/new/web1901/example_of_vue_element.html @@ -0,0 +1,127 @@ + + + + + Vue入门 + + + + +
+

员工信息表

+
+ + + + + + + + + +
+ 上一页 + 下一页 +
+
+ + + + + diff --git a/Day21-30/code/new/web1901/fonts/chunkfive.ttf b/Day21-30/code/new/web1901/fonts/chunkfive.ttf new file mode 100755 index 000000000..2dc041a3e Binary files /dev/null and b/Day21-30/code/new/web1901/fonts/chunkfive.ttf differ diff --git a/Day21-30/code/new/web1901/fonts/quicksand.ttf b/Day21-30/code/new/web1901/fonts/quicksand.ttf new file mode 100755 index 000000000..39338fc66 Binary files /dev/null and b/Day21-30/code/new/web1901/fonts/quicksand.ttf differ diff --git a/Day21-30/code/new/web1901/images/a1.jpg b/Day21-30/code/new/web1901/images/a1.jpg new file mode 100755 index 000000000..b4ce07254 Binary files /dev/null and b/Day21-30/code/new/web1901/images/a1.jpg differ diff --git a/Day21-30/code/new/web1901/images/a2.jpg b/Day21-30/code/new/web1901/images/a2.jpg new file mode 100755 index 000000000..a6be447b4 Binary files /dev/null and b/Day21-30/code/new/web1901/images/a2.jpg differ diff --git a/Day21-30/code/new/web1901/images/a3.jpg b/Day21-30/code/new/web1901/images/a3.jpg new file mode 100755 index 000000000..440997467 Binary files /dev/null and b/Day21-30/code/new/web1901/images/a3.jpg differ diff --git a/Day21-30/code/new/web1901/images/add.gif b/Day21-30/code/new/web1901/images/add.gif new file mode 100755 index 000000000..fa20bf3fd Binary files /dev/null and b/Day21-30/code/new/web1901/images/add.gif differ diff --git a/Day21-30/code/new/web1901/images/backdrop.gif b/Day21-30/code/new/web1901/images/backdrop.gif new file mode 100755 index 000000000..0fa15c3fb Binary files /dev/null and b/Day21-30/code/new/web1901/images/backdrop.gif differ diff --git a/Day21-30/code/new/web1901/images/bird.gif b/Day21-30/code/new/web1901/images/bird.gif new file mode 100755 index 000000000..849480169 Binary files /dev/null and b/Day21-30/code/new/web1901/images/bird.gif differ diff --git a/Day21-30/code/new/web1901/images/bok-choi.jpg b/Day21-30/code/new/web1901/images/bok-choi.jpg new file mode 100755 index 000000000..5fa893317 Binary files /dev/null and b/Day21-30/code/new/web1901/images/bok-choi.jpg differ diff --git a/Day21-30/code/new/web1901/images/button-sprite.jpg b/Day21-30/code/new/web1901/images/button-sprite.jpg new file mode 100755 index 000000000..b5e9636a5 Binary files /dev/null and b/Day21-30/code/new/web1901/images/button-sprite.jpg differ diff --git a/Day21-30/code/new/web1901/images/buttons.jpg b/Day21-30/code/new/web1901/images/buttons.jpg new file mode 100755 index 000000000..8efc830aa Binary files /dev/null and b/Day21-30/code/new/web1901/images/buttons.jpg differ diff --git a/Day21-30/code/new/web1901/images/chocolate-islands.jpg b/Day21-30/code/new/web1901/images/chocolate-islands.jpg new file mode 100755 index 000000000..36cd69b34 Binary files /dev/null and b/Day21-30/code/new/web1901/images/chocolate-islands.jpg differ diff --git a/Day21-30/code/new/web1901/images/clavinet.jpg b/Day21-30/code/new/web1901/images/clavinet.jpg new file mode 100755 index 000000000..d4078d6ab Binary files /dev/null and b/Day21-30/code/new/web1901/images/clavinet.jpg differ diff --git a/Day21-30/code/new/web1901/images/dark-wood.jpg b/Day21-30/code/new/web1901/images/dark-wood.jpg new file mode 100755 index 000000000..33ffe9852 Binary files /dev/null and b/Day21-30/code/new/web1901/images/dark-wood.jpg differ diff --git a/Day21-30/code/new/web1901/images/dots.gif b/Day21-30/code/new/web1901/images/dots.gif new file mode 100755 index 000000000..5d4015519 Binary files /dev/null and b/Day21-30/code/new/web1901/images/dots.gif differ diff --git a/Day21-30/code/new/web1901/images/email.png b/Day21-30/code/new/web1901/images/email.png new file mode 100755 index 000000000..c258e8671 Binary files /dev/null and b/Day21-30/code/new/web1901/images/email.png differ diff --git a/Day21-30/code/new/web1901/images/header.gif b/Day21-30/code/new/web1901/images/header.gif new file mode 100755 index 000000000..b2bca3a38 Binary files /dev/null and b/Day21-30/code/new/web1901/images/header.gif differ diff --git a/Day21-30/code/new/web1901/images/header.jpg b/Day21-30/code/new/web1901/images/header.jpg new file mode 100755 index 000000000..8da7f6590 Binary files /dev/null and b/Day21-30/code/new/web1901/images/header.jpg differ diff --git a/Day21-30/code/new/web1901/images/icon-plus.png b/Day21-30/code/new/web1901/images/icon-plus.png new file mode 100755 index 000000000..04f15ce6b Binary files /dev/null and b/Day21-30/code/new/web1901/images/icon-plus.png differ diff --git a/Day21-30/code/new/web1901/images/icon.png b/Day21-30/code/new/web1901/images/icon.png new file mode 100755 index 000000000..ad5889e0d Binary files /dev/null and b/Day21-30/code/new/web1901/images/icon.png differ diff --git a/Day21-30/code/new/web1901/images/icons.jpg b/Day21-30/code/new/web1901/images/icons.jpg new file mode 100755 index 000000000..62cb24c19 Binary files /dev/null and b/Day21-30/code/new/web1901/images/icons.jpg differ diff --git a/Day21-30/code/new/web1901/images/keys.jpg b/Day21-30/code/new/web1901/images/keys.jpg new file mode 100755 index 000000000..da3bc7dca Binary files /dev/null and b/Day21-30/code/new/web1901/images/keys.jpg differ diff --git a/Day21-30/code/new/web1901/images/lemon-posset.jpg b/Day21-30/code/new/web1901/images/lemon-posset.jpg new file mode 100755 index 000000000..f63ca9534 Binary files /dev/null and b/Day21-30/code/new/web1901/images/lemon-posset.jpg differ diff --git a/Day21-30/code/new/web1901/images/logo-1.gif b/Day21-30/code/new/web1901/images/logo-1.gif new file mode 100755 index 000000000..1eb753c95 Binary files /dev/null and b/Day21-30/code/new/web1901/images/logo-1.gif differ diff --git a/Day21-30/code/new/web1901/images/logo-2.gif b/Day21-30/code/new/web1901/images/logo-2.gif new file mode 100755 index 000000000..ecb16ed1c Binary files /dev/null and b/Day21-30/code/new/web1901/images/logo-2.gif differ diff --git a/Day21-30/code/new/web1901/images/logo-3.gif b/Day21-30/code/new/web1901/images/logo-3.gif new file mode 100755 index 000000000..f80d9bc78 Binary files /dev/null and b/Day21-30/code/new/web1901/images/logo-3.gif differ diff --git a/Day21-30/code/new/web1901/images/logo.gif b/Day21-30/code/new/web1901/images/logo.gif new file mode 100755 index 000000000..46d3e3c8c Binary files /dev/null and b/Day21-30/code/new/web1901/images/logo.gif differ diff --git a/Day21-30/code/new/web1901/images/magnolia-large.jpg b/Day21-30/code/new/web1901/images/magnolia-large.jpg new file mode 100755 index 000000000..ba16630a6 Binary files /dev/null and b/Day21-30/code/new/web1901/images/magnolia-large.jpg differ diff --git a/Day21-30/code/new/web1901/images/magnolia-medium.jpg b/Day21-30/code/new/web1901/images/magnolia-medium.jpg new file mode 100755 index 000000000..55df4466b Binary files /dev/null and b/Day21-30/code/new/web1901/images/magnolia-medium.jpg differ diff --git a/Day21-30/code/new/web1901/images/magnolia-small.jpg b/Day21-30/code/new/web1901/images/magnolia-small.jpg new file mode 100755 index 000000000..7e8a9f91a Binary files /dev/null and b/Day21-30/code/new/web1901/images/magnolia-small.jpg differ diff --git a/Day21-30/code/new/web1901/images/otters.jpg b/Day21-30/code/new/web1901/images/otters.jpg new file mode 100755 index 000000000..cebaafed9 Binary files /dev/null and b/Day21-30/code/new/web1901/images/otters.jpg differ diff --git a/Day21-30/code/new/web1901/images/pattern.gif b/Day21-30/code/new/web1901/images/pattern.gif new file mode 100755 index 000000000..cd24c4c6b Binary files /dev/null and b/Day21-30/code/new/web1901/images/pattern.gif differ diff --git a/Day21-30/code/new/web1901/images/picture-1.jpg b/Day21-30/code/new/web1901/images/picture-1.jpg new file mode 100755 index 000000000..9100e3180 Binary files /dev/null and b/Day21-30/code/new/web1901/images/picture-1.jpg differ diff --git a/Day21-30/code/new/web1901/images/picture-2.jpg b/Day21-30/code/new/web1901/images/picture-2.jpg new file mode 100755 index 000000000..f2194e8b6 Binary files /dev/null and b/Day21-30/code/new/web1901/images/picture-2.jpg differ diff --git a/Day21-30/code/new/web1901/images/picture-3.jpg b/Day21-30/code/new/web1901/images/picture-3.jpg new file mode 100755 index 000000000..9489dd092 Binary files /dev/null and b/Day21-30/code/new/web1901/images/picture-3.jpg differ diff --git a/Day21-30/code/new/web1901/images/print-01.jpg b/Day21-30/code/new/web1901/images/print-01.jpg new file mode 100755 index 000000000..be5242d4d Binary files /dev/null and b/Day21-30/code/new/web1901/images/print-01.jpg differ diff --git a/Day21-30/code/new/web1901/images/print-02.jpg b/Day21-30/code/new/web1901/images/print-02.jpg new file mode 100755 index 000000000..84648ce58 Binary files /dev/null and b/Day21-30/code/new/web1901/images/print-02.jpg differ diff --git a/Day21-30/code/new/web1901/images/print-03.jpg b/Day21-30/code/new/web1901/images/print-03.jpg new file mode 100755 index 000000000..1c10abaac Binary files /dev/null and b/Day21-30/code/new/web1901/images/print-03.jpg differ diff --git a/Day21-30/code/new/web1901/images/print-04.jpg b/Day21-30/code/new/web1901/images/print-04.jpg new file mode 100755 index 000000000..e28fcba2a Binary files /dev/null and b/Day21-30/code/new/web1901/images/print-04.jpg differ diff --git a/Day21-30/code/new/web1901/images/print-05.jpg b/Day21-30/code/new/web1901/images/print-05.jpg new file mode 100755 index 000000000..d23c32bfa Binary files /dev/null and b/Day21-30/code/new/web1901/images/print-05.jpg differ diff --git a/Day21-30/code/new/web1901/images/print-06.jpg b/Day21-30/code/new/web1901/images/print-06.jpg new file mode 100755 index 000000000..6ff6ccb3d Binary files /dev/null and b/Day21-30/code/new/web1901/images/print-06.jpg differ diff --git a/Day21-30/code/new/web1901/images/puppy.jpg b/Day21-30/code/new/web1901/images/puppy.jpg new file mode 100755 index 000000000..abea7c36e Binary files /dev/null and b/Day21-30/code/new/web1901/images/puppy.jpg differ diff --git a/Day21-30/code/new/web1901/images/python-logo.png b/Day21-30/code/new/web1901/images/python-logo.png new file mode 100755 index 000000000..68ef60f9a Binary files /dev/null and b/Day21-30/code/new/web1901/images/python-logo.png differ diff --git a/Day21-30/code/new/web1901/images/quokka.jpg b/Day21-30/code/new/web1901/images/quokka.jpg new file mode 100755 index 000000000..a7d801ccb Binary files /dev/null and b/Day21-30/code/new/web1901/images/quokka.jpg differ diff --git a/Day21-30/code/new/web1901/images/rhodes.jpg b/Day21-30/code/new/web1901/images/rhodes.jpg new file mode 100755 index 000000000..4d5dc696e Binary files /dev/null and b/Day21-30/code/new/web1901/images/rhodes.jpg differ diff --git a/Day21-30/code/new/web1901/images/roasted-brussel-sprouts.jpg b/Day21-30/code/new/web1901/images/roasted-brussel-sprouts.jpg new file mode 100755 index 000000000..e95dae462 Binary files /dev/null and b/Day21-30/code/new/web1901/images/roasted-brussel-sprouts.jpg differ diff --git a/Day21-30/code/new/web1901/images/shadow.png b/Day21-30/code/new/web1901/images/shadow.png new file mode 100755 index 000000000..98ce05f44 Binary files /dev/null and b/Day21-30/code/new/web1901/images/shadow.png differ diff --git a/Day21-30/code/new/web1901/images/slide-1.jpg b/Day21-30/code/new/web1901/images/slide-1.jpg new file mode 100755 index 000000000..6dc6a8512 Binary files /dev/null and b/Day21-30/code/new/web1901/images/slide-1.jpg differ diff --git a/Day21-30/code/new/web1901/images/slide-2.jpg b/Day21-30/code/new/web1901/images/slide-2.jpg new file mode 100755 index 000000000..a631b8754 Binary files /dev/null and b/Day21-30/code/new/web1901/images/slide-2.jpg differ diff --git a/Day21-30/code/new/web1901/images/slide-3.jpg b/Day21-30/code/new/web1901/images/slide-3.jpg new file mode 100755 index 000000000..4fa7315ce Binary files /dev/null and b/Day21-30/code/new/web1901/images/slide-3.jpg differ diff --git a/Day21-30/code/new/web1901/images/slide-4.jpg b/Day21-30/code/new/web1901/images/slide-4.jpg new file mode 100755 index 000000000..78235522e Binary files /dev/null and b/Day21-30/code/new/web1901/images/slide-4.jpg differ diff --git a/Day21-30/code/new/web1901/images/star.png b/Day21-30/code/new/web1901/images/star.png new file mode 100755 index 000000000..9c8849335 Binary files /dev/null and b/Day21-30/code/new/web1901/images/star.png differ diff --git a/Day21-30/code/new/web1901/images/subscribe.jpg b/Day21-30/code/new/web1901/images/subscribe.jpg new file mode 100755 index 000000000..ef7dabd30 Binary files /dev/null and b/Day21-30/code/new/web1901/images/subscribe.jpg differ diff --git a/Day21-30/code/new/web1901/images/teriyaki.jpg b/Day21-30/code/new/web1901/images/teriyaki.jpg new file mode 100755 index 000000000..f8bac47f5 Binary files /dev/null and b/Day21-30/code/new/web1901/images/teriyaki.jpg differ diff --git a/Day21-30/code/new/web1901/images/thumb-1.jpg b/Day21-30/code/new/web1901/images/thumb-1.jpg new file mode 100755 index 000000000..5fc58b7ee Binary files /dev/null and b/Day21-30/code/new/web1901/images/thumb-1.jpg differ diff --git a/Day21-30/code/new/web1901/images/thumb-2.jpg b/Day21-30/code/new/web1901/images/thumb-2.jpg new file mode 100755 index 000000000..54c1821fd Binary files /dev/null and b/Day21-30/code/new/web1901/images/thumb-2.jpg differ diff --git a/Day21-30/code/new/web1901/images/thumb-3.jpg b/Day21-30/code/new/web1901/images/thumb-3.jpg new file mode 100755 index 000000000..b787e4261 Binary files /dev/null and b/Day21-30/code/new/web1901/images/thumb-3.jpg differ diff --git a/Day21-30/code/new/web1901/images/tim.png b/Day21-30/code/new/web1901/images/tim.png new file mode 100755 index 000000000..00ef80ec6 Binary files /dev/null and b/Day21-30/code/new/web1901/images/tim.png differ diff --git a/Day21-30/code/new/web1901/images/title.gif b/Day21-30/code/new/web1901/images/title.gif new file mode 100755 index 000000000..8b3fd41d4 Binary files /dev/null and b/Day21-30/code/new/web1901/images/title.gif differ diff --git a/Day21-30/code/new/web1901/images/tulip.gif b/Day21-30/code/new/web1901/images/tulip.gif new file mode 100755 index 000000000..ebae83c62 Binary files /dev/null and b/Day21-30/code/new/web1901/images/tulip.gif differ diff --git a/Day21-30/code/new/web1901/images/twitter.png b/Day21-30/code/new/web1901/images/twitter.png new file mode 100755 index 000000000..6a7ad5196 Binary files /dev/null and b/Day21-30/code/new/web1901/images/twitter.png differ diff --git a/Day21-30/code/new/web1901/images/web.png b/Day21-30/code/new/web1901/images/web.png new file mode 100755 index 000000000..b0c5f404e Binary files /dev/null and b/Day21-30/code/new/web1901/images/web.png differ diff --git a/Day21-30/code/new/web1901/images/wurlitzer.jpg b/Day21-30/code/new/web1901/images/wurlitzer.jpg new file mode 100755 index 000000000..971c21898 Binary files /dev/null and b/Day21-30/code/new/web1901/images/wurlitzer.jpg differ diff --git a/Day21-30/code/new/web1901/images/zucchini-cake.jpg b/Day21-30/code/new/web1901/images/zucchini-cake.jpg new file mode 100755 index 000000000..2788a0a8e Binary files /dev/null and b/Day21-30/code/new/web1901/images/zucchini-cake.jpg differ diff --git a/Day21-30/code/new/web1901/index.html b/Day21-30/code/new/web1901/index.html new file mode 100755 index 000000000..9faa7cb9f --- /dev/null +++ b/Day21-30/code/new/web1901/index.html @@ -0,0 +1,102 @@ + + + + + + + + + 骆昊的技术专栏 + + + + + + 点击这里给我发消息 + + 革命理想 + 坚持马克思 +

<jackfrued>的博客 - H2O

+
+

骆昊喜欢的网站

+ +
+
+

图片加载失败Python

+
+
+
+
面向对象的编程语言
+
基本概念:类、对象
+
三大支柱:封装、继承、多态
+
+
+
动态弱类型语言,需要Python解释器才能执行
+
+

床     前明月光1

+

疑似地上霜©

+

举头™望明月2

+

低头故乡

+ + + + + + \ No newline at end of file diff --git a/Day21-30/code/new/web1901/js/hello.js b/Day21-30/code/new/web1901/js/hello.js new file mode 100755 index 000000000..d14d4fda9 --- /dev/null +++ b/Day21-30/code/new/web1901/js/hello.js @@ -0,0 +1,6 @@ +function randomColor(opacity=1) { + let r = parseInt(Math.random() * 256) + let g = parseInt(Math.random() * 256) + let b = parseInt(Math.random() * 256) + return `rgba(${r}, ${g}, ${b}, ${opacity})` +} diff --git a/Day41-55/oa/static/js/jquery.min.js b/Day21-30/code/new/web1901/js/jquery.min.js old mode 100644 new mode 100755 similarity index 100% rename from Day41-55/oa/static/js/jquery.min.js rename to Day21-30/code/new/web1901/js/jquery.min.js diff --git a/Day21-30/code/new/web1901/js_practice_1.html b/Day21-30/code/new/web1901/js_practice_1.html new file mode 100755 index 000000000..fc9266426 --- /dev/null +++ b/Day21-30/code/new/web1901/js_practice_1.html @@ -0,0 +1,34 @@ + + + + + 乘法口诀表 + + + + + + diff --git a/Day21-30/code/new/web1901/js_practice_3.html b/Day21-30/code/new/web1901/js_practice_3.html new file mode 100755 index 000000000..e6366bbf7 --- /dev/null +++ b/Day21-30/code/new/web1901/js_practice_3.html @@ -0,0 +1,88 @@ + + + + + 成都机动车限行查询 + + + + +
+

+ + + \ No newline at end of file diff --git a/Day21-30/code/new/web1901/js_practice_4.html b/Day21-30/code/new/web1901/js_practice_4.html new file mode 100755 index 000000000..7e3d9ec19 --- /dev/null +++ b/Day21-30/code/new/web1901/js_practice_4.html @@ -0,0 +1,41 @@ + + + + + 轮播广告 + + + +
+ +
+ + + diff --git a/Day21-30/code/new/web1901/js_practice_5.html b/Day21-30/code/new/web1901/js_practice_5.html new file mode 100755 index 000000000..375305f68 --- /dev/null +++ b/Day21-30/code/new/web1901/js_practice_5.html @@ -0,0 +1,69 @@ + + + + + 给多个元素绑定事件 + + + +
+ + + + + + + + +
+ + + + diff --git a/Day21-30/code/new/web1901/js_practice_6.html b/Day21-30/code/new/web1901/js_practice_6.html new file mode 100755 index 000000000..691bd48f3 --- /dev/null +++ b/Day21-30/code/new/web1901/js_practice_6.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
数据统计表
姓名年龄性别身高体重
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
+
+ + + + +
+
+ + + diff --git a/Day21-30/code/new/web1901/js_practice_7.html b/Day21-30/code/new/web1901/js_practice_7.html new file mode 100755 index 000000000..0a499cc1b --- /dev/null +++ b/Day21-30/code/new/web1901/js_practice_7.html @@ -0,0 +1,62 @@ + + + + + + + + +
+
+ + +
+ + + + + diff --git a/Day21-30/code/new/web1901/problem_of_float.html b/Day21-30/code/new/web1901/problem_of_float.html new file mode 100755 index 000000000..16ef4a3cd --- /dev/null +++ b/Day21-30/code/new/web1901/problem_of_float.html @@ -0,0 +1,58 @@ + + + + Parent Float - Problem + + + +

The Evolution of the Bicycle

+
+

+ In 1817 Baron von Drais invented a walking machine that would help him get around the royal gardens faster. +

+

+ The device know as the Draisienne (or "hobby horse") was made of wood, and propelled by pushing your feed on the + ground in a gliding movement. +

+

+ It was not seen a suitable for any place other than a well maintained pathway. +

+

+ In 1865, the velocipede (meaning "fast foot") attached pedals to the front wheel, but its wooden + structure made it extremely uncomfortable. +

+

+ In 1870 the first all-metal machine appeared. The pedals were attached directly to the front wheel. +

+

+ Solid rubber tires and the long spokes of the large front wheel provided a much smoother ride than its + predecessor. +

+

+ In 1817 Baron von Drais invented a walking machine that would help him get around the royal gardens faster. +

+

+ The device know as the Draisienne (or "hobby horse") was made of wood, and propelled by pushing your feed on the + ground in a gliding movement. +

+ +
+ + diff --git a/Day21-30/code/new/web1901/shopping_cart.html b/Day21-30/code/new/web1901/shopping_cart.html new file mode 100755 index 000000000..651137819 --- /dev/null +++ b/Day21-30/code/new/web1901/shopping_cart.html @@ -0,0 +1,275 @@ + + + + + + + + + +
+
+
+ + +
+
商品
+
单价
+
数量
+
小计
+
操作
+
+
+
+
+ +
+
+ + + +
+
+ + 海澜之家/Heilan Home春装商务白衬衫男修身HNCAD3A067Y 漂白(69) 漂 + +
+
¥138.00
+
+ + + +
+
¥138.00
+
+ 删除 +
+
+
+
+ +
+
+ + + +
+
+ + HLA海澜之家长袖衬衫男牛津纺休闲干净透气HNEAJ1E048A浅灰 + +
+
¥128.00
+
+ + + +
+
¥128.00
+
+ 删除 +
+
+
+
+ +
+
+ + + +
+
+ + HLA海澜之家牛津纺清新休闲衬衫2018春季新品质感柔软长袖衬衫男 + +
+
¥99.00
+
+ + + +
+
¥99.00
+
+ 删除 +
+
+
+ +
+ + + + diff --git a/Day21-30/code/new/web1901/video/puppy.flv b/Day21-30/code/new/web1901/video/puppy.flv new file mode 100755 index 000000000..ac947255d Binary files /dev/null and b/Day21-30/code/new/web1901/video/puppy.flv differ diff --git a/Day21-30/code/new/web1901/video/puppy.mp4 b/Day21-30/code/new/web1901/video/puppy.mp4 new file mode 100755 index 000000000..6176cd1cb Binary files /dev/null and b/Day21-30/code/new/web1901/video/puppy.mp4 differ diff --git a/Day21-30/code/new/web1901/video/puppy.webm b/Day21-30/code/new/web1901/video/puppy.webm new file mode 100755 index 000000000..a1e34b336 Binary files /dev/null and b/Day21-30/code/new/web1901/video/puppy.webm differ diff --git a/Day21-30/classical_layout.html b/Day21-30/code/old/html+css/classical_layout.html old mode 100644 new mode 100755 similarity index 100% rename from Day21-30/classical_layout.html rename to Day21-30/code/old/html+css/classical_layout.html diff --git a/Day21-30/example.html b/Day21-30/code/old/html+css/example.html old mode 100644 new mode 100755 similarity index 93% rename from Day21-30/example.html rename to Day21-30/code/old/html+css/example.html index ae77ce585..1ffdc318d --- a/Day21-30/example.html +++ b/Day21-30/code/old/html+css/example.html @@ -1,80 +1,80 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/Day21-30/form_and_table.html b/Day21-30/code/old/html+css/form_and_table.html old mode 100644 new mode 100755 similarity index 100% rename from Day21-30/form_and_table.html rename to Day21-30/code/old/html+css/form_and_table.html diff --git a/Day21-30/code/old/html+css/qq_link.html b/Day21-30/code/old/html+css/qq_link.html new file mode 100755 index 000000000..b80ead526 --- /dev/null +++ b/Day21-30/code/old/html+css/qq_link.html @@ -0,0 +1,20 @@ + + + + + + + + + 聊我吧 + + + + \ No newline at end of file diff --git a/Day21-30/code/old/javascript/example01.html b/Day21-30/code/old/javascript/example01.html new file mode 100755 index 000000000..44f2408f7 --- /dev/null +++ b/Day21-30/code/old/javascript/example01.html @@ -0,0 +1,88 @@ + + + + + + + + +
+

Hello, world!

+ + + + + diff --git a/Day21-30/code/old/javascript/example02.html b/Day21-30/code/old/javascript/example02.html new file mode 100755 index 000000000..5c032edef --- /dev/null +++ b/Day21-30/code/old/javascript/example02.html @@ -0,0 +1,90 @@ + + + + + 成都机动车限行查询 + + + + +
+

+ + + \ No newline at end of file diff --git a/Day21-30/code/old/javascript/example03.html b/Day21-30/code/old/javascript/example03.html new file mode 100755 index 000000000..826a1e091 --- /dev/null +++ b/Day21-30/code/old/javascript/example03.html @@ -0,0 +1,28 @@ + + + + + 延迟跳转 + + +

5秒钟以后自动跳转到百度

+ + + diff --git a/Day21-30/code/old/javascript/example04.html b/Day21-30/code/old/javascript/example04.html new file mode 100755 index 000000000..cc63f2db7 --- /dev/null +++ b/Day21-30/code/old/javascript/example04.html @@ -0,0 +1,49 @@ + + + + + + + + +
+ +
+ + + diff --git a/Day21-30/code/old/javascript/example05.html b/Day21-30/code/old/javascript/example05.html new file mode 100755 index 000000000..55729bfa1 --- /dev/null +++ b/Day21-30/code/old/javascript/example05.html @@ -0,0 +1,60 @@ + + + + + + + + +
+
+
+
+
+ + + diff --git a/Day21-30/code/old/javascript/example06.html b/Day21-30/code/old/javascript/example06.html new file mode 100755 index 000000000..a54c6ff7d --- /dev/null +++ b/Day21-30/code/old/javascript/example06.html @@ -0,0 +1,61 @@ + + + + + + + + +
+ + + + + + + + +
+ + + diff --git a/Day21-30/code/old/javascript/example07.html b/Day21-30/code/old/javascript/example07.html new file mode 100755 index 000000000..512c9e51d --- /dev/null +++ b/Day21-30/code/old/javascript/example07.html @@ -0,0 +1,109 @@ + + + + + + + + + +
+
    + +
  • 苹果×
  • +
  • 香蕉×
  • +
  • 火龙果×
  • +
  • 西瓜×
  • +
+ + +
+ + + + diff --git a/Day21-30/code/old/javascript/example08.html b/Day21-30/code/old/javascript/example08.html new file mode 100755 index 000000000..e8d453f02 --- /dev/null +++ b/Day21-30/code/old/javascript/example08.html @@ -0,0 +1,55 @@ + + + + + + + + +
+ 此广告位招租 + +
+ + + \ No newline at end of file diff --git a/Day21-30/code/old/javascript/example09.html b/Day21-30/code/old/javascript/example09.html new file mode 100755 index 000000000..1988607cb --- /dev/null +++ b/Day21-30/code/old/javascript/example09.html @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
数据统计表
姓名年龄性别身高体重
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
+
+ + + + +
+ + + + + + + + diff --git a/Day21-30/code/old/javascript/example10.html b/Day21-30/code/old/javascript/example10.html new file mode 100755 index 000000000..441c041ad --- /dev/null +++ b/Day21-30/code/old/javascript/example10.html @@ -0,0 +1,97 @@ + + + + + + + + +
+
    +
  • 苹果×
  • +
  • 香蕉×
  • +
  • 火龙果×
  • +
  • 西瓜×
  • +
+ + +
+ + + + diff --git a/Day21-30/code/old/javascript/example11.html b/Day21-30/code/old/javascript/example11.html new file mode 100755 index 000000000..e0e1f4318 --- /dev/null +++ b/Day21-30/code/old/javascript/example11.html @@ -0,0 +1,57 @@ + + + + + + + + +
+ + + + + + + + + + + diff --git a/Day21-30/code/old/javascript/example12.html b/Day21-30/code/old/javascript/example12.html new file mode 100755 index 000000000..e3a5a1c34 --- /dev/null +++ b/Day21-30/code/old/javascript/example12.html @@ -0,0 +1,49 @@ + + + + + + + + +
+ + + + diff --git a/Day21-30/code/old/javascript/homework01.html b/Day21-30/code/old/javascript/homework01.html new file mode 100755 index 000000000..40472f2b9 --- /dev/null +++ b/Day21-30/code/old/javascript/homework01.html @@ -0,0 +1,88 @@ + + + + + + + + +
+ +
    +
  • +
  • +
  • +
  • +
+
+ + + diff --git a/Day21-30/code/old/javascript/homework02.html b/Day21-30/code/old/javascript/homework02.html new file mode 100755 index 000000000..5d7671ba4 --- /dev/null +++ b/Day21-30/code/old/javascript/homework02.html @@ -0,0 +1,44 @@ + + + + + + + + +
+ 狗屎 +
    +
  • +
  • +
  • +
+
+ + + diff --git a/Day21-30/code/old/javascript/homework03.html b/Day21-30/code/old/javascript/homework03.html new file mode 100755 index 000000000..63cb4af11 --- /dev/null +++ b/Day21-30/code/old/javascript/homework03.html @@ -0,0 +1,75 @@ + + + + + + + + +
+
+ + +
+ + + + diff --git a/Day21-30/code/old/javascript/homework04.html b/Day21-30/code/old/javascript/homework04.html new file mode 100755 index 000000000..dcc61ae04 --- /dev/null +++ b/Day21-30/code/old/javascript/homework04.html @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
数据统计表
姓名年龄性别身高体重
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
Item1Item2Item3Item4Item5
+
+ + + + +
+ + + + diff --git a/Day21-30/code/old/javascript/homework05.html b/Day21-30/code/old/javascript/homework05.html new file mode 100755 index 000000000..641b99cf4 --- /dev/null +++ b/Day21-30/code/old/javascript/homework05.html @@ -0,0 +1,275 @@ + + + + + + + + + +
+
+
+ + +
+
商品
+
单价
+
数量
+
小计
+
操作
+
+
+
+
+ +
+
+ + + +
+
+ + 海澜之家/Heilan Home春装商务白衬衫男修身HNCAD3A067Y 漂白(69) 漂 + +
+
¥138.00
+
+ + + +
+
¥138.00
+
+ 删除 +
+
+
+
+ +
+
+ + + +
+
+ + HLA海澜之家长袖衬衫男牛津纺休闲干净透气HNEAJ1E048A浅灰 + +
+
¥128.00
+
+ + + +
+
¥128.00
+
+ 删除 +
+
+
+
+ +
+
+ + + +
+
+ + HLA海澜之家牛津纺清新休闲衬衫2018春季新品质感柔软长袖衬衫男 + +
+
¥99.00
+
+ + + +
+
¥99.00
+
+ 删除 +
+
+
+ +
+ + + + diff --git a/Day21-30/code/old/javascript/homework06.html b/Day21-30/code/old/javascript/homework06.html new file mode 100755 index 000000000..354b9200e --- /dev/null +++ b/Day21-30/code/old/javascript/homework06.html @@ -0,0 +1,73 @@ + + + + + + + + +
+
+
+ + + + diff --git a/Day21-30/code/old/javascript/homework07.html b/Day21-30/code/old/javascript/homework07.html new file mode 100755 index 000000000..9073ac0e7 --- /dev/null +++ b/Day21-30/code/old/javascript/homework07.html @@ -0,0 +1,73 @@ + + + + + + + + +
+ + +
+
+

+ + + + diff --git a/Day21-30/code/old/javascript/homework08.html b/Day21-30/code/old/javascript/homework08.html new file mode 100755 index 000000000..b6d658325 --- /dev/null +++ b/Day21-30/code/old/javascript/homework08.html @@ -0,0 +1,85 @@ + + + + + + + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ +
+
+ + + 我同意《XYZ服务协议》 +
+
+ + + diff --git a/Day21-30/code/old/javascript/img/a1.jpg b/Day21-30/code/old/javascript/img/a1.jpg new file mode 100755 index 000000000..b4ce07254 Binary files /dev/null and b/Day21-30/code/old/javascript/img/a1.jpg differ diff --git a/Day21-30/code/old/javascript/img/a2.jpg b/Day21-30/code/old/javascript/img/a2.jpg new file mode 100755 index 000000000..a6be447b4 Binary files /dev/null and b/Day21-30/code/old/javascript/img/a2.jpg differ diff --git a/Day21-30/code/old/javascript/img/a3.jpg b/Day21-30/code/old/javascript/img/a3.jpg new file mode 100755 index 000000000..440997467 Binary files /dev/null and b/Day21-30/code/old/javascript/img/a3.jpg differ diff --git a/Day21-30/code/old/javascript/img/picture-1.jpg b/Day21-30/code/old/javascript/img/picture-1.jpg new file mode 100755 index 000000000..9100e3180 Binary files /dev/null and b/Day21-30/code/old/javascript/img/picture-1.jpg differ diff --git a/Day21-30/code/old/javascript/img/picture-2.jpg b/Day21-30/code/old/javascript/img/picture-2.jpg new file mode 100755 index 000000000..f2194e8b6 Binary files /dev/null and b/Day21-30/code/old/javascript/img/picture-2.jpg differ diff --git a/Day21-30/code/old/javascript/img/picture-3.jpg b/Day21-30/code/old/javascript/img/picture-3.jpg new file mode 100755 index 000000000..9489dd092 Binary files /dev/null and b/Day21-30/code/old/javascript/img/picture-3.jpg differ diff --git a/Day21-30/code/old/javascript/img/slide-1.jpg b/Day21-30/code/old/javascript/img/slide-1.jpg new file mode 100755 index 000000000..6dc6a8512 Binary files /dev/null and b/Day21-30/code/old/javascript/img/slide-1.jpg differ diff --git a/Day21-30/code/old/javascript/img/slide-2.jpg b/Day21-30/code/old/javascript/img/slide-2.jpg new file mode 100755 index 000000000..a631b8754 Binary files /dev/null and b/Day21-30/code/old/javascript/img/slide-2.jpg differ diff --git a/Day21-30/code/old/javascript/img/slide-3.jpg b/Day21-30/code/old/javascript/img/slide-3.jpg new file mode 100755 index 000000000..4fa7315ce Binary files /dev/null and b/Day21-30/code/old/javascript/img/slide-3.jpg differ diff --git a/Day21-30/code/old/javascript/img/slide-4.jpg b/Day21-30/code/old/javascript/img/slide-4.jpg new file mode 100755 index 000000000..78235522e Binary files /dev/null and b/Day21-30/code/old/javascript/img/slide-4.jpg differ diff --git a/Day21-30/code/old/javascript/img/thumb-1.jpg b/Day21-30/code/old/javascript/img/thumb-1.jpg new file mode 100755 index 000000000..5fc58b7ee Binary files /dev/null and b/Day21-30/code/old/javascript/img/thumb-1.jpg differ diff --git a/Day21-30/code/old/javascript/img/thumb-2.jpg b/Day21-30/code/old/javascript/img/thumb-2.jpg new file mode 100755 index 000000000..54c1821fd Binary files /dev/null and b/Day21-30/code/old/javascript/img/thumb-2.jpg differ diff --git a/Day21-30/code/old/javascript/img/thumb-3.jpg b/Day21-30/code/old/javascript/img/thumb-3.jpg new file mode 100755 index 000000000..b787e4261 Binary files /dev/null and b/Day21-30/code/old/javascript/img/thumb-3.jpg differ diff --git a/Day21-30/code/old/javascript/index.html b/Day21-30/code/old/javascript/index.html new file mode 100755 index 000000000..8a659113f --- /dev/null +++ b/Day21-30/code/old/javascript/index.html @@ -0,0 +1,57 @@ + + + + + + + + +

JavaScript课堂案例

+
+

Make English as your working language!!!

+

浏览器中的JavaScript:

+
    +
  • ECMAScript: JavaScript语法规范
  • +
  • BOM: 浏览器对象模型(Browser Object Model),把浏览器当成一个对象(window),通过这个对象可以操控浏览器
  • +
  • DOM: 文档对象模型(Document Object Model),把整个页面当成一个对象(document),通过这个对象可以操作整个页面
  • +
+
+

课堂案例

+
    +
  1. 例子1:BOM和DOM的感性认识
  2. +
  3. 例子2:成都机动车限行查询
  4. +
  5. 例子3:延迟跳转到百度
  6. +
  7. + 例子4:轮播广告 + 完整效果请参考作业1 +
  8. +
  9. 例子5:事件冒泡和事件捕获
  10. +
  11. 例子6:获取事件源和访问相关元素
  12. +
  13. 例子7:动态添加和删除元素
  14. +
  15. 例子8:流氓浮动广告
  16. +
  17. 例子9:jQuery实现表格效果
  18. +
  19. 例子10:jQuery实现动态列表
  20. +
  21. 例子11:Ajax加载美女图片(原生JavaScript)
  22. +
  23. 例子12:Ajax加载美女图片(jQuery)
  24. +
+

课后练习

+
    +
  1. + + 练习1:轮播广告 +
  2. +
  3. 练习2:缩略图效果
  4. +
  5. 练习3:闪烁的方块
  6. +
  7. 练习4:表格效果
  8. +
  9. 练习5:购物车效果(仿京东)
  10. +
  11. 练习6:可拖拽的元素
  12. +
  13. 练习7:周公解梦(Ajax)
  14. +
  15. 练习7:表单验证(正则表达式)
  16. +
+ + diff --git a/Day21-30/code/old/javascript/js/jquery.min.js b/Day21-30/code/old/javascript/js/jquery.min.js new file mode 100755 index 000000000..4d9b3a258 --- /dev/null +++ b/Day21-30/code/old/javascript/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + + + \ No newline at end of file diff --git "a/Day21-30/code/\345\236\203\345\234\276\345\210\206\347\261\273\346\237\245\350\257\242/index.html" "b/Day21-30/code/\345\236\203\345\234\276\345\210\206\347\261\273\346\237\245\350\257\242/index.html" new file mode 100755 index 000000000..534ecc974 --- /dev/null +++ "b/Day21-30/code/\345\236\203\345\234\276\345\210\206\347\261\273\346\237\245\350\257\242/index.html" @@ -0,0 +1,120 @@ + + + + + + 垃圾分类查询助手 + + + + + + +
+ +
+ +

没有对应的查询结果

+
+

+ + +    + + {{ result.name }} +    + (预测结果) +

+

说明:{{ result.explain }}

+
+
+
+ + + + + \ No newline at end of file diff --git a/Day21-30/form.html b/Day21-30/form.html deleted file mode 100644 index 609e1d8ac..000000000 --- a/Day21-30/form.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- -
-
- - - 我同意《XYZ服务协议》 -
-
- - - - diff --git a/Day21-30/img/Thumbs.db b/Day21-30/img/Thumbs.db deleted file mode 100644 index cd04faa74..000000000 Binary files a/Day21-30/img/Thumbs.db and /dev/null differ diff --git a/Day21-30/img/a1.jpg b/Day21-30/img/a1.jpg deleted file mode 100644 index eed94cd4a..000000000 Binary files a/Day21-30/img/a1.jpg and /dev/null differ diff --git a/Day21-30/img/a2.jpg b/Day21-30/img/a2.jpg deleted file mode 100644 index dfefd6c74..000000000 Binary files a/Day21-30/img/a2.jpg and /dev/null differ diff --git a/Day21-30/img/a3.jpg b/Day21-30/img/a3.jpg deleted file mode 100644 index 01323ade9..000000000 Binary files a/Day21-30/img/a3.jpg and /dev/null differ diff --git a/Day21-30/jquery3.html b/Day21-30/jquery3.html deleted file mode 100644 index 0d4798bed..000000000 --- a/Day21-30/jquery3.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - -
-
-
- - -
-
商品
-
单价
-
数量
-
小计
-
操作
-
-
-
-
- -
-
- - - -
-
- - 海澜之家/Heilan Home春装商务白衬衫男修身HNCAD3A067Y 漂白(69) 漂 - -
-
¥138.00
-
- - - -
-
¥138.00
-
- 删除 -
-
-
-
- -
-
- - - -
-
- - HLA海澜之家长袖衬衫男牛津纺休闲干净透气HNEAJ1E048A浅灰 - -
-
¥128.00
-
- - - -
-
¥128.00
-
- 删除 -
-
-
-
- -
-
- - - -
-
- - HLA海澜之家牛津纺清新休闲衬衫2018春季新品质感柔软长袖衬衫男 - -
-
¥99.00
-
- - - -
-
¥99.00
-
- 删除 -
-
-
- -
- - - - diff --git a/Day21-30/js/jquery.min.js b/Day21-30/js/jquery.min.js deleted file mode 100644 index e83647587..000000000 --- a/Day21-30/js/jquery.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; -}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="
a",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:l.htmlSerialize?[0,"",""]:[1,"X
","
"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("