diff --git a/Day01-15/Day01/code/peppa_pig.py b/Day01-15/Day01/code/peppa_pig.py index d33858a221e2a8380c3a9aa21d7dc12952455d3a..21cda98ee7b93df1860d61974cd8714d876633e6 100644 --- a/Day01-15/Day01/code/peppa_pig.py +++ b/Day01-15/Day01/code/peppa_pig.py @@ -1,93 +1,101 @@ -from turtle import* +""" +绘制小猪佩奇 +""" +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 +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 - left(3) #向左转3度 - forward(a) #向前走a的步长 + if 0 <= i < 30 or 60 <= i <90: + a = a + 0.08 + # 向左转3度 + left(3) + # 向前走 + forward(a) else: - a=a-0.08 + a = a - 0.08 left(3) forward(a) - end_fill()#填充完成 - + end_fill() penup() setheading(90) forward(25) setheading(0) forward(10) pendown() - pencolor(255,155,192)#画笔颜色 + # 设置画笔的颜色(红, 绿, 蓝) + pencolor(255, 155, 192) setheading(10) begin_fill() circle(5) - color(160,82,45)#返回或设置pencolor和fillcolor + color(160, 82, 45) end_fill() - penup() setheading(0) forward(20) pendown() - pencolor(255,155,192) + pencolor(255, 155, 192) setheading(10) begin_fill() circle(5) - color(160,82,45) + color(160, 82, 45) end_fill() -def head(x,y):#头 - color((255,155,192),"pink") +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) + circle(300, -30) + circle(100, -60) + circle(80, -100) + circle(150, -20) + circle(60, -95) setheading(161) - circle(-300,15) + circle(-300, 15) penup() - goto(-100,100) + goto(-100, 100) pendown() setheading(-30) - a=0.4 + a = 0.4 for i in range(60): - if 0<=i<30 or 60<=i<90: - a=a+0.08 + if 0<= i < 30 or 60 <= i < 90: + a = a + 0.08 lt(3) #向左转3度 fd(a) #向前走a的步长 else: - a=a-0.08 + a = a - 0.08 lt(3) fd(a) end_fill() -def ears(x,y): #耳朵 - color((255,155,192),"pink") +def ears(x,y): + """画耳朵""" + color((255, 155, 192), "pink") penup() - goto(x,y) + goto(x, y) pendown() begin_fill() setheading(100) - circle(-50,50) - circle(-10,120) - circle(-50,54) + circle(-50, 50) + circle(-10, 120) + circle(-50, 54) end_fill() - penup() setheading(90) forward(-12) @@ -96,14 +104,15 @@ def ears(x,y): #耳朵 pendown() begin_fill() setheading(100) - circle(-50,50) - circle(-10,120) - circle(-50,56) + circle(-50, 50) + circle(-10, 120) + circle(-50, 56) end_fill() -def eyes(x,y):#眼睛 - color((255,155,192),"white") +def eyes(x,y): + """画眼睛""" + color((255, 155, 192), "white") penup() setheading(90) forward(-20) @@ -113,7 +122,6 @@ def eyes(x,y):#眼睛 begin_fill() circle(15) end_fill() - color("black") penup() setheading(90) @@ -124,8 +132,7 @@ def eyes(x,y):#眼睛 begin_fill() circle(3) end_fill() - - color((255,155,192),"white") + color((255, 155, 192), "white") penup() seth(90) forward(-25) @@ -135,7 +142,6 @@ def eyes(x,y):#眼睛 begin_fill() circle(15) end_fill() - color("black") penup() setheading(90) @@ -148,8 +154,9 @@ def eyes(x,y):#眼睛 end_fill() -def cheek(x,y):#腮 - color((255,155,192)) +def cheek(x,y): + """画脸颊""" + color((255, 155, 192)) penup() goto(x,y) pendown() @@ -159,35 +166,39 @@ def cheek(x,y):#腮 end_fill() -def mouth(x,y): #嘴 - color(239,69,19) +def mouth(x,y): + """画嘴巴""" + color(239, 69, 19) penup() - goto(x,y) + goto(x, y) pendown() setheading(-80) - circle(30,40) - circle(40,80) + circle(30, 40) + circle(40, 80) -def setting(): #参数设置 +def setting(): + """设置参数""" pensize(4) - hideturtle() #使乌龟无形(隐藏) - colormode(255) #将其设置为1.0或255.随后 颜色三元组的r,g,b值必须在0 .. cmode范围内 - color((255,155,192),"pink") - setup(840,500) + # 隐藏海龟 + 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) #嘴 + """主函数""" + setting() + nose(-100, 100) + head(-69, 167) + ears(0, 160) + eyes(0, 140) + cheek(80, 10) + mouth(-20, 30) done() if __name__ == '__main__': - main() + main() diff --git "a/Day01-15/Day01/\345\210\235\350\257\206Python.md" "b/Day01-15/Day01/\345\210\235\350\257\206Python.md" index 7f084d71730d17c5dcd15dd4982008dc287a919d..fbf48aea0ee888bdbf5b0b2c54760c7ce29ccfd8 100644 --- "a/Day01-15/Day01/\345\210\235\350\257\206Python.md" +++ "b/Day01-15/Day01/\345\210\235\350\257\206Python.md" @@ -7,38 +7,38 @@ 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版本的代码。 +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.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 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. 学习曲线低,与其他很多语言比上手更容易。 +2. 学习曲线低,跟其他很多语言相比,Python更容易上手。 3. 开放源代码,拥有强大的社区和生态圈。 -4. 解释型语言,完美的平台可移植性。 -5. 支持两种主流的编程范式,可以使用面向对象和函数式编程。 -6. 可扩展性和可嵌入性,可以调用C/C++代码也可以在C/C++中调用。 +4. 解释型语言,天生具有平台可移植性。 +5. 支持两种主流的编程范式(面向对象编程和函数式编程)都提供了支持。 +6. 可扩展性和可嵌入性,可以调用C/C++代码,也可以在C/C++中调用Python。 7. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。 Python的缺点主要集中在以下几点。 -1. 执行效率低下,因此计算密集型任务可以由C/C++编写。 -2. 代码无法加密,但是现在的公司很多都不是卖软件而是卖服务,这个问题慢慢会淡化。 -3. 在开发时可以选择的框架太多,有选择的地方就有错误。 +1. 执行效率稍低,因此计算密集型任务可以由C/C++编写。 +2. 代码无法加密,但是现在的公司很多都不是卖软件而是卖服务,这个问题会被淡化。 +3. 在开发时可以选择的框架太多(如Web框架就有100多个),有选择的地方就有错误。 #### Python的应用领域 -目前Python在云基础设施、DevOps、网络爬虫开发、数据分析挖掘、机器学习等领域都有着广泛的应用,因此也产生了服务器开发、数据接口开发、自动化运维、科学计算和数据可视化、聊天机器人开发、图像识别和处理等一系列的职位。 +目前Python在云基础设施、DevOps、网络爬虫开发、数据分析挖掘、机器学习等领域都有着广泛的应用,因此也产生了Web后端开发、数据接口开发、自动化运维、自动化测试、科学计算和可视化、数据分析、量化交易、机器人开发、图像识别和处理等一系列的职位。 ### 搭建编程环境 #### 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修复工具进行修复。 +可以在[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环境 @@ -53,15 +53,15 @@ yum -y install wget gcc zlib-devel bzip2-devel openssl-devel ncurses-devel sqlit 下载Python源代码并解压缩到指定目录。 ```Shell -wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz -xz -d Python-3.7.0.tar.xz -tar -xvf Python-3.7.0.tar +wget https://www.python.org/ftp/python/3.7.0/Python-3.7.1.tar.xz +xz -d Python-3.7.1.tar.xz +tar -xvf Python-3.7.1.tar ``` 切换至Python源代码目录并执行下面的命令进行配置和安装。 ```Shell -cd Python-3.7.0 +cd Python-3.7.1 ./configure --prefix=/usr/local/python37 --enable-optimizations make && make install ``` @@ -87,7 +87,7 @@ source .bash_profile #### MacOS环境 -MacOS也是自带了Python 2.x版本的,可以通过[Python的官方网站](https://www.python.org)提供的安装文件(pkg文件)安装3.x的版本。默认安装完成后,可以通过在终端执行python命令来启动2.x版本的Python解释器,可以通过执行python3命令来启动3.x版本的Python解释器,当然也可以通过重新设置软链接来修改启动Python解释器的命令。 +MacOS也是自带了Python 2.x版本的,可以通过[Python的官方网站](https://www.python.org)提供的安装文件(pkg文件)安装3.x的版本。默认安装完成后,可以通过在终端执行python命令来启动2.x版本的Python解释器,可以通过执行python3命令来启动3.x版本的Python解释器。 ### 从终端运行Python程序 @@ -137,7 +137,6 @@ python hello.py Version: 0.1 Author: 骆昊 -Date: 2018-02-26 """ print('hello, world!') @@ -179,8 +178,6 @@ python -m pip install ipython jupyter jupyter notebook ``` -![](./res/python-jupyter-1.png) - ![](./res/python-jupyter-2.png) #### Sublime - 文本编辑神器 @@ -203,13 +200,13 @@ jupyter notebook 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就可以找到安装插件的工具,然后再查找需要的插件。我们推荐大家安装以下几个插件。 +- 安装插件。通过Preference菜单的Package Control或快捷键Ctrl+Shift+P打开命令面板,在面板中输入Install Package就可以找到安装插件的工具,然后再查找需要的插件。我们推荐大家安装以下几个插件: - - SublimeCodeIntel - 代码自动补全工具插件 - - Emmet - 前端开发代码模板插件 - - Git - 版本控制工具插件 - - Python PEP8 Autoformat - PEP8规范自动格式化插件 - - ConvertToUTF8 - 将本地编码转换为UTF-8 + - SublimeCodeIntel - 代码自动补全工具插件。 + - Emmet - 前端开发代码模板插件。 + - Git - 版本控制工具插件。 + - Python PEP8 Autoformat - PEP8规范自动格式化插件。 + - ConvertToUTF8 - 将本地编码转换为UTF-8。 #### PyCharm - Python开发神器 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" index 57a192a190a5b58b91cc9b08c9f9dbd3cc6d6a79..4a0937c42025b233633cc6c9f13a25441e423930 100644 --- "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" @@ -2,7 +2,7 @@ #### 指令和程序 -计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说的中央处理器,它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。我们通常所说的程序实际上就是指令的集合,我们程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。今天我们使用的计算机虽然器件做工越来越精密,处理能力越来越强大,但究其本质来说仍然属于[“冯·诺依曼结构”](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)科普一下。 +计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说的中央处理器,它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。我们通常所说的程序实际上就是指令的集合,我们程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。今天我们使用的计算机虽然器件做工越来越精密,处理能力越来越强大,但究其本质来说仍然属于[“冯·诺依曼结构”](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)科普一下。 ### 变量和类型 @@ -12,7 +12,7 @@ - 浮点型:浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,浮点数除了数学写法(如`123.456`)之外还支持科学计数法(如`1.23456e2`)。 - 字符串型:字符串是以单引号或双引号括起来的任意文本,比如`'hello'`和`"hello"`,字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法,而且可以书写成多行的形式(用三个单引号或三个双引号开头,三个单引号或三个双引号结尾)。 - 布尔型:布尔值只有`True`、`False`两种值,要么是`True`,要么是`False`,在Python中,可以直接用`True`、`False`表示布尔值(请注意大小写),也可以通过布尔运算计算出来(例如`3 < 5`会产生布尔值`True`,而`2 == 1`会产生布尔值`False`)。 -- 复数型:形如`3+5j`,跟数学上的复数表示一样,唯一不同的是虚部的i换成了j。 +- 复数型:形如`3+5j`,跟数学上的复数表示一样,唯一不同的是虚部的`i`换成了`j`。 #### 变量命名 @@ -27,7 +27,7 @@ - 受保护的实例属性用单个下划线开头(后面会讲到)。 - 私有的实例属性用两个下划线开头(后面会讲到)。 -当然,作为一个专业的程序员,给变量(事实上应该是所有的标识符)命名做到见名知意也是非常重要的。 +当然,作为一个专业的程序员,给变量(事实上应该是所有的标识符)命名时做到见名知意也是非常重要的。 #### 变量的使用 @@ -39,7 +39,6 @@ Version: 0.1 Author: 骆昊 -Date: 2018-02-27 """ a = 321 @@ -62,7 +61,6 @@ print(a ** b) Version: 0.1 Author: 骆昊 -Date: 2018-02-27 """ a = int(input('a = ')) @@ -138,7 +136,6 @@ Python支持多种运算符,下表大致按照优先级从高到低的顺序 Version: 0.1 Author: 骆昊 -Date: 2018-02-27 """ a = 5 @@ -178,7 +175,6 @@ F = 1.8C + 32 Version: 0.1 Author: 骆昊 -Date: 2018-02-27 """ f = float(input('请输入华氏温度: ')) @@ -195,7 +191,6 @@ print('%.1f华氏度 = %.1f摄氏度' % (f, c)) Version: 0.1 Author: 骆昊 -Date: 2018-02-27 """ import math @@ -216,7 +211,6 @@ print('面积: %.2f' % area) Version: 0.1 Author: 骆昊 -Date: 2018-02-27 """ year = int(input('请输入年份: ')) 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" index 7aeabd099d80470630e37d4b72d47a9a90f3f5f6..c230cc9bca69f5f13e6e8526f18a42eb12318eb9 100644 --- "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" @@ -14,7 +14,6 @@ Version: 0.1 Author: 骆昊 -Date: 2018-02-28 """ username = input('请输入用户名: ') @@ -44,7 +43,6 @@ f(x) = x + 2 (-1 <= x <= 1) Version: 0.1 Author: 骆昊 -Date: 2018-02-28 """ x = float(input('x = ')) @@ -68,7 +66,6 @@ f(x) = x + 2 (-1 <= x <= 1) Version: 0.1 Author: 骆昊 -Date: 2018-02-28 """ x = float(input('x = ')) @@ -94,7 +91,6 @@ print('f(%.2f) = %.2f' % (x, y)) Version: 0.1 Author: 骆昊 -Date: 2018-02-28 """ value = float(input('请输入长度: ')) @@ -115,7 +111,6 @@ else: Version: 0.1 Author: 骆昊 -Date: 2018-02-28 """ from random import randint @@ -150,7 +145,6 @@ print(result) Version: 0.1 Author: 骆昊 -Date: 2018-02-28 """ score = float(input('请输入成绩: ')) @@ -175,7 +169,6 @@ print('对应的等级是:', grade) Version: 0.1 Author: 骆昊 -Date: 2018-02-28 """ import math @@ -193,7 +186,7 @@ else: ``` > **说明:**上面的代码中使用了`math`模块的`sqrt`函数来计算平方根。用边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。 -#### 练习5:实现一个个人所得税计算器。 +#### 练习5:个人所得税计算器。 ```Python """ @@ -201,7 +194,6 @@ else: Version: 0.1 Author: 骆昊 -Date: 2018-02-28 """ salary = float(input('本月收入: ')) 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" index 9a5e1606bebe5e4ce1dfa95d98f5890a88e4a2bb..a0c19d9adb33a2604aa8c9efc0eaad0a7b2120e8 100644 --- "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" @@ -2,7 +2,7 @@ ### 循环结构的应用场景 -如果在程序中我们需要重复的执行某条或某些指令,例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了,刚才的描述中其实不仅仅有需要重复的动作,还有我们上一个章节讲到的分支结构。再举一个简单的例子,比如在我们的程序中要实现每隔1秒中在屏幕上打印一个"hello, world"这样的字符串并持续一个小时,我们肯定不能够将`print('hello, world')`这句代码写上3600遍,如果真的需要这样做那么我们的工作就太无聊了。因此,我们需要循环结构,使用循环结构我们就可以轻松的控制某件事或者某些事重复、重复、再重复的发生。在Python中构造循环结构有两种做法,一种是`for-in`循环,一种是`while`循环。 +如果在程序中我们需要重复的执行某条或某些指令,例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了,刚才的描述中其实不仅仅有需要重复的动作,还有我们上一个章节讲到的分支结构。再举一个简单的例子,比如在我们的程序中要实现每隔1秒中在屏幕上打印一个"hello, world"这样的字符串并持续一个小时,我们肯定不能够将`print('hello, world')`这句代码写上3600遍,如果真的需要这样做那么编程的工作就太无聊了。因此,我们需要了解一下循环结构,有了循环结构我们就可以轻松的控制某件事或者某些事重复、重复、再重复的发生。在Python中构造循环结构有两种做法,一种是`for-in`循环,一种是`while`循环。 ### for-in循环 @@ -14,7 +14,6 @@ Version: 0.1 Author: 骆昊 -Date: 2018-03-01 """ sum = 0 @@ -37,7 +36,6 @@ print(sum) Version: 0.1 Author: 骆昊 -Date: 2018-03-01 """ sum = 0 @@ -54,7 +52,6 @@ print(sum) Version: 0.1 Author: 骆昊 -Date: 2018-03-01 """ sum = 0 @@ -77,7 +74,6 @@ print(sum) Version: 0.1 Author: 骆昊 -Date: 2018-03-01 """ import random @@ -109,8 +105,6 @@ if counter > 7: Version: 0.1 Author: 骆昊 -Date: 2018-03-01 - """ for i in range(1, 10): @@ -196,7 +190,6 @@ for factor in range(x, 0, -1): Version: 0.1 Author: 骆昊 -Date: 2018-03-01 """ row = int(input('请输入行数: ')) 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" index 9ef6a1b0eb6f1f0661b306b69a1f7b271743e93e..1729888bad0f66c8021b0cf56b4725d9b90e8666 100644 --- "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" @@ -27,7 +27,6 @@ fmn = 1 for num in range(1, m - n + 1): fmn *= num print(fm // fn // fmn) - ``` ### 函数的作用 @@ -134,7 +133,8 @@ def foo(): print('goodbye, world!') -foo() # 输出goodbye, world! +# 下面的代码会输出什么呢? +foo() ``` 当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。 @@ -158,11 +158,13 @@ test.py ```Python from module1 import foo -foo() # 输出hello, world! +# 输出hello, world! +foo() from module2 import foo -foo() # 输出goodbye, world! +# 输出goodbye, world! +foo() ``` 也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。 @@ -185,7 +187,8 @@ test.py from module1 import foo from module2 import foo -foo() # 输出goodbye, world! +# 输出goodbye, world! +foo() ``` test.py @@ -194,7 +197,8 @@ test.py from module2 import foo from module1 import foo -foo() # 输出hello, world! +# 输出hello, world! +foo() ``` 需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是“\_\_main\_\_”。 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" index 89d6ad665633922f04cd51214c4824c19c51d40c..90f348199100af4a7758eab0c138d14cabb1dfd0 100644 --- "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" @@ -42,7 +42,6 @@ def main(): if __name__ == '__main__': main() - ``` 下面是运行程序得到的一次运行结果。 @@ -86,7 +85,6 @@ def main(): if __name__ == '__main__': main() - ``` 在上面的代码中,我们通过`Process`类创建了进程对象,通过`target`参数我们传入一个函数来表示进程启动后要执行的代码,后面的`args`是一个元组,它代表了传递给函数的参数。`Process`对象的`start`方法用来启动进程,而`join`方法表示等待进程执行结束。运行上面的代码可以明显发现两个下载任务“同时”启动了,而且程序的执行时间将大大缩短,不再是两个任务的时间总和。下面是程序的一次执行结果。 @@ -375,7 +373,6 @@ def main(): if __name__ == '__main__': main() - ``` 如果使用多线程将耗时间的任务放到一个独立的线程中执行,这样就不会因为执行耗时间的任务而阻塞了主线程,修改后的代码如下所示。 @@ -424,7 +421,6 @@ def main(): if __name__ == '__main__': main() - ``` #### 例子2:使用多进程对复杂任务进行“分而治之”。 @@ -448,7 +444,6 @@ def main(): if __name__ == '__main__': main() - ``` 在上面的代码中,我故意先去创建了一个列表容器然后填入了100000000个数,这一步其实是比较耗时间的,所以为了公平起见,当我们将这个任务分解到8个进程中去执行的时候,我们暂时也不考虑列表切片操作花费的时间,只是把做运算和合并运算结果的时间统计出来,代码如下所示。 @@ -493,7 +488,6 @@ def main(): if __name__ == '__main__': main() - ``` 比较两段代码的执行结果(在我目前使用的MacBook上,上面的代码需要大概6秒左右的时间,而下面的代码只需要不到1秒的时间,再强调一次我们只是比较了运算的时间,不考虑列表创建及切片操作花费的时间),使用多进程后由于获得了更多的CPU执行时间以及更好的利用了CPU的多核特性,明显的减少了程序的执行时间,而且计算量越大效果越明显。当然,如果愿意还可以将多个进程部署在不同的计算机上,做成分布式进程,具体的做法就是通过multiprocessing.managers模块中提供的管理器将`Queue`对象通过网络共享出来(注册到网络上让其他计算机可以访问),这部分内容也留到爬虫的专题再进行讲解。 \ No newline at end of file diff --git a/Day01-15/Day14/code/chatclient.py b/Day01-15/Day14-A/code/chatclient.py similarity index 100% rename from Day01-15/Day14/code/chatclient.py rename to Day01-15/Day14-A/code/chatclient.py diff --git a/Day01-15/Day14/code/chatserver.py b/Day01-15/Day14-A/code/chatserver.py similarity index 100% rename from Day01-15/Day14/code/chatserver.py rename to Day01-15/Day14-A/code/chatserver.py diff --git a/Day01-15/Day14/code/fileclient.py b/Day01-15/Day14-A/code/fileclient.py similarity index 100% rename from Day01-15/Day14/code/fileclient.py rename to Day01-15/Day14-A/code/fileclient.py diff --git a/Day01-15/Day14/code/fileserver.py b/Day01-15/Day14-A/code/fileserver.py similarity index 100% rename from Day01-15/Day14/code/fileserver.py rename to Day01-15/Day14-A/code/fileserver.py diff --git a/Day01-15/Day14/code/guido.jpg b/Day01-15/Day14-A/code/guido.jpg similarity index 100% rename from Day01-15/Day14/code/guido.jpg rename to Day01-15/Day14-A/code/guido.jpg diff --git a/Day01-15/Day14/code/mmdownloader.py b/Day01-15/Day14-A/code/mmdownloader.py similarity index 100% rename from Day01-15/Day14/code/mmdownloader.py rename to Day01-15/Day14-A/code/mmdownloader.py diff --git a/Day01-15/Day14/code/socket1.py b/Day01-15/Day14-A/code/socket1.py similarity index 100% rename from Day01-15/Day14/code/socket1.py rename to Day01-15/Day14-A/code/socket1.py diff --git a/Day01-15/Day14/code/socket2.py b/Day01-15/Day14-A/code/socket2.py similarity index 100% rename from Day01-15/Day14/code/socket2.py rename to Day01-15/Day14-A/code/socket2.py diff --git a/Day01-15/Day14/code/socket3.py b/Day01-15/Day14-A/code/socket3.py similarity index 100% rename from Day01-15/Day14/code/socket3.py rename to Day01-15/Day14-A/code/socket3.py diff --git a/Day01-15/Day14/code/socket4.py b/Day01-15/Day14-A/code/socket4.py similarity index 100% rename from Day01-15/Day14/code/socket4.py rename to Day01-15/Day14-A/code/socket4.py diff --git a/Day01-15/Day14/code/socket5.py b/Day01-15/Day14-A/code/socket5.py similarity index 100% rename from Day01-15/Day14/code/socket5.py rename to Day01-15/Day14-A/code/socket5.py diff --git a/Day01-15/Day14/code/timeclient.py b/Day01-15/Day14-A/code/timeclient.py similarity index 100% rename from Day01-15/Day14/code/timeclient.py rename to Day01-15/Day14-A/code/timeclient.py diff --git a/Day01-15/Day14/code/timeserver.py b/Day01-15/Day14-A/code/timeserver.py similarity index 100% rename from Day01-15/Day14/code/timeserver.py rename to Day01-15/Day14-A/code/timeserver.py diff --git a/Day01-15/Day14/res/TCP-IP-model.png b/Day01-15/Day14-A/res/TCP-IP-model.png similarity index 100% rename from Day01-15/Day14/res/TCP-IP-model.png rename to Day01-15/Day14-A/res/TCP-IP-model.png diff --git a/Day01-15/Day14/res/after-browser.jpg b/Day01-15/Day14-A/res/after-browser.jpg similarity index 100% rename from Day01-15/Day14/res/after-browser.jpg rename to Day01-15/Day14-A/res/after-browser.jpg diff --git a/Day01-15/Day14/res/arpanet.png b/Day01-15/Day14-A/res/arpanet.png similarity index 100% rename from Day01-15/Day14/res/arpanet.png rename to Day01-15/Day14-A/res/arpanet.png diff --git a/Day01-15/Day14/res/before-browser.jpg b/Day01-15/Day14-A/res/before-browser.jpg similarity index 100% rename from Day01-15/Day14/res/before-browser.jpg rename to Day01-15/Day14-A/res/before-browser.jpg diff --git a/Day01-15/Day14/res/browers.jpg b/Day01-15/Day14-A/res/browers.jpg similarity index 100% rename from Day01-15/Day14/res/browers.jpg rename to Day01-15/Day14-A/res/browers.jpg diff --git a/Day01-15/Day14/res/browser-market-place.jpeg b/Day01-15/Day14-A/res/browser-market-place.jpeg similarity index 100% rename from Day01-15/Day14/res/browser-market-place.jpeg rename to Day01-15/Day14-A/res/browser-market-place.jpeg diff --git a/Day01-15/Day14/res/how-data-is-processed.jpg b/Day01-15/Day14-A/res/how-data-is-processed.jpg similarity index 100% rename from Day01-15/Day14/res/how-data-is-processed.jpg rename to Day01-15/Day14-A/res/how-data-is-processed.jpg diff --git a/Day01-15/Day14/res/osi_rm.gif b/Day01-15/Day14-A/res/osi_rm.gif similarity index 100% rename from Day01-15/Day14/res/osi_rm.gif rename to Day01-15/Day14-A/res/osi_rm.gif diff --git a/Day01-15/Day14/res/osimodel.png b/Day01-15/Day14-A/res/osimodel.png similarity index 100% rename from Day01-15/Day14/res/osimodel.png rename to Day01-15/Day14-A/res/osimodel.png diff --git a/Day01-15/Day14/res/tcpipprotocols.png b/Day01-15/Day14-A/res/tcpipprotocols.png similarity index 100% rename from Day01-15/Day14/res/tcpipprotocols.png rename to Day01-15/Day14-A/res/tcpipprotocols.png diff --git a/Day01-15/Day14/res/telnet.png b/Day01-15/Day14-A/res/telnet.png similarity index 100% rename from Day01-15/Day14/res/telnet.png rename to Day01-15/Day14-A/res/telnet.png 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-A/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" similarity index 99% rename from "Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" rename to "Day01-15/Day14-A/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" index 7f33271ec283aeabc5996572f07fc873f13c662f..5c02b2f9ce6f99373360cca0fc2158eb81a54662 100644 --- "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-A/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" @@ -126,7 +126,6 @@ def main(): if __name__ == '__main__': main() - ``` ### 基于传输层协议的套接字编程 @@ -174,7 +173,6 @@ def main(): if __name__ == '__main__': main() - ``` 运行服务器程序后我们可以通过Windows系统的telnet来访问该服务器,结果如下图所示。 @@ -203,7 +201,6 @@ def main(): if __name__ == '__main__': main() - ``` 需要注意的是,上面的服务器并没有使用多线程或者异步I/O的处理方式,这也就意味着当服务器与一个客户端处于通信状态时,其他的客户端只能排队等待。很显然,这样的服务器并不能满足我们的需求,我们需要的服务器是能够同时接纳和处理多个用户请求的。下面我们来设计一个使用多线程技术处理多个用户请求的服务器,该服务器会向连接到服务器的客户端发送一张图片。 @@ -256,7 +253,6 @@ def main(): if __name__ == '__main__': main() - ``` 客户端代码: @@ -291,7 +287,6 @@ def main(): 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的读者可以自行阅读。 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/Day14-B/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" similarity index 99% rename from "Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" rename to "Day01-15/Day14-B/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" index 36c5aafa2105251ff826676969e5013e5df33fd3..6af7d297ab676a002989a5bea3c188121a717528 100644 --- "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/Day14-B/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" @@ -31,7 +31,6 @@ def main(): if __name__ == '__main__': main() - ``` 如果要发送带有附件的邮件,那么可以按照下面的方式进行操作。 @@ -88,7 +87,6 @@ def main(): if __name__ == '__main__': main() - ``` ### 发送短信 @@ -119,7 +117,5 @@ def main(): if __name__ == '__main__': main() - - ``` diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/excel1.py" b/Day01-15/Day15/code/excel1.py similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/excel1.py" rename to Day01-15/Day15/code/excel1.py diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/excel2.py" b/Day01-15/Day15/code/excel2.py similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/excel2.py" rename to Day01-15/Day15/code/excel2.py diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/pdf1.py" b/Day01-15/Day15/code/pdf1.py similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/pdf1.py" rename to Day01-15/Day15/code/pdf1.py diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/pdf2.py" b/Day01-15/Day15/code/pdf2.py similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/pdf2.py" rename to Day01-15/Day15/code/pdf2.py diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/pillow1.py" b/Day01-15/Day15/code/pillow1.py similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/pillow1.py" rename to Day01-15/Day15/code/pillow1.py diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/res/Python\350\257\276\347\250\213\345\244\247\347\272\262.pdf" "b/Day01-15/Day15/code/res/Python\350\257\276\347\250\213\345\244\247\347\272\262.pdf" similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/res/Python\350\257\276\347\250\213\345\244\247\347\272\262.pdf" rename to "Day01-15/Day15/code/res/Python\350\257\276\347\250\213\345\244\247\347\272\262.pdf" diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/res/guido.jpg" b/Day01-15/Day15/code/res/guido.jpg similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/res/guido.jpg" rename to Day01-15/Day15/code/res/guido.jpg diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/res/\345\255\246\347\224\237\346\230\216\347\273\206\350\241\250.xlsx" "b/Day01-15/Day15/code/res/\345\255\246\347\224\237\346\230\216\347\273\206\350\241\250.xlsx" similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/res/\345\255\246\347\224\237\346\230\216\347\273\206\350\241\250.xlsx" rename to "Day01-15/Day15/code/res/\345\255\246\347\224\237\346\230\216\347\273\206\350\241\250.xlsx" diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/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/Day15/code/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" similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/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" rename to "Day01-15/Day15/code/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" diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/word1.py" b/Day01-15/Day15/code/word1.py similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/word1.py" rename to Day01-15/Day15/code/word1.py diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/word2.py" b/Day01-15/Day15/code/word2.py similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/code/word2.py" rename to Day01-15/Day15/code/word2.py diff --git "a/Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/\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/Day15/\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" similarity index 100% rename from "Day01-15/\345\233\276\345\203\217\345\222\214\345\212\236\345\205\254\346\226\207\346\241\243/\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" rename to "Day01-15/Day15/\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" 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" index 6b853dcf26a5ebc9be57dcffa2816751be7415e1..c292ae62acaca1eaadcfe5474a91e4e9357190ac 100644 --- "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" @@ -1,644 +1,1038 @@ -## Python语言进阶 - -### 数据结构和算法 - -#### 排序算法(冒泡和归并) - -```Python -def bubble_sort(items, comp=lambda x, y: x > y): - """高质量冒泡排序(搅拌排序)""" - 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 -``` - -```Python -def merge_sort(items, comp=lambda x, y: x <= y): - """归并排序(分治法)""" - if len(items) < 2: - return items[:] - mid = len(items) // 2 - left = merge_sort(items[:mid], comp) - right = merge_sort(items[mid:], comp) - return merge(left, right, comp) - - -def merge(items1, items2, comp=lambda x, y: x <= y): - """合并(将两个有序的列表合并成一个有序的列表)""" - items = [] - idx1, idx2 = 0, 0 - while idx1 < len(items1) and idx2 < len(items2): - if comp(items1[idx1], items2[idx2]): - items.append(items1[idx1]) - idx1 += 1 - else: - items.append(items2[idx2]) - idx2 += 1 - items += items1[idx1:] - items += items2[idx2:] - return items -``` - -```Python -def seq_search(items, key): - """顺序查找""" - for index, item in enumerate(items): - if item == key: - return index - return -1 -``` -#### 查找算法(顺序和折半) - -```Python -def bin_search(items, key): - """折半查找(循环实现)""" - start, end = 0, len(items) - 1 - while start <= end: - mid = (start + end) // 2 - if key > items[mid]: - start = mid + 1 - elif key < items[mid]: - end = mid - 1 - else: - return mid - return -1 -``` - -#### 使用生成式(推导式) - -```Python -prices = { - 'AAPL': 191.88, - 'GOOG': 1186.96, - 'IBM': 149.24, - 'ORCL': 48.44, - 'ACN': 166.89, - 'FB': 208.09, - 'SYMC': 21.29 -} -# 用股票价格大于100元的股票构造一个新的字典 -prices2 = {key: value for key, value in prices.items() if value > 100} -print(prices2) -``` - -#### 嵌套的列表 - -```Python -def main(): - 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) - - -if __name__ == '__main__': - main() -``` - -[Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP - -#### heapq、itertools等的用法 - -```Python -""" -从列表中找出最大的或最小的N个元素 -""" -import heapq - - -def main(): - 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'])) - - -if __name__ == '__main__': - main() -``` - -```Python -""" -排列 / 组合 / 笛卡尔积 -""" -import itertools - - -def main(): - for val in itertools.permutations('ABCD'): - print(val) - print('-' * 50) - for val in itertools.combinations('ABCDE', 3): - print(val) - print('-' * 50) - for val in itertools.product('ABCD', '123'): - print(val) - - -if __name__ == '__main__': - main() -``` - -#### collections模块下的工具类 - -```Python -""" -找出序列中出现次数最多的元素 -""" -from collections import Counter - - -def main(): - 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)) - - -if __name__ == '__main__': - main() -``` - -#### 穷举法、贪婪法、分治法、动态规划 - -```Python -""" -穷举法 - 穷尽所有可能直到找到正确答案 -""" - - -def main(): - # 公鸡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 = 1 - 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 += 1 - - -if __name__ == '__main__': - main() -``` - -```Python -""" -动态规划 - 适用于有重叠子问题和最优子结构性质的问题 -使用动态规划方法所耗时间往往远少于朴素解法(用空间换取时间) -""" - - -def fib(num, temp={}): - """用递归计算Fibonacci数""" - if num in (1, 2): - return 1 - try: - return temp[num] - except KeyError: - temp[num] = fib(num - 1) + fib(num - 2) - return temp[num] -``` - -### 函数的使用方式 - -- 将函数视为“一等公民” - -- 高阶函数的用法(filter、map以及它们的替代品) - -- 位置参数、可变参数、关键字参数、命名关键字参数 - -- 参数的元信息(代码可读性问题) - -- 匿名函数和内联函数的用法(lambda函数) - -- 闭包和作用域问题(LEGB) - -- 装饰器函数(使用装饰器和取消装饰器) - - 输出函数执行时间的装饰器。 - - ```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(object): - - 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 - ``` - - 用装饰器来实现单例模式。 - - ```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 Singleton(object): - pass - ``` - -### 面向对象相关知识 - -- 三大支柱:封装、继承、多态 - - ```Python - """ - 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪+销售额5%提成 - """ - from abc import ABCMeta, abstractmethod - - - class Employee(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 get_salary(self): - return 15000.0 - - - class Programmer(Employee): - """程序员""" - - def __init__(self, name): - self._working_hour = 0 - super().__init__(name) - - @property - def working_hour(self): - """工作时间""" - return self._working_hour - - @working_hour.setter - def working_hour(self, hour): - self._working_hour = hour if hour > 0 else 0 - - def get_salary(self): - return 200.0 * self.working_hour - - - class Salesman(Employee): - """销售员""" - - def __init__(self, name): - self._sales = 0.0 - super().__init__(name) - - @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 1800.0 + self.sales * 0.05 - - - def main(): - emps = [ - Manager('刘备'), Manager('曹操'), Programmer('许褚'), - Salesman('貂蝉'), Salesman('赵云'), Programmer('张辽'), - Programmer('关羽'), Programmer('周瑜') - ] - for emp in emps: - if isinstance(emp, Programmer): - emp.working_hour = int(input('本月工作时间: ')) - elif isinstance(emp, Salesman): - emp.sales = float(input('本月销售额: ')) - print('%s: %.2f元' % (emp.name, emp.get_salary())) - - - if __name__ == '__main__': - main() - ``` - -- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆) - -- 垃圾回收、循环引用和弱引用 - - 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 - list1 = [] - list2 = [] - list1.append(list2) - list2.append(list1) - ``` - - 以下情况会导致垃圾回收: - - - 调用`gc.collect()` - - gc模块的计数器达到阀值 - - 程序退出 - - 如果循环引用中两个对象都定义了`__del__`方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。 - - 也可以通过`weakref`模块构造弱引用的方式来解决循环引用的问题。 - -- 魔法属性和方法(请参考《Python魔法方法指南》) - - 有几个小问题请大家思考: - - - 自定义的对象能不能使用运算符做运算? - - 自定义的对象能不能放到set中?能去重吗? - - 自定义的对象能不能作为dict的键? - - 自定义的对象能不能使用上下文语法? - -- 混入(Mixin) - - ```Python - """ - 限制字典只有在指定的key不存在时才能设置键值对 - MRO - Method Resolution Order - 多重继承时的方法解析顺序 - """ - - - 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(): - dict1 = SetOnceDict() - try: - dict1['username'] = 'jackfrued' - dict1['username'] = 'hellokitty' - dict1['username'] = 'wangdachui' - except KeyError: - pass - print(dict1) - - - if __name__ == '__main__': - main() - ``` - -- 元编程和元类 - - 用元类实现单例模式。 - - ```Python - """ - 通过元类实现单例模式 - """ - - - class SingletonMeta(type): - """单例的元类""" - - def __init__(cls, *args, **kwargs): - cls.__instance = None - super().__init__(*args, **kwargs) - - def __call__(cls, *args, **kwargs): - if cls.__instance is None: - cls.__instance = super().__call__(*args, **kwargs) - return cls.__instance - - - class Singleton(metaclass=SingletonMeta): - """单例类""" - - def __init__(self, name): - self._name = name - from random import randrange - self._value = randrange(100000) - - @property - def name(self): - return self._name - - @property - def value(self): - return self._value - - - def main(): - sin1 = Singleton('Lee') - sin2 = Singleton('Wang') - print(sin1 == sin2) - print(sin1.value, sin2.value) - print(sin1.name, sin2.name) - - - if __name__ == '__main__': - main() - ``` - -### 迭代器和生成器 - -```Python -""" -生成器和迭代器 -""" - - -def fib1(num): - """普通函数""" - a, b = 0, 1 - for _ in range(num): - a, b = b, a + b - return a - - -def fib2(num): - """生成器""" - a, b = 0, 1 - for _ in range(num): - a, b = b, a + b - yield a - - -class Fib3: - """迭代器""" - - 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 fib2(20): - print(val) - print('-' * 50) - for val in Fib3(20): - print(val) - - -if __name__ == '__main__': - main() -``` - -### 并发编程 - -- 多线程和多进程 -- 协程和异步I/O -- concurrent.futures - +## Python语言进程 + +1. 数据结构和算法 + - 排序算法(冒泡和归并)和查找算法(顺序和折半) + + ```Python + def bubble_sort(origin_items, comp=lambda x, y: x > y): + """高质量冒泡排序(搅拌排序)""" + items = origin_items[:] + for i in range(len(items) - 1): + swapped = False + for j in range(len(items) - 1 - i): + if comp(items[j], items[j + 1]): + items[j], items[j + 1] = items[j + 1], items[j] + swapped = True + if swapped: + swapped = False + for j in range(len(items) - 2 - i, i, -1): + if comp(items[j - 1], items[j]): + items[j], items[j - 1] = items[j - 1], items[j] + swapped = True + if not swapped: + break + return items + ``` + + ```Python + def merge_sort(items, comp=lambda x, y: x <= y): + """归并排序(分治法)""" + if len(items) < 2: + return items[:] + mid = len(items) // 2 + left = merge_sort(items[:mid], comp) + right = merge_sort(items[mid:], comp) + return merge(left, right, comp) + + + def merge(items1, items2, comp): + """合并(将两个有序的列表合并成一个有序的列表)""" + items = [] + idx1, idx2 = 0, 0 + while idx1 < len(items1) and idx2 < len(items2): + if comp(items1[idx1], items2[idx2]): + items.append(items1[idx1]) + idx1 += 1 + else: + items.append(items2[idx2]) + idx2 += 1 + items += items1[idx1:] + items += items2[idx2:] + return items + ``` + + ```Python + def seq_search(items, key): + """顺序查找""" + for index, item in enumerate(items): + if item == key: + return index + return -1 + ``` + + ```Python + def bin_search(items, key): + """折半查找""" + start, end = 0, len(items) - 1 + while start <= end: + mid = (start + end) // 2 + if key > items[mid]: + start = mid + 1 + elif key < items[mid]: + end = mid - 1 + else: + return mid + return -1 + ``` + + - 使用生成式(推导式)语法 + + ```Python + prices = { + 'AAPL': 191.88, + 'GOOG': 1186.96, + 'IBM': 149.24, + 'ORCL': 48.44, + 'ACN': 166.89, + 'FB': 208.09, + 'SYMC': 21.29 + } + # 用股票价格大于100元的股票构造一个新的字典 + prices2 = {key: value for key, value in prices.items() if value > 100} + print(prices2) + ``` + + - 嵌套的列表 + + ```Python + names = ['关羽', '张飞', '赵云', '马超', '黄忠'] + courses = ['语文', '数学', '英语'] + # 录入五个学生三门课程的成绩 + # 错误 - 参考http://pythontutor.com/visualize.html#mode=edit + # scores = [[None] * len(courses)] * len(names) + scores = [[None] * len(courses) for _ in range(len(names))] + for row, name in enumerate(names): + for col, course in enumerate(courses): + scores[row][col] = float(input(f'请输入{name}的{course}成绩: ')) + print(scores) + ``` + + [Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP + + - heapq、itertools等的用法 + ```Python + """ + 从列表中找出最大的或最小的N个元素 + """ + import heapq + + list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92] + list2 = [ + {'name': 'IBM', 'shares': 100, 'price': 91.1}, + {'name': 'AAPL', 'shares': 50, 'price': 543.22}, + {'name': 'FB', 'shares': 200, 'price': 21.09}, + {'name': 'HPQ', 'shares': 35, 'price': 31.75}, + {'name': 'YHOO', 'shares': 45, 'price': 16.35}, + {'name': 'ACME', 'shares': 75, 'price': 115.65} + ] + print(heapq.nlargest(3, list1)) + print(heapq.nsmallest(3, list1)) + print(heapq.nlargest(2, list2, key=lambda x: x['price'])) + print(heapq.nlargest(2, list2, key=lambda x: x['shares'])) + ``` + + ```Python + """ + 排列 / 组合 / 笛卡尔积 + """ + import itertools + + for val in itertools.permutations('ABCD'): + print(val) + + for val in itertools.combinations('ABCDE', 3): + print(val) + + for val in itertools.product('ABCD', '123'): + print(val) + ``` + + - collections模块下的工具类 + + ```Python + """ + 找出序列中出现次数最多的元素 + """ + from collections import Counter + + words = [ + 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', + 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', + 'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes', + 'look', 'into', 'my', 'eyes', "you're", 'under' + ] + counter = Counter(words) + print(counter.most_common(3)) + ``` + + - 穷举法、贪婪法、分治法、回溯法、动态规划 + + 例子:百钱百鸡和五人分鱼。 + + ```Python + """ + 穷举法 - 穷尽所有可能直到找到正确答案 + """ + + # 公鸡5元一只 母鸡3元一只 小鸡1元三只 + # 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只 + for x in range(20): + for y in range(33): + z = 100 - x - y + if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0: + print(x, y, z) + + # A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉 + # 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份 + # B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份 + # 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼 + fish = 1 + 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 += 1 + ``` + + 例子:斐波拉切数列。 + + ```Python + """ + 动态规划 - 适用于有重叠子问题和最优子结构性质的问题 + 使用动态规划方法所耗时间往往远少于朴素解法(用空间换取时间) + """ + + + def fib(num, temp={}): + """用递归计算Fibonacci数""" + if num in (1, 2): + return 1 + try: + return temp[num] + except KeyError: + temp[num] = fib(num - 1) + fib(num - 2) + return temp[num] + ``` + +2. 函数的使用方式 + + - 将函数视为“一等公民” + + - 函数可以赋值给变量 + - 函数可以作为函数的参数 + - 函数可以作为函数的返回值 + + - 高阶函数的用法(`filter`、`map`以及它们的替代品) + + ```Python + items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, [1, 2, 3, 4, 5, 6, 7, 8, 9]))) + 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(object): + """自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)""" + + def __init__(self, output): + self.output = output + + def __call__(self, func): + + @wraps(func) + def wrapper(*args, **kwargs): + start = time() + result = func(*args, **kwargs) + self.output(func.__name__, time() - start) + return result + + return wrapper + ``` + + > 说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。 + + 例子:用装饰器来实现单例模式。 + + ```Python + from functools import wraps + + + def singleton(cls): + """装饰类的装饰器""" + instances = {} + + @wraps(cls) + def wrapper(*args, **kwargs): + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return wrapper + + + @singleton + class President(object): + """总统(单例类)""" + pass + ``` + + > 说明:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢? + + ```Python + from functools import wraps + + + def singleton(cls): + """线程安全的单例装饰器""" + instances = {} + locker = Lock() + + @wraps(cls) + def wrapper(*args, **kwargs): + if cls not in instances: + with locker: + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return wrapper + ``` + +3. 面向对象相关知识 + + - 三大支柱:封装、继承、多态 + + 例子:工资结算系统。 + + ```Python + """ + 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成 + """ + from abc import ABCMeta, abstractmethod + + + class Employee(metaclass=ABCMeta): + """员工(抽象类)""" + + def __init__(self, name): + self._name = name + + @property + def name(self): + """姓名""" + return self._name + + @abstractmethod + def get_salary(self): + """结算月薪(抽象方法)""" + pass + + + class Manager(Employee): + """部门经理""" + + def get_salary(self): + return 15000.0 + + + class Programmer(Employee): + """程序员""" + + def __init__(self, name): + self._working_hour = 0 + super().__init__(name) + + @property + def working_hour(self): + """工作时间""" + return self._working_hour + + @working_hour.setter + def working_hour(self, hour): + self._working_hour = hour if hour > 0 else 0 + + def get_salary(self): + return 200.0 * self.working_hour + + + class Salesman(Employee): + """销售员""" + + def __init__(self, name): + self._sales = 0.0 + super().__init__(name) + + @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 1800.0 + self.sales * 0.05 + + + class EmployeeFactory(): + """创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)""" + + @staticmethod + def create(emp_type, *args): + """创建员工""" + emp_type = emp_type.upper() + emp = None + if emp_type == 'M': + emp = Manager(*args) + elif emp_type == 'P': + emp = Programmer(*args) + elif emp_type == 'S': + emp = Salesman(*args) + return emp + + + def main(): + """主函数""" + emps = [ + EmployeeFactory.create('M', '曹操'), EmployeeFactory.create('P', '荀彧'), + EmployeeFactory.create('P', '郭嘉'), EmployeeFactory.create('S', '典韦') + ] + for emp in emps: + # 用isinstance函数识别对象引用所引用对象的类型 + if isinstance(emp, Programmer): + emp.working_hour = int(input('本月工作时间: ')) + elif isinstance(emp, Salesman): + emp.sales = float(input('本月销售额: ')) + print('%s: %.2f元' % (emp.name, emp.get_salary())) + + + if __name__ == '__main__': + main() + ``` + + - 类与类之间的关系 + + - is-a关系:继承 + - has-a关系:关联 / 聚合 / 合成 + - use-a关系:依赖 + + 例子:扑克游戏。 + + ```Python + """ + 经验:符号常量总是优于字面常量,枚举类型是定义符号常量的最佳选择 + """ + from enum import Enum, unique + + import random + + + @unique + class Suite(Enum): + """花色""" + + SPADE, HEART, CLUB, DIAMOND = range(4) + + def __lt__(self, other): + return self.value < other.value + + + class Card(object): + """牌""" + + def __init__(self, suite, face): + """初始化方法""" + self.suite = suite + self.face = face + + def show(self): + """显示牌面""" + suites = ['♠️', '♥️', '♣️', '♦️'] + faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'] + return f'{suites[self.suite.value]} {faces[self.face]}' + + def __str__(self): + return self.show() + + def __repr__(self): + return self.show() + + + class Poker(object): + """扑克""" + + 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(object): + """玩家""" + + 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() + ``` + + - 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆) + + - 垃圾回收、循环引用和弱引用 + + Python使用了自动化内存管理,这种管理机制以**引用计数**为基础,同时也引入了**标记-清除**和**分代收集**两种机制为辅的策略。 + + ```C + typedef struct_object { + /* 引用计数 */ + int ob_refcnt; + /* 对象指针 */ + struct_typeobject *ob_type; + } PyObject; + ``` + + ```C + /* 增加引用计数的宏定义 */ + #define Py_INCREF(op) ((op)->ob_refcnt++) + /* 减少引用计数的宏定义 */ + #define Py_DECREF(op) \ //减少计数 + if (--(op)->ob_refcnt != 0) \ + ; \ + else \ + __Py_Dealloc((PyObject *)(op)) + ``` + + 导致引用计数+1的情况: + + - 对象被创建,例如`a = 23` + - 对象被引用,例如`b = a` + - 对象被作为参数,传入到一个函数中,例如`f(a)` + - 对象作为一个元素,存储在容器中,例如`list1 = [a, a]` + + 导致引用计数-1的情况: + + - 对象的别名被显式销毁,例如`del a` + - 对象的别名被赋予新的对象,例如`a = 24` + - 一个对象离开它的作用域,例如f函数执行完毕时,f函数中的局部变量(全局变量不会) + - 对象所在的容器被销毁,或从容器中删除对象 + + 引用计数可能会导致循环引用问题,而循环引用会导致内存泄露,如下面的代码所示。为了解决这个问题,Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。 + + ```Python + # 循环引用会导致内存泄露 - Python除了引用技术还引入了标记清理和分代回收 + # 在Python 3.6以前如果重写__del__魔术方法会导致循环引用处理失效 + # 如果不想造成循环引用可以使用弱引用 + list1 = [] + list2 = [] + list1.append(list2) + list2.append(list1) + ``` + + 以下情况会导致垃圾回收: + + - 调用`gc.collect()` + - gc模块的计数器达到阀值 + - 程序退出 + + 如果循环引用中两个对象都定义了`__del__`方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的`__del__`方法,这个问题在Python 3.6中得到了解决。 + + 也可以通过`weakref`模块构造弱引用的方式来解决循环引用的问题。 + + - 魔法属性和方法(请参考《Python魔法方法指南》) + + 有几个小问题请大家思考: + + - 自定义的对象能不能使用运算符做运算? + - 自定义的对象能不能放到set中?能去重吗? + - 自定义的对象能不能作为dict的键? + - 自定义的对象能不能使用上下文语法? + + - 混入(Mixin) + + 例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。 + + ```Python + class SetOnceMappingMixin: + """自定义混入类""" + __slots__ = () + + def __setitem__(self, key, value): + if key in self: + raise KeyError(str(key) + ' already set') + return super().__setitem__(key, value) + + + class SetOnceDict(SetOnceMappingMixin, dict): + """自定义字典""" + pass + + + my_dict= SetOnceDict() + try: + my_dict['username'] = 'jackfrued' + my_dict['username'] = 'hellokitty' + except KeyError: + pass + print(my_dict) + ``` + + - 元编程和元类 + + 例子:用元类实现单例模式。 + + ```Python + class SingletonMeta(type): + """自定义元类""" + + def __init__(cls, *args, **kwargs): + cls.__instance = None + super().__init__(*args, **kwargs) + + def __call__(cls, *args, **kwargs): + 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 + self.hasher = getattr(__import__('hashlib'), alg.lower())() + + def __call__(self, stream): + return self.to_digest(stream) + + def to_digest(self, stream): + """生成十六进制形式的摘要""" + for buf in iter(lambda: stream.read(self.size), b''): + self.hasher.update(buf) + return self.hasher.hexdigest() + + def main(): + """主函数""" + hasher1 = StreamHasher() + with open('Python-3.7.1.tgz', 'rb') as stream: + print(hasher1.to_digest(stream)) + hasher2 = StreamHasher('sha1') + with open('Python-3.7.1.tgz', 'rb') as stream: + print(hasher2(stream)) + + + if __name__ == '__main__': + main() + ``` + +4. 迭代器和生成器 + + - 和迭代器相关的魔术方法(`__iter__`和`__next__`) + + - 两种创建生成器的方式(生成器表达式和`yield`关键字) + + ```Python + """ + 生成器和迭代器 + """ + + + def fib(num): + """生成器""" + a, b = 0, 1 + for _ in range(num): + a, b = b, a + b + yield a + + + class Fib(object): + """迭代器""" + + def __init__(self, num): + self.num = num + self.a, self.b = 0, 1 + self.idx = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.idx < self.num: + self.a, self.b = self.b, self.a + self.b + self.idx += 1 + return self.a + raise StopIteration() + ``` + +5. 并发编程 + + Python中实现并发编程的三种方案:多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验;坏处在于并发的程序不容易开发和调试,同时对其他程序来说它并不友好。 + + - 多线程:Python中提供了Thread类并辅以Lock、Condition、Event、Semaphore和Barrier。Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。 + + ```Python + """ + 面试题:进程和线程的区别和联系? + 进程 - 操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程 + 线程 - 操作系统分配CPU的基本单位 + 并发编程(concurrent programming) + 1. 提升执行性能 - 让程序中没有因果关系的部分可以并发的执行 + 2. 改善用户体验 - 让耗时间的操作不会造成程序的假死 + """ + import glob + import os + import threading + + from PIL import Image + + PREFIX = 'thumbnails' + + + def generate_thumbnail(infile, size, format='PNG'): + """生成指定图片文件的缩略图""" + file, ext = os.path.splitext(infile) + file = file[file.rfind('/') + 1:] + outfile = f'{PREFIX}/{file}_{size[0]}_{size[1]}.{ext}' + img = Image.open(infile) + img.thumbnail(size, Image.ANTIALIAS) + img.save(outfile, format) + + + def main(): + """主函数""" + if not os.path.exists(PREFIX): + os.mkdir(PREFIX) + for infile in glob.glob('images/*.png'): + for size in (32, 64, 128): + # 创建并启动线程 + threading.Thread( + target=generate_thumbnail, + args=(infile, (size, size)) + ).start() + + + if __name__ == '__main__': + main() + ``` + + 多个线程竞争资源的情况 + + ```Python + """ + 多线程程序如果没有竞争资源处理起来通常也比较简单 + 当多个线程竞争临界资源的时候如果缺乏必要的保护措施就会导致数据错乱 + 说明:临界资源就是被多个线程竞争的资源 + """ + import time + import threading + + from concurrent.futures import ThreadPoolExecutor + + + class Account(object): + """银行账户""" + + def __init__(self): + self.balance = 0.0 + self.lock = threading.Lock() + + def deposit(self, money): + # 通过锁保护临界资源 + with self.lock: + new_balance = self.balance + money + time.sleep(0.001) + self.balance = new_balance + + + def add_money(account, money): + """向指定账户打钱""" + account.deposit(money) + + + class AddMoneyThread(threading.Thread): + """自定义线程类""" + + def __init__(self, account, money): + self.account = account + self.money = money + # 自定义线程的初始化方法中必须调用父类的初始化方法 + super().__init__() + + def run(self): + # 线程启动之后要执行的操作 + self.account.deposit(self.money) + + def main(): + """主函数""" + account = Account() + # 创建线程池 + pool = ThreadPoolExecutor(max_workers=10) + futures = [] + for _ in range(100): + # 创建线程的第1种方式 + # threading.Thread( + # target=add_money, args=(account, 1) + # ).start() + # 创建线程的第2种方式 + # AddMoneyThread(account, 1).start() + # 创建线程的第3种方式 + # 调用线程池中的线程来执行特定的任务 + future = pool.submit(add_money, account, 1) + futures.append(future) + # 关闭线程池 + pool.shutdown() + for future in futures: + future.result() + print(account.balance) + + + if __name__ == '__main__': + main() + ``` + + - 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是Process,其他辅助的类跟threading模块中的类似,进程间共享数据可以使用管道、套接字等,在multiprocessing模块中有一个Queue类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。 + + ```Python + """ + 多进程和进程池的使用 + """ + import concurrent.futures + import math + + PRIMES = [ + 112272535095293, + 112582705942171, + 112272535095293, + 115280095190773, + 115797848077099, + 1099726899285419 + ] + + + 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`对象获得协程执行结果。 \ No newline at end of file diff --git "a/Day31-35/\347\216\251\350\275\254Linux\346\223\215\344\275\234\347\263\273\347\273\237.md" "b/Day31-35/\347\216\251\350\275\254Linux\346\223\215\344\275\234\347\263\273\347\273\237.md" index 829ba2e1aa966932e770a941d35164874cd9f9b6..f96d5be656e499bb5e122c2cd858d8542c307d02 100644 --- "a/Day31-35/\347\216\251\350\275\254Linux\346\223\215\344\275\234\347\263\273\347\273\237.md" +++ "b/Day31-35/\347\216\251\350\275\254Linux\346\223\215\344\275\234\347\263\273\347\273\237.md" @@ -1249,7 +1249,3 @@ build environment: 3. HISTSIZE 4. RANDOM 5. PATH - - - - diff --git "a/Day66-75/01.\347\275\221\347\273\234\347\210\254\350\231\253\345\222\214\347\233\270\345\205\263\345\267\245\345\205\267.md" "b/Day66-75/01.\347\275\221\347\273\234\347\210\254\350\231\253\345\222\214\347\233\270\345\205\263\345\267\245\345\205\267.md" index fd13164b927f405e63f76d7301e3b7322b5a61b7..371f21e9ec0c599db40ca5d538ba473ee11d4782 100644 --- "a/Day66-75/01.\347\275\221\347\273\234\347\210\254\350\231\253\345\222\214\347\233\270\345\205\263\345\267\245\345\205\267.md" +++ "b/Day66-75/01.\347\275\221\347\273\234\347\210\254\350\231\253\345\222\214\347\233\270\345\205\263\345\267\245\345\205\267.md" @@ -115,7 +115,6 @@ HTTP响应(响应行+响应头+空行+消息体): 3. HTTPie:命令行HTTP客户端。 ```Shell - $ http --header http://www.scu.edu.cn HTTP/1.1 200 OK Accept-Ranges: bytes @@ -138,8 +137,6 @@ HTTP响应(响应行+响应头+空行+消息体): 4. BuiltWith:识别网站所用技术的工具。 ```Python - - >>> >>> import builtwith >>> builtwith.parse('http://www.bootcss.com/') {'web-servers': ['Nginx'], 'font-scripts': ['Font Awesome'], 'javascript-frameworks': ['Lo-dash', 'Underscore.js', 'Vue.js', 'Zepto', 'jQuery'], 'web-frameworks': ['Twitter Bootstrap']} @@ -153,8 +150,6 @@ HTTP响应(响应行+响应头+空行+消息体): 5. python-whois:查询网站所有者的工具。 ```Python - - >>> >>> import whois >>> whois.whois('baidu.com') {'domain_name': ['BAIDU.COM', 'baidu.com'], 'registrar': 'MarkMonitor, Inc.', 'whois_server': 'whois.markmonitor.com', 'referral_url': None, 'updated_date': [datetime.datetime(2017, 7, 28, 2, 36, 28), datetime.datetime(2017, 7, 27, 19, 36, 28)], 'creation_date': [datetime.datetime(1999, 10, 11, 11, 5, 17), datetime.datetime(1999, 10, 11, 4, 5, 17)], 'expiration_date': [datetime.datetime(2026, 10, 11, 11, 5, 17), datetime.datetime(2026, 10, 11, 0, 0)], 'name_servers': ['DNS.BAIDU.COM', 'NS2.BAIDU.COM', 'NS3.BAIDU.COM', 'NS4.BAIDU.COM', 'NS7.BAIDU.COM', 'dns.baidu.com', 'ns4.baidu.com', 'ns3.baidu.com', 'ns7.baidu.com', 'ns2.baidu.com'], 'status': ['clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited', 'clientTransferProhibited https://icann.org/epp#clientTransferProhibited', 'clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited', 'serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited', 'serverTransferProhibited https://icann.org/epp#serverTransferProhibited', 'serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited', 'clientUpdateProhibited (https://www.icann.org/epp#clientUpdateProhibited)', 'clientTransferProhibited (https://www.icann.org/epp#clientTransferProhibited)', 'clientDeleteProhibited (https://www.icann.org/epp#clientDeleteProhibited)', 'serverUpdateProhibited (https://www.icann.org/epp#serverUpdateProhibited)', 'serverTransferProhibited (https://www.icann.org/epp#serverTransferProhibited)', 'serverDeleteProhibited (https://www.icann.org/epp#serverDeleteProhibited)'], 'emails': ['abusecomplaints@markmonitor.com', 'whoisrelay@markmonitor.com'], 'dnssec': 'unsigned', 'name': None, 'org': 'Beijing Baidu Netcom Science Technology Co., Ltd.', 'address': None, 'city': None, 'state': 'Beijing', 'zipcode': None, 'country': 'CN'} @@ -195,7 +190,6 @@ HTTP响应(响应行+响应头+空行+消息体): 下面的例子给出了一个从“搜狐体育”上获取NBA新闻标题和链接的爬虫。 ```Python - from urllib.error import URLError from urllib.request import urlopen @@ -304,7 +298,6 @@ if __name__ == '__main__': - 使用未经验证的上下文 ```Python - import ssl request = urllib.request.Request(url='...', headers={...}) @@ -315,10 +308,8 @@ if __name__ == '__main__': - 设置全局的取消证书验证 ```Python - import ssl ssl._create_default_https_context = ssl._create_unverified_context ``` - diff --git "a/Day66-75/02.\346\225\260\346\215\256\351\207\207\351\233\206\345\222\214\350\247\243\346\236\220.md" "b/Day66-75/02.\346\225\260\346\215\256\351\207\207\351\233\206\345\222\214\350\247\243\346\236\220.md" index 9db5901feed7e05c5b828c67726e789eab1643b4..50b532b1eab9d3c74a6116c8539fed2a0faa8fae 100644 --- "a/Day66-75/02.\346\225\260\346\215\256\351\207\207\351\233\206\345\222\214\350\247\243\346\236\220.md" +++ "b/Day66-75/02.\346\225\260\346\215\256\351\207\207\351\233\206\345\222\214\350\247\243\346\236\220.md" @@ -12,7 +12,6 @@ ### HTML页面分析 ```HTML - @@ -136,7 +135,6 @@ pyquery相当于jQuery的Python实现,可以用于解析HTML网页。 ### 实例 - 获取知乎发现上的问题链接 ```Python - from urllib.parse import urljoin import re @@ -168,6 +166,5 @@ def main(): if __name__ == '__main__': main() - ``` diff --git "a/Day66-75/03.\345\255\230\345\202\250\346\225\260\346\215\256.md" "b/Day66-75/03.\345\255\230\345\202\250\346\225\260\346\215\256.md" index 657446cc99403094e166fb78569b8e7009a98d82..2cd04c39e7d063b11fb038c7ba5471671598c066 100644 --- "a/Day66-75/03.\345\255\230\345\202\250\346\225\260\346\215\256.md" +++ "b/Day66-75/03.\345\255\230\345\202\250\346\225\260\346\215\256.md" @@ -20,21 +20,19 @@ Redis是REmote DIctionary Server的缩写,它是一个用ANSI C编写的高性 可以使用Linux系统的包管理工具(如yum)来安装Redis,也可以通过在Redis的[官方网站](https://redis.io/)下载Redis的[源代码](http://download.redis.io/releases/redis-3.2.11.tar.gz)解压缩解归档之后进行构件安装。 ```Shell - -# wget http://download.redis.io/releases/redis-3.2.11.tar.gz -# gunzip redis-3.2.11.tar.gz -# tar -xvf redis-3.2.11.tar -# cd redis-3.2.11 -# make && make install +wget http://download.redis.io/releases/redis-3.2.11.tar.gz +gunzip redis-3.2.11.tar.gz +tar -xvf redis-3.2.11.tar +cd redis-3.2.11 +make && make install ``` 接下来我们将redis-3.2.11目录下的redis.conf配置文件复制到用户主目录下并修改配置文件(如果你对配置文件不是很有把握就不要直接修改而是先复制一份再修改这个副本)。 ```Shell - -# cd .. -# cp redis-3.2.11/redis.conf redis.conf -# vim redis.conf +cd .. +cp redis-3.2.11/redis.conf redis.conf +vim redis.conf ``` 配置将Redis服务绑定到指定的IP地址和端口。 @@ -76,8 +74,7 @@ Redis是REmote DIctionary Server的缩写,它是一个用ANSI C编写的高性 接下来启动Redis服务器,可以将服务器放在后台去运行。 ```Shell - -# redis-server redis.conf & +redis-server redis.conf & _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 3.2.11 (00000000/0) 64 bit @@ -100,8 +97,7 @@ Redis是REmote DIctionary Server的缩写,它是一个用ANSI C编写的高性 接下来,我们尝试用Redis客户端去连接服务器。 ```Shell - -# redis-cli -h 172.18.61.250 -p 6379 +redis-cli -h 172.18.61.250 -p 6379 172.18.61.250:6379> auth 1qaz2wsx OK 172.18.61.250:6379> ping @@ -112,7 +108,6 @@ PONG Redis有着非常丰富的数据类型,也有很多的命令来操作这些数据,具体的内容可以查看[Redis命令参考](http://redisdoc.com/),在这个网站上,除了Redis的命令参考,还有Redis的详细文档,其中包括了通知、事务、主从复制、持久化、哨兵、集群等内容。 ```Shell - 172.18.61.250:6379> set username admin OK 172.18.61.250:6379> get username @@ -166,13 +161,11 @@ OK 可以使用pip安装redis模块。redis模块的核心是名为Redis的类,该类的对象代表一个Redis客户端,通过该客户端可以向Redis服务器发送命令并获取执行的结果。上面我们在Redis客户端中使用的命令基本上就是Redis对象可以接收的消息,所以如果了解了Redis的命令就可以在Python中玩转Redis。 ```Shell - -$ pip3 install redis -$ python3 +pip3 install redis +python3 ``` ```Python - >>> import redis >>> client = redis.Redis(host='1.2.3.4', port=6379, password='1qaz2wsx') >>> client.set('username', 'admin') @@ -202,14 +195,14 @@ MongoDB将数据存储为一个文档,一个文档由一系列的“键值对 可以从MongoDB的[官方下载链接](https://www.mongodb.com/download-center#community)下载MongoDB,官方为Windows系统提供了一个Installer程序,而Linux和MacOS则提供了压缩文件。下面简单说一下Linux系统如何安装和配置MongoDB。 ```Shell +wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-amazon-3.6.5.tgz +gunzip mongodb-linux-x86_64-amazon-3.6.5.tgz +mkdir mongodb-3.6.5 +tar -xvf mongodb-linux-x86_64-amazon-3.6.5.tar --strip-components 1 -C mongodb-3.6.5/ +export PATH=$PATH:~/mongodb-3.6.5/bin +mkdir -p /data/db +mongod --bind_ip 172.18.61.250 -# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-amazon-3.6.5.tgz -# gunzip mongodb-linux-x86_64-amazon-3.6.5.tgz -# mkdir mongodb-3.6.5 -# tar -xvf mongodb-linux-x86_64-amazon-3.6.5.tar --strip-components 1 -C mongodb-3.6.5/ -# export PATH=$PATH:~/mongodb-3.6.5/bin -# mkdir -p /data/db -# mongod --bind_ip 172.18.61.250 2018-06-03T18:03:28.232+0800 I CONTROL [initandlisten] MongoDB starting : pid=1163 port=27017 dbpath=/data/db 64-bit host=iZwz97tbgo9lkabnat2lo8Z 2018-06-03T18:03:28.232+0800 I CONTROL [initandlisten] db version v3.6.5 2018-06-03T18:03:28.232+0800 I CONTROL [initandlisten] git version: a20ecd3e3a174162052ff99913bc2ca9a839d618 @@ -239,7 +232,8 @@ MongoDB将数据存储为一个文档,一个文档由一系列的“键值对 启动服务器后可以使用交互式环境跟服务器通信,如下所示。 ```shell -# mongo --host 172.18.61.250 +mongo --host 172.18.61.250 + MongoDB shell version v3.6.5 connecting to: mongodb://172.18.61.250:27017/ ... @@ -249,7 +243,6 @@ connecting to: mongodb://172.18.61.250:27017/ 1. 查看、创建和删除数据库。 ```JavaScript - > // 显示所有数据库 > show dbs admin 0.000GB @@ -267,7 +260,6 @@ connecting to: mongodb://172.18.61.250:27017/ 2. 创建、删除和查看集合。 ```JavaScript - > // 创建并切换到school数据库 > use school switched to db school @@ -292,7 +284,6 @@ connecting to: mongodb://172.18.61.250:27017/ 3. 文档的CRUD操作。 ```JavaScript - > // 向students集合插入文档 > db.students.insert({stuid: 1001, name: '骆昊', age: 38}) WriteResult({ "nInserted" : 1 }) @@ -394,13 +385,11 @@ connecting to: mongodb://172.18.61.250:27017/ 可以通过pip安装pymongo来实现对MongoDB的操作。 ```Shell - -$ pip3 install pymongo -$ python3 +pip3 install pymongo +python3 ``` ```Python - >>> from pymongo import MongoClient >>> client = MongoClient('mongodb://120.77.222.217:27017') >>> db = client.school @@ -451,7 +440,6 @@ $ python3 ### 实例 - 缓存知乎发现上的链接和页面代码 ```Python - from hashlib import sha1 from urllib.parse import urljoin @@ -501,7 +489,6 @@ def main(): if __name__ == '__main__': main() - ``` diff --git "a/Day66-75/04.\345\271\266\345\217\221\344\270\213\350\275\275.md" "b/Day66-75/04.\345\271\266\345\217\221\344\270\213\350\275\275.md" index f3eab36aad6046ca918c77c4059effe0a60b164f..a5c36c89f82821865ac90259a34212c7de86d533 100644 --- "a/Day66-75/04.\345\271\266\345\217\221\344\270\213\350\275\275.md" +++ "b/Day66-75/04.\345\271\266\345\217\221\344\270\213\350\275\275.md" @@ -44,7 +44,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池 1. 生成器 - 数据的生产者。 ```Python - from time import sleep @@ -70,7 +69,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池 生成器还可以叠加来组成生成器管道,代码如下所示。 ```Python - # Fibonacci数生成器 def fib(): a, b = 0, 1 @@ -94,13 +92,11 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池 if __name__ == '__main__': main() - ``` 2. 协程 - 数据的消费者。 ```Python - from time import sleep @@ -130,13 +126,11 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池 if __name__ == '__main__': main() - ``` > 说明:上面代码中countdown_gen函数中的第1行consumer.send(None)是为了激活生成器,通俗的说就是让生成器执行到有yield关键字的地方挂起,当然也可以通过next(consumer)来达到同样的效果。如果不愿意每次都用这样的代码来“预激”生成器,可以写一个包装器来完成该操作,代码如下所示。 ```Python - from functools import wraps @@ -156,7 +150,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池 3. 异步I/O - 非阻塞式I/O操作。 ```Python - import asyncio @@ -179,13 +172,11 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池 if __name__ == '__main__': main() - ``` -4. `async`和`await`。 +4. `async`和`await`。 ```Python - import asyncio import aiohttp @@ -215,7 +206,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池 if __name__ == '__main__': main() - ``` 上面的代码使用了[AIOHTTP](https://github.com/aio-libs/aiohttp)这个非常著名的第三方库,它实现了HTTP客户端和HTTP服务器的功能,对异步操作提供了非常好的支持,有兴趣可以阅读它的[官方文档](https://aiohttp.readthedocs.io/en/stable/)。 @@ -225,7 +215,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池 下面我们把之间讲的所有知识结合起来,用面向对象的方式实现一个爬取“手机搜狐网”的多线程爬虫。 ```Python - import pickle import zlib from enum import Enum, unique @@ -382,6 +371,5 @@ def main(): if __name__ == '__main__': main() - ``` diff --git "a/Day66-75/05.\350\247\243\346\236\220\345\212\250\346\200\201\345\206\205\345\256\271.md" "b/Day66-75/05.\350\247\243\346\236\220\345\212\250\346\200\201\345\206\205\345\256\271.md" index dc302ee27610fb6025c71441a00ce2536c07ffdf..94344ac6a0be99a4058a677e1979736882acb053 100644 --- "a/Day66-75/05.\350\247\243\346\236\220\345\212\250\346\200\201\345\206\205\345\256\271.md" +++ "b/Day66-75/05.\350\247\243\346\236\220\345\212\250\346\200\201\345\206\205\345\256\271.md" @@ -25,14 +25,12 @@ WebKit的代码始于1998年的KHTML项目,当时它是Konqueror浏览器的 如果没有打算用上面所说的方式来渲染页面并获得动态内容,其实还有一种替代方案就是使用自动化测试工具Selenium,它提供了浏览器自动化的API接口,这样就可以通过操控浏览器来获取动态内容。首先可以使用pip来安装Selenium。 ```Shell - -$ pip3 install selenium +pip3 install selenium ``` 下面以“阿里V任务”的“直播服务”为例,来演示如何使用Selenium获取到动态内容并抓取主播图片。 ```Python - import requests from bs4 import BeautifulSoup @@ -47,13 +45,11 @@ def main(): if __name__ == '__main__': main() - ``` 运行上面的程序会发现没有任何的输出,因为页面的HTML代码上根本找不到``标签。接下来我们使用Selenium来获取到页面上的动态内容,再提取主播图片。 ```Python - from bs4 import BeautifulSoup from selenium import webdriver from selenium.webdriver.common.keys import Keys @@ -69,21 +65,18 @@ def main(): if __name__ == '__main__': main() - ``` 在上面的程序中,我们通过Selenium实现对Chrome浏览器的操控,如果要操控其他的浏览器,可以创对应的浏览器对象,例如Firefox、IE等。运行上面的程序,如果看到如下所示的错误提示,那是说明我们还没有将Chrome浏览器的驱动添加到PATH环境变量中,也没有在程序中指定Chrome浏览器驱动所在的位置。 ```Shell - selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home ``` 为了解决上面的问题,可以到Selenium的[官方网站]()找到浏览器驱动的下载链接并下载需要的驱动,在Linux或macOS系统下可以通过下面的命令来设置PATH环境变量,Windows下配置环境变量也非常简单,不清楚的可以自行了解。 ```Shell - -$ export PATH=$PATH:/Users/Hao/Downloads/Tools/chromedriver/ +export PATH=$PATH:/Users/Hao/Downloads/Tools/chromedriver/ ``` 其中`/Users/Hao/Downloads/Tools/chromedriver/ `就是chromedriver所在的路径。 \ No newline at end of file diff --git "a/Day66-75/07.Scrapy\345\205\245\351\227\250.md" "b/Day66-75/07.Scrapy\345\205\245\351\227\250.md" index 4d156322a9a113170e74a27716ee29a20e5c003c..56ab2d18e0185842ef3897f76b0276e3c6b80069 100644 --- "a/Day66-75/07.Scrapy\345\205\245\351\227\250.md" +++ "b/Day66-75/07.Scrapy\345\205\245\351\227\250.md" @@ -43,13 +43,11 @@ Scrapy的整个数据处理流程由Scrapy引擎进行控制,通常的运转 ```Shell -$ ``` 项目的目录结构如下图所示。 ```Shell - (venv) $ tree . |____ scrapy.cfg @@ -78,7 +76,6 @@ $ 1. 在items.py文件中定义字段,这些字段用来保存数据,方便后续的操作。 ```Python - # -*- coding: utf-8 -*- # Define here the models for your scraped items @@ -102,12 +99,10 @@ $ 2. 在spiders文件夹中编写自己的爬虫。 ```Shell - (venv) $ scrapy genspider movie movie.douban.com --template=crawl ``` ```Python - # -*- coding: utf-8 -*- import scrapy from scrapy.selector import Selector @@ -136,28 +131,24 @@ $ item['classification']= sel.xpath('//span[@property="v:genre"]/text()').extract() item['actor']= sel.xpath('//*[@id="info"]/span[3]/a[1]/text()').extract() return item - ``` > 说明:上面我们通过Scrapy提供的爬虫模板创建了Spider,其中的rules中的LinkExtractor对象会自动完成对新的链接的解析,该对象中有一个名为extract_link的回调方法。Scrapy支持用XPath语法和CSS选择器进行数据解析,对应的方法分别是xpath和css,上面我们使用了XPath语法对页面进行解析,如果不熟悉XPath语法可以看看后面的补充说明。 到这里,我们已经可以通过下面的命令让爬虫运转起来。 ```Shell - (venv)$ scrapy crawl movie ``` 可以在控制台看到爬取到的数据,如果想将这些数据保存到文件中,可以通过`-o`参数来指定文件名,Scrapy支持我们将爬取到的数据导出成JSON、CSV、XML、pickle、marshal等格式。 ```Shell - (venv)$ scrapy crawl moive -o result.json ``` 3. 在pipelines.py中完成对数据进行持久化的操作。 ```Python - # -*- coding: utf-8 -*- # Define your item pipelines here @@ -211,7 +202,6 @@ $ 4. 修改settings.py文件对项目进行配置。 ```Python - # -*- coding: utf-8 -*- # Scrapy settings for douban project @@ -325,7 +315,6 @@ $ XML文件。 ```XML - diff --git "a/Day91-100/\351\241\271\347\233\256\351\203\250\347\275\262\344\270\212\347\272\277\346\214\207\345\215\227.md" "b/Day91-100/\351\241\271\347\233\256\351\203\250\347\275\262\344\270\212\347\272\277\346\214\207\345\215\227.md" new file mode 100644 index 0000000000000000000000000000000000000000..0c4b379ef4f5a81fe433d77ec8ca41ed980ae690 --- /dev/null +++ "b/Day91-100/\351\241\271\347\233\256\351\203\250\347\275\262\344\270\212\347\272\277\346\214\207\345\215\227.md" @@ -0,0 +1,764 @@ +## 项目部署上线指南 + +### 准备上线 + +1. 上线前的检查工作。 + + ```Shell + python manage.py check --deploy + ``` + +2. 将DEBUG设置为False并配置ALLOWED_HOSTS。 + + ```Python + DEBUG = False + ALLOWED_HOSTS = ['*'] + ``` + +3. 安全相关的配置。 + + ```Python + # 保持HTTPS连接的时间 + SECURE_HSTS_SECONDS = 3600 + SECURE_HSTS_INCLUDE_SUBDOMAINS = True + SECURE_HSTS_PRELOAD = True + + # 自动重定向到安全连接 + SECURE_SSL_REDIRECT = True + + # 避免浏览器自作聪明推断内容类型 + SECURE_CONTENT_TYPE_NOSNIFF = True + + # 避免跨站脚本攻击 + SECURE_BROWSER_XSS_FILTER = True + + # COOKIE只能通过HTTPS进行传输 + SESSION_COOKIE_SECURE = True + CSRF_COOKIE_SECURE = True + + # 防止点击劫持攻击手段 - 修改HTTP协议响应头 + # 当前网站是不允许使用