更新了部分文档和代码

pull/25/head
jackfrued 2018-12-22 16:26:07 +08:00
parent 2d08d42fff
commit 321278297f
63 changed files with 1956 additions and 874 deletions

View File

@ -1,37 +1,44 @@
"""
绘制小猪佩奇
"""
from turtle import * from turtle import *
def nose(x,y):#鼻子 def nose(x,y):
penup()#提起笔 """画鼻子"""
goto(x,y)#定位 penup()
pendown()#落笔,开始画 # 将海龟移动到指定的坐标
setheading(-30)#将乌龟的方向设置为to_angle/为数字0-东、90-北、180-西、270-南) goto(x,y)
begin_fill()#准备开始填充图形 pendown()
# 设置海龟的方向0-东、90-北、180-西、270-南)
setheading(-30)
begin_fill()
a = 0.4 a = 0.4
for i in range(120): for i in range(120):
if 0 <= i < 30 or 60 <= i <90: if 0 <= i < 30 or 60 <= i <90:
a = a + 0.08 a = a + 0.08
left(3) #向左转3度 # 向左转3度
forward(a) #向前走a的步长 left(3)
# 向前走
forward(a)
else: else:
a = a - 0.08 a = a - 0.08
left(3) left(3)
forward(a) forward(a)
end_fill()#填充完成 end_fill()
penup() penup()
setheading(90) setheading(90)
forward(25) forward(25)
setheading(0) setheading(0)
forward(10) forward(10)
pendown() pendown()
pencolor(255,155,192)#画笔颜色 # 设置画笔的颜色(红, 绿, 蓝)
pencolor(255, 155, 192)
setheading(10) setheading(10)
begin_fill() begin_fill()
circle(5) circle(5)
color(160,82,45)#返回或设置pencolor和fillcolor color(160, 82, 45)
end_fill() end_fill()
penup() penup()
setheading(0) setheading(0)
forward(20) forward(20)
@ -44,7 +51,8 @@ def nose(x,y):#鼻子
end_fill() end_fill()
def head(x,y):#头 def head(x, y):
"""画头"""
color((255, 155, 192), "pink") color((255, 155, 192), "pink")
penup() penup()
goto(x,y) goto(x,y)
@ -76,7 +84,8 @@ def head(x,y):#头
end_fill() end_fill()
def ears(x,y): #耳朵 def ears(x,y):
"""画耳朵"""
color((255, 155, 192), "pink") color((255, 155, 192), "pink")
penup() penup()
goto(x, y) goto(x, y)
@ -87,7 +96,6 @@ def ears(x,y): #耳朵
circle(-10, 120) circle(-10, 120)
circle(-50, 54) circle(-50, 54)
end_fill() end_fill()
penup() penup()
setheading(90) setheading(90)
forward(-12) forward(-12)
@ -102,7 +110,8 @@ def ears(x,y): #耳朵
end_fill() end_fill()
def eyes(x,y):#眼睛 def eyes(x,y):
"""画眼睛"""
color((255, 155, 192), "white") color((255, 155, 192), "white")
penup() penup()
setheading(90) setheading(90)
@ -113,7 +122,6 @@ def eyes(x,y):#眼睛
begin_fill() begin_fill()
circle(15) circle(15)
end_fill() end_fill()
color("black") color("black")
penup() penup()
setheading(90) setheading(90)
@ -124,7 +132,6 @@ def eyes(x,y):#眼睛
begin_fill() begin_fill()
circle(3) circle(3)
end_fill() end_fill()
color((255, 155, 192), "white") color((255, 155, 192), "white")
penup() penup()
seth(90) seth(90)
@ -135,7 +142,6 @@ def eyes(x,y):#眼睛
begin_fill() begin_fill()
circle(15) circle(15)
end_fill() end_fill()
color("black") color("black")
penup() penup()
setheading(90) setheading(90)
@ -148,7 +154,8 @@ def eyes(x,y):#眼睛
end_fill() end_fill()
def cheek(x,y):#腮 def cheek(x,y):
"""画脸颊"""
color((255, 155, 192)) color((255, 155, 192))
penup() penup()
goto(x,y) goto(x,y)
@ -159,7 +166,8 @@ def cheek(x,y):#腮
end_fill() end_fill()
def mouth(x,y): #嘴 def mouth(x,y):
"""画嘴巴"""
color(239, 69, 19) color(239, 69, 19)
penup() penup()
goto(x, y) goto(x, y)
@ -169,23 +177,26 @@ def mouth(x,y): #嘴
circle(40, 80) circle(40, 80)
def setting(): #参数设置 def setting():
"""设置参数"""
pensize(4) pensize(4)
hideturtle() #使乌龟无形(隐藏) # 隐藏海龟
colormode(255) #将其设置为1.0或255.随后 颜色三元组的rgb值必须在0 .. cmode范围内 hideturtle()
colormode(255)
color((255, 155, 192), "pink") color((255, 155, 192), "pink")
setup(840, 500) setup(840, 500)
speed(10) speed(10)
def main(): def main():
setting() #画布、画笔设置 """主函数"""
nose(-100,100) #鼻子 setting()
head(-69,167) #头 nose(-100, 100)
ears(0,160) #耳朵 head(-69, 167)
eyes(0,140) #眼睛 ears(0, 160)
cheek(80,10) #腮 eyes(0, 140)
mouth(-20,30) #嘴 cheek(80, 10)
mouth(-20, 30)
done() done()

View File

@ -7,38 +7,38 @@
1. 1989年圣诞节Guido von Rossum开始写Python语言的编译器。 1. 1989年圣诞节Guido von Rossum开始写Python语言的编译器。
2. 1991年2月第一个Python编译器同时也是解释器诞生它是用C语言实现的后面又出现了Java和C#实现的版本Jython和IronPython以及PyPy、Brython、Pyston等其他实现可以调用C语言的库函数。在最早的版本中Python已经提供了对“类”“函数”“异常处理”等构造块的支持同时提供了“列表”和“字典”等核心数据类型同时支持以模块为基础的拓展系统。 2. 1991年2月第一个Python编译器同时也是解释器诞生它是用C语言实现的后面又出现了Java和C#实现的版本Jython和IronPython以及PyPy、Brython、Pyston等其他实现可以调用C语言的库函数。在最早的版本中Python已经提供了对“类”“函数”“异常处理”等构造块的支持同时提供了“列表”和“字典”等核心数据类型同时支持以模块为基础的拓展系统。
3. 1994年1月Python 1.0正式发布。 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的整个开发过程更加透明社区对开发进度的影响逐渐扩大生态圈开始慢慢形成。 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版本的代码 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表示大版本号一般当整体重写或出现不向后兼容的改变时增加AB表示功能更新出现新功能时增加BC表示小的改动如修复了某个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表示大版本号一般当整体重写或出现不向后兼容的改变时增加AB表示功能更新出现新功能时增加BC表示小的改动如修复了某个Bug只要有修改就增加C。如果对Python的历史感兴趣可以查看一篇名为[《Python简史》](http://www.cnblogs.com/vamei/archive/2013/02/06/2892628.html)的博文。
#### Python的优缺点 #### Python的优缺点
Python的优点很多简单的可以总结为以下几点。 Python的优点很多简单的可以总结为以下几点。
1. 简单和明确,做一件事只有一种方法。 1. 简单和明确,做一件事只有一种方法。
2. 学习曲线低,与其他很多语言比上手更容易 2. 学习曲线低,跟其他很多语言相比Python更容易上手
3. 开放源代码,拥有强大的社区和生态圈。 3. 开放源代码,拥有强大的社区和生态圈。
4. 解释型语言,完美的平台可移植性。 4. 解释型语言,天生具有平台可移植性。
5. 支持两种主流的编程范式,可以使用面向对象和函数式编程 5. 支持两种主流的编程范式(面向对象编程和函数式编程)都提供了支持
6. 可扩展性和可嵌入性可以调用C/C++代码也可以在C/C++中调用。 6. 可扩展性和可嵌入性可以调用C/C++代码也可以在C/C++中调用Python
7. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。 7. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。
Python的缺点主要集中在以下几点。 Python的缺点主要集中在以下几点。
1. 执行效率低因此计算密集型任务可以由C/C++编写。 1. 执行效率因此计算密集型任务可以由C/C++编写。
2. 代码无法加密,但是现在的公司很多都不是卖软件而是卖服务,这个问题慢慢会淡化。 2. 代码无法加密,但是现在的公司很多都不是卖软件而是卖服务,这个问题会淡化。
3. 在开发时可以选择的框架太多,有选择的地方就有错误。 3. 在开发时可以选择的框架太多如Web框架就有100多个,有选择的地方就有错误。
#### Python的应用领域 #### Python的应用领域
目前Python在云基础设施、DevOps、网络爬虫开发、数据分析挖掘、机器学习等领域都有着广泛的应用因此也产生了服务器开发、数据接口开发、自动化运维、科学计算和数据可视化、聊天机器人开发、图像识别和处理等一系列的职位。 目前Python在云基础设施、DevOps、网络爬虫开发、数据分析挖掘、机器学习等领域都有着广泛的应用因此也产生了Web后端开发、数据接口开发、自动化运维、自动化测试、科学计算和可视化、数据分析、量化交易、机器人开发、图像识别和处理等一系列的职位。
### 搭建编程环境 ### 搭建编程环境
#### Windows环境 #### 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环境 #### Linux环境
@ -53,15 +53,15 @@ yum -y install wget gcc zlib-devel bzip2-devel openssl-devel ncurses-devel sqlit
下载Python源代码并解压缩到指定目录。 下载Python源代码并解压缩到指定目录。
```Shell ```Shell
wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz wget https://www.python.org/ftp/python/3.7.0/Python-3.7.1.tar.xz
xz -d Python-3.7.0.tar.xz xz -d Python-3.7.1.tar.xz
tar -xvf Python-3.7.0.tar tar -xvf Python-3.7.1.tar
``` ```
切换至Python源代码目录并执行下面的命令进行配置和安装。 切换至Python源代码目录并执行下面的命令进行配置和安装。
```Shell ```Shell
cd Python-3.7.0 cd Python-3.7.1
./configure --prefix=/usr/local/python37 --enable-optimizations ./configure --prefix=/usr/local/python37 --enable-optimizations
make && make install make && make install
``` ```
@ -87,7 +87,7 @@ source .bash_profile
#### MacOS环境 #### 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程序 ### 从终端运行Python程序
@ -137,7 +137,6 @@ python hello.py
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-26
""" """
print('hello, world!') print('hello, world!')
@ -179,8 +178,6 @@ python -m pip install ipython jupyter
jupyter notebook jupyter notebook
``` ```
![](./res/python-jupyter-1.png)
![](./res/python-jupyter-2.png) ![](./res/python-jupyter-2.png)
#### Sublime - 文本编辑神器 #### 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') 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 - 代码自动补全工具插件 - SublimeCodeIntel - 代码自动补全工具插件
- Emmet - 前端开发代码模板插件 - Emmet - 前端开发代码模板插件
- Git - 版本控制工具插件 - Git - 版本控制工具插件
- Python PEP8 Autoformat - PEP8规范自动格式化插件 - Python PEP8 Autoformat - PEP8规范自动格式化插件
- ConvertToUTF8 - 将本地编码转换为UTF-8 - ConvertToUTF8 - 将本地编码转换为UTF-8
#### PyCharm - Python开发神器 #### PyCharm - Python开发神器

View File

@ -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`)。 - 浮点型:浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,浮点数除了数学写法(如`123.456`)之外还支持科学计数法(如`1.23456e2`)。
- 字符串型:字符串是以单引号或双引号括起来的任意文本,比如`'hello'`和`"hello"`,字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法而且可以书写成多行的形式用三个单引号或三个双引号开头三个单引号或三个双引号结尾 - 字符串型:字符串是以单引号或双引号括起来的任意文本,比如`'hello'`和`"hello"`,字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法而且可以书写成多行的形式用三个单引号或三个双引号开头三个单引号或三个双引号结尾
- 布尔型:布尔值只有`True`、`False`两种值,要么是`True`,要么是`False`在Python中可以直接用`True`、`False`表示布尔值(请注意大小写),也可以通过布尔运算计算出来(例如`3 < 5``True``2 == 1``False` - 布尔型:布尔值只有`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 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27
""" """
a = 321 a = 321
@ -62,7 +61,6 @@ print(a ** b)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27
""" """
a = int(input('a = ')) a = int(input('a = '))
@ -138,7 +136,6 @@ Python支持多种运算符下表大致按照优先级从高到低的顺序
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27
""" """
a = 5 a = 5
@ -178,7 +175,6 @@ F = 1.8C + 32
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27
""" """
f = float(input('请输入华氏温度: ')) f = float(input('请输入华氏温度: '))
@ -195,7 +191,6 @@ print('%.1f华氏度 = %.1f摄氏度' % (f, c))
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27
""" """
import math import math
@ -216,7 +211,6 @@ print('面积: %.2f' % area)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27
""" """
year = int(input('请输入年份: ')) year = int(input('请输入年份: '))

View File

@ -14,7 +14,6 @@
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28
""" """
username = input('请输入用户名: ') username = input('请输入用户名: ')
@ -44,7 +43,6 @@ f(x) = x + 2 (-1 <= x <= 1)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28
""" """
x = float(input('x = ')) x = float(input('x = '))
@ -68,7 +66,6 @@ f(x) = x + 2 (-1 <= x <= 1)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28
""" """
x = float(input('x = ')) x = float(input('x = '))
@ -94,7 +91,6 @@ print('f(%.2f) = %.2f' % (x, y))
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28
""" """
value = float(input('请输入长度: ')) value = float(input('请输入长度: '))
@ -115,7 +111,6 @@ else:
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28
""" """
from random import randint from random import randint
@ -150,7 +145,6 @@ print(result)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28
""" """
score = float(input('请输入成绩: ')) score = float(input('请输入成绩: '))
@ -175,7 +169,6 @@ print('对应的等级是:', grade)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28
""" """
import math import math
@ -193,7 +186,7 @@ else:
``` ```
> **说明:**上面的代码中使用了`math`模块的`sqrt`函数来计算平方根。用边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。 > **说明:**上面的代码中使用了`math`模块的`sqrt`函数来计算平方根。用边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。
#### 练习5实现一个个人所得税计算器。 #### 练习5个人所得税计算器。
```Python ```Python
""" """
@ -201,7 +194,6 @@ else:
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28
""" """
salary = float(input('本月收入: ')) salary = float(input('本月收入: '))

View File

@ -2,7 +2,7 @@
### 循环结构的应用场景 ### 循环结构的应用场景
如果在程序中我们需要重复的执行某条或某些指令例如用程序控制机器人踢足球如果机器人持球而且还没有进入射门范围那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了刚才的描述中其实不仅仅有需要重复的动作还有我们上一个章节讲到的分支结构。再举一个简单的例子比如在我们的程序中要实现每隔1秒中在屏幕上打印一个&quot;hello, world&quot;这样的字符串并持续一个小时,我们肯定不能够将`print('hello, world')`这句代码写上3600遍如果真的需要这样做那么我们的工作就太无聊了。因此,我们需要循环结构,使用循环结构我们就可以轻松的控制某件事或者某些事重复、重复、再重复的发生。在Python中构造循环结构有两种做法一种是`for-in`循环,一种是`while`循环。 如果在程序中我们需要重复的执行某条或某些指令例如用程序控制机器人踢足球如果机器人持球而且还没有进入射门范围那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了刚才的描述中其实不仅仅有需要重复的动作还有我们上一个章节讲到的分支结构。再举一个简单的例子比如在我们的程序中要实现每隔1秒中在屏幕上打印一个&quot;hello, world&quot;这样的字符串并持续一个小时,我们肯定不能够将`print('hello, world')`这句代码写上3600遍如果真的需要这样做那么编程的工作就太无聊了。因此,我们需要了解一下循环结构,有了循环结构我们就可以轻松的控制某件事或者某些事重复、重复、再重复的发生。在Python中构造循环结构有两种做法一种是`for-in`循环,一种是`while`循环。
### for-in循环 ### for-in循环
@ -14,7 +14,6 @@
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01
""" """
sum = 0 sum = 0
@ -37,7 +36,6 @@ print(sum)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01
""" """
sum = 0 sum = 0
@ -54,7 +52,6 @@ print(sum)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01
""" """
sum = 0 sum = 0
@ -77,7 +74,6 @@ print(sum)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01
""" """
import random import random
@ -109,8 +105,6 @@ if counter > 7:
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01
""" """
for i in range(1, 10): for i in range(1, 10):
@ -196,7 +190,6 @@ for factor in range(x, 0, -1):
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01
""" """
row = int(input('请输入行数: ')) row = int(input('请输入行数: '))

View File

@ -27,7 +27,6 @@ fmn = 1
for num in range(1, m - n + 1): for num in range(1, m - n + 1):
fmn *= num fmn *= num
print(fm // fn // fmn) print(fm // fn // fmn)
``` ```
### 函数的作用 ### 函数的作用
@ -134,7 +133,8 @@ def foo():
print('goodbye, world!') print('goodbye, world!')
foo() # 输出goodbye, world! # 下面的代码会输出什么呢?
foo()
``` ```
当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数那么怎么解决这种命名冲突呢答案其实很简单Python中每个文件就代表了一个模块module我们在不同的模块中可以有同名的函数在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。 当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数那么怎么解决这种命名冲突呢答案其实很简单Python中每个文件就代表了一个模块module我们在不同的模块中可以有同名的函数在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。
@ -158,11 +158,13 @@ test.py
```Python ```Python
from module1 import foo from module1 import foo
foo() # 输出hello, world! # 输出hello, world!
foo()
from module2 import foo from module2 import foo
foo() # 输出goodbye, world! # 输出goodbye, world!
foo()
``` ```
也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。 也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。
@ -185,7 +187,8 @@ test.py
from module1 import foo from module1 import foo
from module2 import foo from module2 import foo
foo() # 输出goodbye, world! # 输出goodbye, world!
foo()
``` ```
test.py test.py
@ -194,7 +197,8 @@ test.py
from module2 import foo from module2 import foo
from module1 import foo from module1 import foo
foo() # 输出hello, world! # 输出hello, world!
foo()
``` ```
需要说明的是如果我们导入的模块除了定义函数之外还中有可以执行代码那么Python解释器在导入这个模块时就会执行这些代码事实上我们可能并不希望如此因此如果我们在模块中编写了执行代码最好是将这些执行代码放入如下所示的条件中这样的话除非直接运行该模块if条件下的这些代码是不会执行的因为只有直接执行的模块的名字才是“\_\_main\_\_”。 需要说明的是如果我们导入的模块除了定义函数之外还中有可以执行代码那么Python解释器在导入这个模块时就会执行这些代码事实上我们可能并不希望如此因此如果我们在模块中编写了执行代码最好是将这些执行代码放入如下所示的条件中这样的话除非直接运行该模块if条件下的这些代码是不会执行的因为只有直接执行的模块的名字才是“\_\_main\_\_”。

View File

@ -42,7 +42,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
下面是运行程序得到的一次运行结果。 下面是运行程序得到的一次运行结果。
@ -86,7 +85,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
在上面的代码中,我们通过`Process`类创建了进程对象,通过`target`参数我们传入一个函数来表示进程启动后要执行的代码,后面的`args`是一个元组,它代表了传递给函数的参数。`Process`对象的`start`方法用来启动进程,而`join`方法表示等待进程执行结束。运行上面的代码可以明显发现两个下载任务“同时”启动了,而且程序的执行时间将大大缩短,不再是两个任务的时间总和。下面是程序的一次执行结果。 在上面的代码中,我们通过`Process`类创建了进程对象,通过`target`参数我们传入一个函数来表示进程启动后要执行的代码,后面的`args`是一个元组,它代表了传递给函数的参数。`Process`对象的`start`方法用来启动进程,而`join`方法表示等待进程执行结束。运行上面的代码可以明显发现两个下载任务“同时”启动了,而且程序的执行时间将大大缩短,不再是两个任务的时间总和。下面是程序的一次执行结果。
@ -375,7 +373,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
如果使用多线程将耗时间的任务放到一个独立的线程中执行,这样就不会因为执行耗时间的任务而阻塞了主线程,修改后的代码如下所示。 如果使用多线程将耗时间的任务放到一个独立的线程中执行,这样就不会因为执行耗时间的任务而阻塞了主线程,修改后的代码如下所示。
@ -424,7 +421,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
#### 例子2使用多进程对复杂任务进行“分而治之”。 #### 例子2使用多进程对复杂任务进行“分而治之”。
@ -448,7 +444,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
在上面的代码中我故意先去创建了一个列表容器然后填入了100000000个数这一步其实是比较耗时间的所以为了公平起见当我们将这个任务分解到8个进程中去执行的时候我们暂时也不考虑列表切片操作花费的时间只是把做运算和合并运算结果的时间统计出来代码如下所示。 在上面的代码中我故意先去创建了一个列表容器然后填入了100000000个数这一步其实是比较耗时间的所以为了公平起见当我们将这个任务分解到8个进程中去执行的时候我们暂时也不考虑列表切片操作花费的时间只是把做运算和合并运算结果的时间统计出来代码如下所示。
@ -493,7 +488,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
比较两段代码的执行结果在我目前使用的MacBook上上面的代码需要大概6秒左右的时间而下面的代码只需要不到1秒的时间再强调一次我们只是比较了运算的时间不考虑列表创建及切片操作花费的时间使用多进程后由于获得了更多的CPU执行时间以及更好的利用了CPU的多核特性明显的减少了程序的执行时间而且计算量越大效果越明显。当然如果愿意还可以将多个进程部署在不同的计算机上做成分布式进程具体的做法就是通过multiprocessing.managers模块中提供的管理器将`Queue`对象通过网络共享出来(注册到网络上让其他计算机可以访问),这部分内容也留到爬虫的专题再进行讲解。 比较两段代码的执行结果在我目前使用的MacBook上上面的代码需要大概6秒左右的时间而下面的代码只需要不到1秒的时间再强调一次我们只是比较了运算的时间不考虑列表创建及切片操作花费的时间使用多进程后由于获得了更多的CPU执行时间以及更好的利用了CPU的多核特性明显的减少了程序的执行时间而且计算量越大效果越明显。当然如果愿意还可以将多个进程部署在不同的计算机上做成分布式进程具体的做法就是通过multiprocessing.managers模块中提供的管理器将`Queue`对象通过网络共享出来(注册到网络上让其他计算机可以访问),这部分内容也留到爬虫的专题再进行讲解。

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 182 KiB

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -126,7 +126,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 基于传输层协议的套接字编程 ### 基于传输层协议的套接字编程
@ -174,7 +173,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
运行服务器程序后我们可以通过Windows系统的telnet来访问该服务器结果如下图所示。 运行服务器程序后我们可以通过Windows系统的telnet来访问该服务器结果如下图所示。
@ -203,7 +201,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
需要注意的是上面的服务器并没有使用多线程或者异步I/O的处理方式这也就意味着当服务器与一个客户端处于通信状态时其他的客户端只能排队等待。很显然这样的服务器并不能满足我们的需求我们需要的服务器是能够同时接纳和处理多个用户请求的。下面我们来设计一个使用多线程技术处理多个用户请求的服务器该服务器会向连接到服务器的客户端发送一张图片。 需要注意的是上面的服务器并没有使用多线程或者异步I/O的处理方式这也就意味着当服务器与一个客户端处于通信状态时其他的客户端只能排队等待。很显然这样的服务器并不能满足我们的需求我们需要的服务器是能够同时接纳和处理多个用户请求的。下面我们来设计一个使用多线程技术处理多个用户请求的服务器该服务器会向连接到服务器的客户端发送一张图片。
@ -256,7 +253,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
客户端代码: 客户端代码:
@ -291,7 +287,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
在这个案例中我们使用了JSON作为数据传输的格式通过JSON格式对传输的数据进行了序列化和反序列化的操作但是JSON并不能携带二进制数据因此对图片的二进制数据进行了Base64编码的处理。Base64是一种用64个字符表示所有二进制数据的编码方式通过将二进制数据每6位一组的方式重新组织刚好可以使用0~9的数字、大小写字母以及“+”和“/”总共64个字符表示从`000000`到`111111`的64种状态。[维基百科](https://zh.wikipedia.org/wiki/Base64)上有关于Base64编码的详细讲解不熟悉Base64的读者可以自行阅读。 在这个案例中我们使用了JSON作为数据传输的格式通过JSON格式对传输的数据进行了序列化和反序列化的操作但是JSON并不能携带二进制数据因此对图片的二进制数据进行了Base64编码的处理。Base64是一种用64个字符表示所有二进制数据的编码方式通过将二进制数据每6位一组的方式重新组织刚好可以使用0~9的数字、大小写字母以及“+”和“/”总共64个字符表示从`000000`到`111111`的64种状态。[维基百科](https://zh.wikipedia.org/wiki/Base64)上有关于Base64编码的详细讲解不熟悉Base64的读者可以自行阅读。

View File

@ -31,7 +31,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
如果要发送带有附件的邮件,那么可以按照下面的方式进行操作。 如果要发送带有附件的邮件,那么可以按照下面的方式进行操作。
@ -88,7 +87,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 发送短信 ### 发送短信
@ -119,7 +117,5 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -1,12 +1,12 @@
## Python语言进 ## Python语言进
### 数据结构和算法 1. 数据结构和算法
- 排序算法(冒泡和归并)和查找算法(顺序和折半)
#### 排序算法(冒泡和归并)
```Python ```Python
def bubble_sort(items, comp=lambda x, y: x > y): def bubble_sort(origin_items, comp=lambda x, y: x > y):
"""高质量冒泡排序(搅拌排序)""" """高质量冒泡排序(搅拌排序)"""
items = origin_items[:]
for i in range(len(items) - 1): for i in range(len(items) - 1):
swapped = False swapped = False
for j in range(len(items) - 1 - i): for j in range(len(items) - 1 - i):
@ -21,6 +21,7 @@ def bubble_sort(items, comp=lambda x, y: x > y):
swapped = True swapped = True
if not swapped: if not swapped:
break break
return items
``` ```
```Python ```Python
@ -34,7 +35,7 @@ def merge_sort(items, comp=lambda x, y: x <= y):
return merge(left, right, comp) return merge(left, right, comp)
def merge(items1, items2, comp=lambda x, y: x <= y): def merge(items1, items2, comp):
"""合并(将两个有序的列表合并成一个有序的列表)""" """合并(将两个有序的列表合并成一个有序的列表)"""
items = [] items = []
idx1, idx2 = 0, 0 idx1, idx2 = 0, 0
@ -58,11 +59,10 @@ def seq_search(items, key):
return index return index
return -1 return -1
``` ```
#### 查找算法(顺序和折半)
```Python ```Python
def bin_search(items, key): def bin_search(items, key):
"""折半查找(循环实现)""" """折半查找"""
start, end = 0, len(items) - 1 start, end = 0, len(items) - 1
while start <= end: while start <= end:
mid = (start + end) // 2 mid = (start + end) // 2
@ -75,7 +75,7 @@ def bin_search(items, key):
return -1 return -1
``` ```
#### 使用生成式(推导式) - 使用生成式(推导式)语法
```Python ```Python
prices = { prices = {
@ -92,10 +92,9 @@ prices2 = {key: value for key, value in prices.items() if value > 100}
print(prices2) print(prices2)
``` ```
#### 嵌套的列表 - 嵌套的列表
```Python ```Python
def main():
names = ['关羽', '张飞', '赵云', '马超', '黄忠'] names = ['关羽', '张飞', '赵云', '马超', '黄忠']
courses = ['语文', '数学', '英语'] courses = ['语文', '数学', '英语']
# 录入五个学生三门课程的成绩 # 录入五个学生三门课程的成绩
@ -106,24 +105,17 @@ def main():
for col, course in enumerate(courses): for col, course in enumerate(courses):
scores[row][col] = float(input(f'请输入{name}的{course}成绩: ')) scores[row][col] = float(input(f'请输入{name}的{course}成绩: '))
print(scores) print(scores)
if __name__ == '__main__':
main()
``` ```
[Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP [Python Tutor](http://pythontutor.com/) - VISUALIZE CODE AND GET LIVE HELP
#### heapq、itertools等的用法 - heapq、itertools等的用法
```Python ```Python
""" """
从列表中找出最大的或最小的N个元素 从列表中找出最大的或最小的N个元素
""" """
import heapq import heapq
def main():
list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92] list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
list2 = [ list2 = [
{'name': 'IBM', 'shares': 100, 'price': 91.1}, {'name': 'IBM', 'shares': 100, 'price': 91.1},
@ -137,10 +129,6 @@ def main():
print(heapq.nsmallest(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['price']))
print(heapq.nlargest(2, list2, key=lambda x: x['shares'])) print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
if __name__ == '__main__':
main()
``` ```
```Python ```Python
@ -149,23 +137,17 @@ if __name__ == '__main__':
""" """
import itertools import itertools
def main():
for val in itertools.permutations('ABCD'): for val in itertools.permutations('ABCD'):
print(val) print(val)
print('-' * 50)
for val in itertools.combinations('ABCDE', 3): for val in itertools.combinations('ABCDE', 3):
print(val) print(val)
print('-' * 50)
for val in itertools.product('ABCD', '123'): for val in itertools.product('ABCD', '123'):
print(val) print(val)
if __name__ == '__main__':
main()
``` ```
#### collections模块下的工具类 - collections模块下的工具类
```Python ```Python
""" """
@ -173,8 +155,6 @@ if __name__ == '__main__':
""" """
from collections import Counter from collections import Counter
def main():
words = [ words = [
'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
@ -183,21 +163,17 @@ def main():
] ]
counter = Counter(words) counter = Counter(words)
print(counter.most_common(3)) print(counter.most_common(3))
if __name__ == '__main__':
main()
``` ```
#### 穷举法、贪婪法、分治法、动态规划 - 穷举法、贪婪法、分治法、回溯法、动态规划
例子:百钱百鸡和五人分鱼。
```Python ```Python
""" """
穷举法 - 穷尽所有可能直到找到正确答案 穷举法 - 穷尽所有可能直到找到正确答案
""" """
def main():
# 公鸡5元一只 母鸡3元一只 小鸡1元三只 # 公鸡5元一只 母鸡3元一只 小鸡1元三只
# 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只 # 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只
for x in range(20): for x in range(20):
@ -205,6 +181,7 @@ def main():
z = 100 - x - y z = 100 - x - y
if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0: if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0:
print(x, y, z) print(x, y, z)
# A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉 # A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉
# 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份 # 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份
# B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份 # B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份
@ -223,12 +200,10 @@ def main():
print(fish) print(fish)
break break
fish += 1 fish += 1
if __name__ == '__main__':
main()
``` ```
例子:斐波拉切数列。
```Python ```Python
""" """
动态规划 - 适用于有重叠子问题和最优子结构性质的问题 动态规划 - 适用于有重叠子问题和最优子结构性质的问题
@ -247,23 +222,56 @@ def fib(num, temp={}):
return temp[num] return temp[num]
``` ```
### 函数的使用方式 2. 函数的使用方式
- 将函数视为“一等公民” - 将函数视为“一等公民”
- 高阶函数的用法filter、map以及它们的替代品 - 函数可以赋值给变量
- 函数可以作为函数的参数
- 函数可以作为函数的返回值
- 高阶函数的用法(`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函数 - 匿名函数和内联函数的用法(`lambda`函数)
- 闭包和作用域问题LEGB - 闭包和作用域问题
- 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 ```Python
from functools import wraps from functools import wraps
@ -271,6 +279,7 @@ def fib(num, temp={}):
def record(output): def record(output):
"""自定义带参数的装饰器"""
def decorate(func): def decorate(func):
@ -292,6 +301,7 @@ def fib(num, temp={}):
class Record(object): class Record(object):
"""自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)"""
def __init__(self, output): def __init__(self, output):
self.output = output self.output = output
@ -308,13 +318,16 @@ def fib(num, temp={}):
return wrapper return wrapper
``` ```
用装饰器来实现单例模式。 > 说明:由于对带装饰功能的函数添加了@wraps装饰器可以通过`func.__wrapped__`方式获得被装饰之前的函数或类来取消装饰器的作用。
例子:用装饰器来实现单例模式。
```Python ```Python
from functools import wraps from functools import wraps
def singleton(cls): def singleton(cls):
"""装饰类的装饰器"""
instances = {} instances = {}
@wraps(cls) @wraps(cls)
@ -327,18 +340,42 @@ def fib(num, temp={}):
@singleton @singleton
class Singleton(object): class President(object):
"""总统(单例类)"""
pass 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 ```Python
""" """
月薪结算系统 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成
部门经理每月15000 程序员每小时200 销售员1800底薪+销售额5%提成
""" """
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
@ -406,13 +443,31 @@ def fib(num, temp={}):
return 1800.0 + self.sales * 0.05 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(): def main():
"""主函数"""
emps = [ emps = [
Manager('刘备'), Manager('曹操'), Programmer('许褚'), EmployeeFactory.create('M', '曹操'), EmployeeFactory.create('P', '荀彧'),
Salesman('貂蝉'), Salesman('赵云'), Programmer('张辽'), EmployeeFactory.create('P', '郭嘉'), EmployeeFactory.create('S', '典韦')
Programmer('关羽'), Programmer('周瑜')
] ]
for emp in emps: for emp in emps:
# 用isinstance函数识别对象引用所引用对象的类型
if isinstance(emp, Programmer): if isinstance(emp, Programmer):
emp.working_hour = int(input('本月工作时间: ')) emp.working_hour = int(input('本月工作时间: '))
elif isinstance(emp, Salesman): elif isinstance(emp, Salesman):
@ -424,6 +479,113 @@ def fib(num, temp={}):
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()
```
- 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆) - 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)
- 垃圾回收、循环引用和弱引用 - 垃圾回收、循环引用和弱引用
@ -467,6 +629,9 @@ def fib(num, temp={}):
引用计数可能会导致循环引用问题而循环引用会导致内存泄露如下面的代码所示。为了解决这个问题Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。 引用计数可能会导致循环引用问题而循环引用会导致内存泄露如下面的代码所示。为了解决这个问题Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。
```Python ```Python
# 循环引用会导致内存泄露 - Python除了引用技术还引入了标记清理和分代回收
# 在Python 3.6以前如果重写__del__魔术方法会导致循环引用处理失效
# 如果不想造成循环引用可以使用弱引用
list1 = [] list1 = []
list2 = [] list2 = []
list1.append(list2) list1.append(list2)
@ -494,14 +659,11 @@ def fib(num, temp={}):
- 混入Mixin - 混入Mixin
例子自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。
```Python ```Python
"""
限制字典只有在指定的key不存在时才能设置键值对
MRO - Method Resolution Order - 多重继承时的方法解析顺序
"""
class SetOnceMappingMixin: class SetOnceMappingMixin:
"""自定义混入类"""
__slots__ = () __slots__ = ()
def __setitem__(self, key, value): def __setitem__(self, key, value):
@ -511,36 +673,26 @@ def fib(num, temp={}):
class SetOnceDict(SetOnceMappingMixin, dict): class SetOnceDict(SetOnceMappingMixin, dict):
"""自定义字典"""
pass pass
def main(): my_dict= SetOnceDict()
dict1 = SetOnceDict()
try: try:
dict1['username'] = 'jackfrued' my_dict['username'] = 'jackfrued'
dict1['username'] = 'hellokitty' my_dict['username'] = 'hellokitty'
dict1['username'] = 'wangdachui'
except KeyError: except KeyError:
pass pass
print(dict1) print(my_dict)
if __name__ == '__main__':
main()
``` ```
- 元编程和元类 - 元编程和元类
用元类实现单例模式。 例子:用元类实现单例模式。
```Python ```Python
"""
通过元类实现单例模式
"""
class SingletonMeta(type): class SingletonMeta(type):
"""单例的元类""" """自定义元类"""
def __init__(cls, *args, **kwargs): def __init__(cls, *args, **kwargs):
cls.__instance = None cls.__instance = None
@ -552,36 +704,67 @@ def fib(num, temp={}):
return cls.__instance return cls.__instance
class Singleton(metaclass=SingletonMeta): class President(metaclass=SingletonMeta):
"""单例类""" """总统(单例类)"""
pass
```
def __init__(self, name): - 面向对象设计原则
self._name = name
from random import randrange
self._value = randrange(100000)
@property - 单一职责原则 **S**RP- 一个类只做该做的事情(类的设计要高内聚)
def name(self): - 开闭原则 **O**CP- 软件实体应该对扩展开发对修改关闭
return self._name - 依赖倒转原则DIP- 面向抽象编程(在弱类型语言中已经被弱化)
- 里氏替换原则(**L**SP - 任何时候可以用子类对象替换掉父类对象
- 接口隔离原则(**I**SP- 接口要小而专不要大而全Python中没有接口的概念
- 合成聚合复用原则CARP - 优先使用强关联关系而不是继承关系复用代码
- 最少知识原则迪米特法则Lo**D**- 不要给没有必然联系的对象发消息
@property > 说明:上面加粗的字母放在一起称为面向对象的**SOLID**原则。
def value(self):
return self._value
- 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(): def main():
sin1 = Singleton('Lee') """主函数"""
sin2 = Singleton('Wang') hasher1 = StreamHasher()
print(sin1 == sin2) with open('Python-3.7.1.tgz', 'rb') as stream:
print(sin1.value, sin2.value) print(hasher1.to_digest(stream))
print(sin1.name, sin2.name) hasher2 = StreamHasher('sha1')
with open('Python-3.7.1.tgz', 'rb') as stream:
print(hasher2(stream))
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 迭代器和生成器 4. 迭代器和生成器
- 和迭代器相关的魔术方法(`__iter__`和`__next__`
- 两种创建生成器的方式(生成器表达式和`yield`关键字)
```Python ```Python
""" """
@ -589,15 +772,7 @@ def fib(num, temp={}):
""" """
def fib1(num): def fib(num):
"""普通函数"""
a, b = 0, 1
for _ in range(num):
a, b = b, a + b
return a
def fib2(num):
"""生成器""" """生成器"""
a, b = 0, 1 a, b = 0, 1
for _ in range(num): for _ in range(num):
@ -605,7 +780,7 @@ def fib2(num):
yield a yield a
class Fib3: class Fib(object):
"""迭代器""" """迭代器"""
def __init__(self, num): def __init__(self, num):
@ -622,23 +797,242 @@ class Fib3:
self.idx += 1 self.idx += 1
return self.a return self.a
raise StopIteration() 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(): def main():
for val in fib2(20): """主函数"""
print(val) if not os.path.exists(PREFIX):
print('-' * 50) os.mkdir(PREFIX)
for val in Fib3(20): for infile in glob.glob('images/*.png'):
print(val) for size in (32, 64, 128):
# 创建并启动线程
threading.Thread(
target=generate_thumbnail,
args=(infile, (size, size))
).start()
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 并发编程 多个线程竞争资源的情况
- 多线程和多进程 ```Python
- 协程和异步I/O """
- concurrent.futures 多线程程序如果没有竞争资源处理起来通常也比较简单
当多个线程竞争临界资源的时候如果缺乏必要的保护措施就会导致数据错乱
说明:临界资源就是被多个线程竞争的资源
"""
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`对象获得协程执行结果。

View File

@ -1249,7 +1249,3 @@ build environment:
3. HISTSIZE 3. HISTSIZE
4. RANDOM 4. RANDOM
5. PATH 5. PATH

View File

@ -115,7 +115,6 @@ HTTP响应响应行+响应头+空行+消息体):
3. HTTPie命令行HTTP客户端。 3. HTTPie命令行HTTP客户端。
```Shell ```Shell
$ http --header http://www.scu.edu.cn $ http --header http://www.scu.edu.cn
HTTP/1.1 200 OK HTTP/1.1 200 OK
Accept-Ranges: bytes Accept-Ranges: bytes
@ -138,8 +137,6 @@ HTTP响应响应行+响应头+空行+消息体):
4. BuiltWith识别网站所用技术的工具。 4. BuiltWith识别网站所用技术的工具。
```Python ```Python
>>>
>>> import builtwith >>> import builtwith
>>> builtwith.parse('http://www.bootcss.com/') >>> 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']} {'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查询网站所有者的工具。 5. python-whois查询网站所有者的工具。
```Python ```Python
>>>
>>> import whois >>> import whois
>>> whois.whois('baidu.com') >>> 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'} {'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新闻标题和链接的爬虫。 下面的例子给出了一个从“搜狐体育”上获取NBA新闻标题和链接的爬虫。
```Python ```Python
from urllib.error import URLError from urllib.error import URLError
from urllib.request import urlopen from urllib.request import urlopen
@ -304,7 +298,6 @@ if __name__ == '__main__':
- 使用未经验证的上下文 - 使用未经验证的上下文
```Python ```Python
import ssl import ssl
request = urllib.request.Request(url='...', headers={...}) request = urllib.request.Request(url='...', headers={...})
@ -315,10 +308,8 @@ if __name__ == '__main__':
- 设置全局的取消证书验证 - 设置全局的取消证书验证
```Python ```Python
import ssl import ssl
ssl._create_default_https_context = ssl._create_unverified_context ssl._create_default_https_context = ssl._create_unverified_context
``` ```

View File

@ -12,7 +12,6 @@
### HTML页面分析 ### HTML页面分析
```HTML ```HTML
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -136,7 +135,6 @@ pyquery相当于jQuery的Python实现可以用于解析HTML网页。
### 实例 - 获取知乎发现上的问题链接 ### 实例 - 获取知乎发现上的问题链接
```Python ```Python
from urllib.parse import urljoin from urllib.parse import urljoin
import re import re
@ -168,6 +166,5 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```

View File

@ -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)解压缩解归档之后进行构件安装。 可以使用Linux系统的包管理工具如yum来安装Redis也可以通过在Redis的[官方网站](https://redis.io/)下载Redis的[源代码](http://download.redis.io/releases/redis-3.2.11.tar.gz)解压缩解归档之后进行构件安装。
```Shell ```Shell
wget http://download.redis.io/releases/redis-3.2.11.tar.gz
# wget http://download.redis.io/releases/redis-3.2.11.tar.gz gunzip redis-3.2.11.tar.gz
# gunzip redis-3.2.11.tar.gz tar -xvf redis-3.2.11.tar
# tar -xvf redis-3.2.11.tar cd redis-3.2.11
# cd redis-3.2.11 make && make install
# make && make install
``` ```
接下来我们将redis-3.2.11目录下的redis.conf配置文件复制到用户主目录下并修改配置文件如果你对配置文件不是很有把握就不要直接修改而是先复制一份再修改这个副本 接下来我们将redis-3.2.11目录下的redis.conf配置文件复制到用户主目录下并修改配置文件如果你对配置文件不是很有把握就不要直接修改而是先复制一份再修改这个副本
```Shell ```Shell
cd ..
# cd .. cp redis-3.2.11/redis.conf redis.conf
# cp redis-3.2.11/redis.conf redis.conf vim redis.conf
# vim redis.conf
``` ```
配置将Redis服务绑定到指定的IP地址和端口。 配置将Redis服务绑定到指定的IP地址和端口。
@ -76,8 +74,7 @@ Redis是REmote DIctionary Server的缩写它是一个用ANSI C编写的高性
接下来启动Redis服务器可以将服务器放在后台去运行。 接下来启动Redis服务器可以将服务器放在后台去运行。
```Shell ```Shell
redis-server redis.conf &
# redis-server redis.conf &
_.-``__ ''-._ _.-``__ ''-._
_.-`` `. `_. ''-._ Redis 3.2.11 (00000000/0) 64 bit _.-`` `. `_. ''-._ Redis 3.2.11 (00000000/0) 64 bit
@ -100,8 +97,7 @@ Redis是REmote DIctionary Server的缩写它是一个用ANSI C编写的高性
接下来我们尝试用Redis客户端去连接服务器。 接下来我们尝试用Redis客户端去连接服务器。
```Shell ```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 172.18.61.250:6379> auth 1qaz2wsx
OK OK
172.18.61.250:6379> ping 172.18.61.250:6379> ping
@ -112,7 +108,6 @@ PONG
Redis有着非常丰富的数据类型也有很多的命令来操作这些数据具体的内容可以查看[Redis命令参考](http://redisdoc.com/)在这个网站上除了Redis的命令参考还有Redis的详细文档其中包括了通知、事务、主从复制、持久化、哨兵、集群等内容。 Redis有着非常丰富的数据类型也有很多的命令来操作这些数据具体的内容可以查看[Redis命令参考](http://redisdoc.com/)在这个网站上除了Redis的命令参考还有Redis的详细文档其中包括了通知、事务、主从复制、持久化、哨兵、集群等内容。
```Shell ```Shell
172.18.61.250:6379> set username admin 172.18.61.250:6379> set username admin
OK OK
172.18.61.250:6379> get username 172.18.61.250:6379> get username
@ -166,13 +161,11 @@ OK
可以使用pip安装redis模块。redis模块的核心是名为Redis的类该类的对象代表一个Redis客户端通过该客户端可以向Redis服务器发送命令并获取执行的结果。上面我们在Redis客户端中使用的命令基本上就是Redis对象可以接收的消息所以如果了解了Redis的命令就可以在Python中玩转Redis。 可以使用pip安装redis模块。redis模块的核心是名为Redis的类该类的对象代表一个Redis客户端通过该客户端可以向Redis服务器发送命令并获取执行的结果。上面我们在Redis客户端中使用的命令基本上就是Redis对象可以接收的消息所以如果了解了Redis的命令就可以在Python中玩转Redis。
```Shell ```Shell
pip3 install redis
$ pip3 install redis python3
$ python3
``` ```
```Python ```Python
>>> import redis >>> import redis
>>> client = redis.Redis(host='1.2.3.4', port=6379, password='1qaz2wsx') >>> client = redis.Redis(host='1.2.3.4', port=6379, password='1qaz2wsx')
>>> client.set('username', 'admin') >>> client.set('username', 'admin')
@ -202,14 +195,14 @@ MongoDB将数据存储为一个文档一个文档由一系列的“键值对
可以从MongoDB的[官方下载链接](https://www.mongodb.com/download-center#community)下载MongoDB官方为Windows系统提供了一个Installer程序而Linux和MacOS则提供了压缩文件。下面简单说一下Linux系统如何安装和配置MongoDB。 可以从MongoDB的[官方下载链接](https://www.mongodb.com/download-center#community)下载MongoDB官方为Windows系统提供了一个Installer程序而Linux和MacOS则提供了压缩文件。下面简单说一下Linux系统如何安装和配置MongoDB。
```Shell ```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] 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] db version v3.6.5
2018-06-03T18:03:28.232+0800 I CONTROL [initandlisten] git version: a20ecd3e3a174162052ff99913bc2ca9a839d618 2018-06-03T18:03:28.232+0800 I CONTROL [initandlisten] git version: a20ecd3e3a174162052ff99913bc2ca9a839d618
@ -239,7 +232,8 @@ MongoDB将数据存储为一个文档一个文档由一系列的“键值对
启动服务器后可以使用交互式环境跟服务器通信,如下所示。 启动服务器后可以使用交互式环境跟服务器通信,如下所示。
```shell ```shell
# mongo --host 172.18.61.250 mongo --host 172.18.61.250
MongoDB shell version v3.6.5 MongoDB shell version v3.6.5
connecting to: mongodb://172.18.61.250:27017/ connecting to: mongodb://172.18.61.250:27017/
... ...
@ -249,7 +243,6 @@ connecting to: mongodb://172.18.61.250:27017/
1. 查看、创建和删除数据库。 1. 查看、创建和删除数据库。
```JavaScript ```JavaScript
> // 显示所有数据库 > // 显示所有数据库
> show dbs > show dbs
admin 0.000GB admin 0.000GB
@ -267,7 +260,6 @@ connecting to: mongodb://172.18.61.250:27017/
2. 创建、删除和查看集合。 2. 创建、删除和查看集合。
```JavaScript ```JavaScript
> // 创建并切换到school数据库 > // 创建并切换到school数据库
> use school > use school
switched to db school switched to db school
@ -292,7 +284,6 @@ connecting to: mongodb://172.18.61.250:27017/
3. 文档的CRUD操作。 3. 文档的CRUD操作。
```JavaScript ```JavaScript
> // 向students集合插入文档 > // 向students集合插入文档
> db.students.insert({stuid: 1001, name: '骆昊', age: 38}) > db.students.insert({stuid: 1001, name: '骆昊', age: 38})
WriteResult({ "nInserted" : 1 }) WriteResult({ "nInserted" : 1 })
@ -394,13 +385,11 @@ connecting to: mongodb://172.18.61.250:27017/
可以通过pip安装pymongo来实现对MongoDB的操作。 可以通过pip安装pymongo来实现对MongoDB的操作。
```Shell ```Shell
pip3 install pymongo
$ pip3 install pymongo python3
$ python3
``` ```
```Python ```Python
>>> from pymongo import MongoClient >>> from pymongo import MongoClient
>>> client = MongoClient('mongodb://120.77.222.217:27017') >>> client = MongoClient('mongodb://120.77.222.217:27017')
>>> db = client.school >>> db = client.school
@ -451,7 +440,6 @@ $ python3
### 实例 - 缓存知乎发现上的链接和页面代码 ### 实例 - 缓存知乎发现上的链接和页面代码
```Python ```Python
from hashlib import sha1 from hashlib import sha1
from urllib.parse import urljoin from urllib.parse import urljoin
@ -501,7 +489,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```

View File

@ -44,7 +44,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池
1. 生成器 - 数据的生产者。 1. 生成器 - 数据的生产者。
```Python ```Python
from time import sleep from time import sleep
@ -70,7 +69,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池
生成器还可以叠加来组成生成器管道,代码如下所示。 生成器还可以叠加来组成生成器管道,代码如下所示。
```Python ```Python
# Fibonacci数生成器 # Fibonacci数生成器
def fib(): def fib():
a, b = 0, 1 a, b = 0, 1
@ -94,13 +92,11 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
2. 协程 - 数据的消费者。 2. 协程 - 数据的消费者。
```Python ```Python
from time import sleep from time import sleep
@ -130,13 +126,11 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
> 说明上面代码中countdown_gen函数中的第1行consumer.send(None)是为了激活生成器通俗的说就是让生成器执行到有yield关键字的地方挂起当然也可以通过next(consumer)来达到同样的效果。如果不愿意每次都用这样的代码来“预激”生成器,可以写一个包装器来完成该操作,代码如下所示。 > 说明上面代码中countdown_gen函数中的第1行consumer.send(None)是为了激活生成器通俗的说就是让生成器执行到有yield关键字的地方挂起当然也可以通过next(consumer)来达到同样的效果。如果不愿意每次都用这样的代码来“预激”生成器,可以写一个包装器来完成该操作,代码如下所示。
```Python ```Python
from functools import wraps from functools import wraps
@ -156,7 +150,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池
3. 异步I/O - 非阻塞式I/O操作。 3. 异步I/O - 非阻塞式I/O操作。
```Python ```Python
import asyncio import asyncio
@ -179,13 +172,11 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
4. `async`和`await`。 4. `async`和`await`。
```Python ```Python
import asyncio import asyncio
import aiohttp import aiohttp
@ -215,7 +206,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
上面的代码使用了[AIOHTTP](https://github.com/aio-libs/aiohttp)这个非常著名的第三方库它实现了HTTP客户端和HTTP服务器的功能对异步操作提供了非常好的支持有兴趣可以阅读它的[官方文档](https://aiohttp.readthedocs.io/en/stable/)。 上面的代码使用了[AIOHTTP](https://github.com/aio-libs/aiohttp)这个非常著名的第三方库它实现了HTTP客户端和HTTP服务器的功能对异步操作提供了非常好的支持有兴趣可以阅读它的[官方文档](https://aiohttp.readthedocs.io/en/stable/)。
@ -225,7 +215,6 @@ Python3.2带来了`concurrent.futures` 模块,这个模块包含了线程池
下面我们把之间讲的所有知识结合起来,用面向对象的方式实现一个爬取“手机搜狐网”的多线程爬虫。 下面我们把之间讲的所有知识结合起来,用面向对象的方式实现一个爬取“手机搜狐网”的多线程爬虫。
```Python ```Python
import pickle import pickle
import zlib import zlib
from enum import Enum, unique from enum import Enum, unique
@ -382,6 +371,5 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```

View File

@ -25,14 +25,12 @@ WebKit的代码始于1998年的KHTML项目当时它是Konqueror浏览器的
如果没有打算用上面所说的方式来渲染页面并获得动态内容其实还有一种替代方案就是使用自动化测试工具Selenium它提供了浏览器自动化的API接口这样就可以通过操控浏览器来获取动态内容。首先可以使用pip来安装Selenium。 如果没有打算用上面所说的方式来渲染页面并获得动态内容其实还有一种替代方案就是使用自动化测试工具Selenium它提供了浏览器自动化的API接口这样就可以通过操控浏览器来获取动态内容。首先可以使用pip来安装Selenium。
```Shell ```Shell
pip3 install selenium
$ pip3 install selenium
``` ```
下面以“阿里V任务”的“直播服务”为例来演示如何使用Selenium获取到动态内容并抓取主播图片。 下面以“阿里V任务”的“直播服务”为例来演示如何使用Selenium获取到动态内容并抓取主播图片。
```Python ```Python
import requests import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
@ -47,13 +45,11 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
运行上面的程序会发现没有任何的输出因为页面的HTML代码上根本找不到`<img>`标签。接下来我们使用Selenium来获取到页面上的动态内容再提取主播图片。 运行上面的程序会发现没有任何的输出因为页面的HTML代码上根本找不到`<img>`标签。接下来我们使用Selenium来获取到页面上的动态内容再提取主播图片。
```Python ```Python
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from selenium import webdriver from selenium import webdriver
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
@ -69,21 +65,18 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
在上面的程序中我们通过Selenium实现对Chrome浏览器的操控如果要操控其他的浏览器可以创对应的浏览器对象例如Firefox、IE等。运行上面的程序如果看到如下所示的错误提示那是说明我们还没有将Chrome浏览器的驱动添加到PATH环境变量中也没有在程序中指定Chrome浏览器驱动所在的位置。 在上面的程序中我们通过Selenium实现对Chrome浏览器的操控如果要操控其他的浏览器可以创对应的浏览器对象例如Firefox、IE等。运行上面的程序如果看到如下所示的错误提示那是说明我们还没有将Chrome浏览器的驱动添加到PATH环境变量中也没有在程序中指定Chrome浏览器驱动所在的位置。
```Shell ```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.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下配置环境变量也非常简单不清楚的可以自行了解。 为了解决上面的问题可以到Selenium的[官方网站]()找到浏览器驱动的下载链接并下载需要的驱动在Linux或macOS系统下可以通过下面的命令来设置PATH环境变量Windows下配置环境变量也非常简单不清楚的可以自行了解。
```Shell ```Shell
export PATH=$PATH:/Users/Hao/Downloads/Tools/chromedriver/
$ export PATH=$PATH:/Users/Hao/Downloads/Tools/chromedriver/
``` ```
其中`/Users/Hao/Downloads/Tools/chromedriver/ `就是chromedriver所在的路径。 其中`/Users/Hao/Downloads/Tools/chromedriver/ `就是chromedriver所在的路径。

View File

@ -43,13 +43,11 @@ Scrapy的整个数据处理流程由Scrapy引擎进行控制通常的运转
```Shell ```Shell
$
``` ```
项目的目录结构如下图所示。 项目的目录结构如下图所示。
```Shell ```Shell
(venv) $ tree (venv) $ tree
. .
|____ scrapy.cfg |____ scrapy.cfg
@ -78,7 +76,6 @@ $
1. 在items.py文件中定义字段这些字段用来保存数据方便后续的操作。 1. 在items.py文件中定义字段这些字段用来保存数据方便后续的操作。
```Python ```Python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Define here the models for your scraped items # Define here the models for your scraped items
@ -102,12 +99,10 @@ $
2. 在spiders文件夹中编写自己的爬虫。 2. 在spiders文件夹中编写自己的爬虫。
```Shell ```Shell
(venv) $ scrapy genspider movie movie.douban.com --template=crawl (venv) $ scrapy genspider movie movie.douban.com --template=crawl
``` ```
```Python ```Python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import scrapy import scrapy
from scrapy.selector import Selector from scrapy.selector import Selector
@ -136,28 +131,24 @@ $
item['classification']= sel.xpath('//span[@property="v:genre"]/text()').extract() item['classification']= sel.xpath('//span[@property="v:genre"]/text()').extract()
item['actor']= sel.xpath('//*[@id="info"]/span[3]/a[1]/text()').extract() item['actor']= sel.xpath('//*[@id="info"]/span[3]/a[1]/text()').extract()
return item return item
``` ```
> 说明上面我们通过Scrapy提供的爬虫模板创建了Spider其中的rules中的LinkExtractor对象会自动完成对新的链接的解析该对象中有一个名为extract_link的回调方法。Scrapy支持用XPath语法和CSS选择器进行数据解析对应的方法分别是xpath和css上面我们使用了XPath语法对页面进行解析如果不熟悉XPath语法可以看看后面的补充说明。 > 说明上面我们通过Scrapy提供的爬虫模板创建了Spider其中的rules中的LinkExtractor对象会自动完成对新的链接的解析该对象中有一个名为extract_link的回调方法。Scrapy支持用XPath语法和CSS选择器进行数据解析对应的方法分别是xpath和css上面我们使用了XPath语法对页面进行解析如果不熟悉XPath语法可以看看后面的补充说明。
到这里,我们已经可以通过下面的命令让爬虫运转起来。 到这里,我们已经可以通过下面的命令让爬虫运转起来。
```Shell ```Shell
(venv)$ scrapy crawl movie (venv)$ scrapy crawl movie
``` ```
可以在控制台看到爬取到的数据,如果想将这些数据保存到文件中,可以通过`-o`参数来指定文件名Scrapy支持我们将爬取到的数据导出成JSON、CSV、XML、pickle、marshal等格式。 可以在控制台看到爬取到的数据,如果想将这些数据保存到文件中,可以通过`-o`参数来指定文件名Scrapy支持我们将爬取到的数据导出成JSON、CSV、XML、pickle、marshal等格式。
```Shell ```Shell
(venv)$ scrapy crawl moive -o result.json (venv)$ scrapy crawl moive -o result.json
``` ```
3. 在pipelines.py中完成对数据进行持久化的操作。 3. 在pipelines.py中完成对数据进行持久化的操作。
```Python ```Python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Define your item pipelines here # Define your item pipelines here
@ -211,7 +202,6 @@ $
4. 修改settings.py文件对项目进行配置。 4. 修改settings.py文件对项目进行配置。
```Python ```Python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Scrapy settings for douban project # Scrapy settings for douban project
@ -325,7 +315,6 @@ $
XML文件。 XML文件。
```XML ```XML
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<bookstore> <bookstore>

View File

@ -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协议响应头
# 当前网站是不允许使用<iframe>标签进行加载的
X_FRAME_OPTIONS = 'DENY'
```
4. 敏感信息放到环境变量或文件中。
```Python
SECRET_KEY = os.environ['SECRET_KEY']
DB_USER = os.environ['DB_USER']
DB_PASS = os.environ['DB_PASS']
REDIS_AUTH = os.environ['REDIS_AUTH']
```
### 更新服务器Python环境到3.x
> 说明:如果需要清除之前的安装,就删除对应的文件和文件夹即可
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.1/Python-3.7.1.tar.xz
```
3. 解压缩和解归档。
```Shell
xz -d Python-3.7.1.tar.xz
tar -xvf Python-3.7.1.tar
```
4. 执行配置生成Makefile构建文件
```Shell
cd Python-3.7.1
./configure --prefix=/usr/local/python37 --enable-optimizations
```
5. 构建和安装。
```Shell
make && make install
```
6. 配置PATH环境变量并激活。
```Shell
cd ~
vim .bash_profile
```
```INI
... 此处省略上面的代码...
export PATH=$PATH:/usr/local/python37/bin
... 此处省略下面的代码...
```
```Shell
source .bash_profile
```
7. 注册软链接(符号链接)- 这一步不是必须的。
```Shell
ln -s /usr/local/python37/bin/python3 /usr/bin/python3
ln -s /usr/local/python37/bin/pip3 /usr/bin/pip3
```
8. 测试Python环境是否更新成功。
```Shell
python3 --version
python --version
```
### 项目目录结构
假设项目文件夹为`project`,下面的四个子目录分别是:`conf`、`logs`、`src`和`venv`分别用来保存项目的配置文件、日志文件、源代码和虚拟环境。其中,`conf`目录下的子目录`cert`中保存了配置HTTPS需要使用的证书和密钥`src`目录下的项目代码可以通过版本控制工具从代码仓库中检出虚拟环境可以通过venv或其他工具进行创建。
```
project
├── conf
│   ├── cert
│   │   ├── 214915882850706.key
│   │   └── 214915882850706.pem
│   ├── nginx.conf
│   └── uwsgi.ini
├── logs
│   ├── access.log
│   ├── error.log
│   └── uwsgi.log
├── code
│   └── fangall
│   ├── api
│   ├── common
│   ├── fang
│   ├── rent
│   ├── user
│   ├── manage.py
│   ├── README.md
│   ├── static
│   └── templates
└── venv
├── bin
│   ├── activate
│   ├── activate.csh
│   ├── activate.fish
│   ├── celery
│   ├── celerybeat
│   ├── celeryd
│   ├── celeryd-multi
│   ├── coverage
│   ├── coverage3
│   ├── coverage-3.7
│   ├── django-admin
│   ├── django-admin.py
│   ├── easy_install
│   ├── easy_install-3.7
│   ├── pip
│   ├── pip3
│   ├── pip3.7
│   ├── __pycache__
│   ├── pyrsa-decrypt
│   ├── pyrsa-decrypt-bigfile
│   ├── pyrsa-encrypt
│   ├── pyrsa-encrypt-bigfile
│   ├── pyrsa-keygen
│   ├── pyrsa-priv2pub
│   ├── pyrsa-sign
│   ├── pyrsa-verify
│   ├── python -> python3
│   ├── python3 -> /usr/bin/python3
│   └── uwsgi
├── include
├── lib
│   └── python3.7
├── lib64 -> lib
├── pip-selfcheck.json
└── pyvenv.cfg
```
下面以阿里云为例,简单说明如何为项目注册域名、解析域名以及购买权威机构颁发的证书。
1. [注册域名](https://wanwang.aliyun.com/domain/)。
![](./res/aliyun-domain.png)
2. [域名备案](https://beian.aliyun.com/)。
![](./res/aliyun-keeprecord.png)
3. [域名解析](https://dns.console.aliyun.com/#/dns/domainList)。
![](./res/aliyun-dnslist.png)
![](./res/aliyun-resolve-settings.png)
4. [购买证书](https://www.aliyun.com/product/cas)。
![](./res/aliyun-certificate.png)
### uWSGI的配置
1. 在`project`目录下创建并激活虚拟环境。
```Shell
python3 -m venv venv
source venv/bin/activate
```
2. 安装项目依赖项。
```Shell
pip install -r requirements.txt
```
3. 通过pip安装uWSGI。
```Shell
pip install uwsgi
```
4. 修改uWSGI的配置文件`/root/project/conf/uwsgi.ini`)。
```INI
[uwsgi]
# 配置前导路径
base=/root/project
# 配置项目名称
name=fangtx
# 守护进程
master=true
# 进程个数
processes=4
# 虚拟环境
pythonhome=%(base)/venv
# 项目地址
chdir=%(base)/code/%(name)
# 指定python解释器
pythonpath=%(pythonhome)/bin/python
# 指定uwsgi文件
module=%(name).wsgi
# 通信的地址和端口(自己服务器的IP地址和端口)
socket=172.18.61.250:8000
# 日志文件地址
logto=%(base)/logs/uwsgi.log
```
> 说明可以先将“通信的地址和端口”项等号前面改为http来进行测试如果没有问题再改回成socket然后通过Nginx来实现项目的“动静分离”静态资源交给Nginx处理动态内容交给 uWSGI处理。按照下面的方式可以启动uWSGI服务器。
5. 启动服务器。
```Shell
uwsgi --ini conf/uwsgi.ini
```
### Nginx的配置
1. 安装Nginx。
```Shell
yum -y install nginx
```
2. 修改全局配置文件(`/etc/nginx/nginx.conf`)。
```Nginx
# 配置用户
user root;
# 工作进程数(建议跟CPU的核数量一致)
worker_processes auto;
# 错误日志
error_log /var/log/nginx/error.log;
# 进程文件
pid /run/nginx.pid;
# 包含其他的配置
include /usr/share/nginx/modules/*.conf;
# 工作模式(多路IO复用方式)和连接上限
events {
use epoll;
worker_connections 1024;
}
# HTTP服务器相关配置
http {
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 访问日志
access_log /var/log/nginx/access.log main;
# 开启高效文件传输模式
sendfile on;
# 用sendfile传输文件时有利于改善性能
tcp_nopush on;
# 禁用Nagle来解决交互性问题
tcp_nodelay on;
# 客户端保持连接时间
keepalive_timeout 30;
types_hash_max_size 2048;
# 包含MIME类型的配置
include /etc/nginx/mime.types;
# 默认使用二进制流格式
default_type application/octet-stream;
# 包含其他配置文件
include /etc/nginx/conf.d/*.conf;
# 包含项目的Nginx配置文件
include /root/project/conf/*.conf;
}
```
3. 编辑局部配置文件(`/root/project/conf/nginx.conf`)。
```Nginx
server {
listen 80;
server_name _;
access_log /root/project/logs/access.log;
error_log /root/project/logs/error.log;
location / {
include uwsgi_params;
uwsgi_pass 172.18.61.250:8000;
}
location /static/ {
alias /root/project/static/;
expires 30d;
}
}
server {
listen 443;
server_name _;
ssl on;
access_log /root/project/logs/access.log;
error_log /root/project/logs/error.log;
ssl_certificate /root/project/conf/cert/214915882850706.pem;
ssl_certificate_key /root/project/conf/cert/214915882850706.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
include uwsgi_params;
uwsgi_pass 172.18.61.250:8000;
}
location /static/ {
alias /root/project/static/;
expires 30d;
}
}
```
到此为止我们可以启动Nginx来访问我们的应用程序HTTP和HTTPS都是没有问题的如果Nginx已经运行在修改配置文件后我们可以用下面的命令重新启动Nginx。
4. 重启Nginx服务器。
```Shell
nginx -s reload
```
```Shell
systemctl restart nginx
```
> 说明可以对Django项目使用`python manage.py collectstatic`命令将静态资源收集到指定目录下,要做到这点只需要在项目的配置文件`settings.py`中添加`STATIC_ROOT`配置即可。
#### 负载均衡配置
下面的配置中我们使用Nginx实现负载均衡为另外的三个Nginx服务器通过Docker创建提供反向代理服务。
```Shell
docker run -d -p 801:80 --name nginx1 nginx:latest
docker run -d -p 802:80 --name nginx2 nginx:latest
docker run -d -p 803:80 --name nginx3 nginx:latest
```
```Nginx
user root;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
# 为HTTP服务配置负载均衡
http {
upstream fangtx {
server 172.18.61.250:801 weight=4;
server 172.18.61.250:802 weight=2;
server 172.18.61.250:803 weight=2;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl;
listen [::]:443 ssl;
ssl on;
access_log /root/project/logs/access.log;
error_log /root/project/logs/error.log;
ssl_certificate /root/project/conf/cert/214915882850706.pem;
ssl_certificate_key /root/project/conf/cert/214915882850706.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
proxy_pass http://fangtx;
}
}
}
```
> 说明Nginx在配置负载均衡时默认使用WRR加权轮询算法除此之外还支持ip_hash、fair需要安装upstream_fair模块和url_hash算法。此外在配置upstream模块时可以指定服务器的状态值包括backup备份机器其他服务器不可用时才将请求分配到该机器、down、fail_timeout请求失败达到max_fails后的暂停服务时间、max_fails允许请求失败的次数和weight轮询的权重
### Keepalived
当使用Nginx进行负载均衡配置时要考虑负载均衡服务器宕机的情况。为此可以使用Keepalived来实现负载均衡主机和备机的热切换从而保证系统的高可用性。Keepalived的配置还是比较复杂通常由专门做运维的人进行配置一个基本的配置可以参照[《Keepalived的配置和使用》](https://www.jianshu.com/p/dd93bc6d45f5)。
### MySQL主从复制
下面还是基于Docker来演示如何配置MySQL主从复制。我们事先准备好MySQL的配置文件以及保存MySQL数据和运行日志的目录然后通过Docker的数据卷映射来指定容器的配置、数据和日志文件的位置。
```Shell
root
└── mysql
├── conf
│   ├── master
│   │   └── mysqld.cnf
│   ├── slave1
│   │   └── mysqld.cnf
│   ├── slave2
│   │   └── mysqld.cnf
│   └── slave3
│   └── mysqld.cnf
└── data
├── master
├── slave1
├── slave2
└── slave3
```
1. MySQL的配置文件master和slave的配置文件需要不同的server-id
```
[mysqld]
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/run/mysqld/mysqld.sock
datadir=/var/lib/mysql
log-error=/var/log/mysql/error.log
server-id=1
log_bin=/var/log/mysql/mysql-bin.log
expire_logs_days=30
max_binlog_size=256M
symbolic-links=0
```
2. 创建和配置master。
```Shell
docker run -d -p 3306:3306 --name mysql57 \
-v /root/mysql/conf/master:/etc/mysql/mysql.conf.d \
-v /root/mysql/data/master:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
docker exec -it mysql57 /bin/bash
```
```Shell
mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.7.23-log MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> grant replication slave on *.* to 'slave'@'%' identified by 'iamslave';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 590 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
mysql> quit
Bye
exit
```
上面创建Docker容器时使用的`-v`参数(`--volume`)表示映射数据卷,冒号前是宿主机的目录,冒号后是容器中的目录,这样相当于将宿主机中的目录挂载到了容器中。
3. 创建和配置slave。
```Shell
docker run -d -p 3307:3306 --name mysql57-slave-1 \
-v /root/mysql/conf/slave1:/etc/mysql/mysql.conf.d \
-v /root/mysql/data/slave1:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--link mysql57:mysql57 mysql:5.7
docker exec -it mysql57-slave-1 /bin/bash
```
```Shell
mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.23-log MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> reset slave;
Query OK, 0 rows affected (0.02 sec)
mysql> change master to master_host='mysql57', master_user='slave', master_password='iamslave', master_log_file='mysql-bin.000003', master_log_pos=590;
Query OK, 0 rows affected, 2 warnings (0.03 sec)
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: mysql57
Master_User: slave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 590
Relay_Log_File: f352f05eb9d0-relay-bin.000002
Relay_Log_Pos: 320
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 590
Relay_Log_Space: 534
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 30c38043-ada1-11e8-8fa1-0242ac110002
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
mysql> quit
Bye
exit
```
接下来可以如法炮制配置出slave2和slave3这样就可以搭建起一个“一主带三从”的主从复制环境。上面创建创建容器时使用的`--link`参数用来配置容器在网络上的主机名(网络地址别名),下一节有这个知识点的介绍。
### Docker
事实上项目上线中最为麻烦的事情就是配置软件运行环境环境的差异会给软件的安装和部署带来诸多的麻烦而Docker正好可以解决这个问题。关于Docker在之前的文档中我们已经介绍过了接下来我们对Docker的知识做一些必要的补充。
1. 创建镜像文件。
将容器保存成镜像:
```Shell
docker commit -m "..." -a "..." <container-name> jackfrued/<image-name>
```
使用Dockerfile构建镜像
```Dockerfile
# 指定基础镜像文件
FROM centos:latest
# 指定维护者信息
MAINTAINER jackfrued
# 执行命令
RUN yum -y install gcc
RUN cd ~
RUN mkdir -p project/code
RUN mkdir -p project/logs
# 拷贝文件
COPY ...
# 暴露端口
EXPOSE ...
# 在容器启动时执行命令
CMD ~/init.sh
```
```Shell
docker build -t jackfrued/<image-name> .
```
2. 镜像的导入和导出。
```Shell
docker save -o <file-name>.tar <image-name>:<version>
docker load -i <file-name>.tar
```
3. 推送到DockerHub服务器。
```Shell
docker tag <image-name>:<version> jackfrued/<name>
docker login
docker push jackfrued/<name>
```
4. 容器之间的通信。
```Shell
docker run --link <container-name>:<alias-name>
```
如果我们能够在Docker中完成项目的部署并且将整个部署好的容器打包成镜像文件进行分发和安装这样就可以解决项目在多个节点上进行部署时可能遇到的麻烦而且整个部署可以在很短的时间内完成。
### Supervisor
[Supervisor](https://github.com/Supervisor/supervisor)是一个用Python写的进程管理工具可以很方便的用来在类Unix系统下启动、重启自动重启程序和关闭进程。
1. 安装Supervisor。
```Shell
yum -y install supervisor
```
2. 查看Supervisor的配置文件。
```Shell
vim /etc/supervisord.conf
```
```INI
; 此处省略上面的代码
; The [include] section can just contain the "files" setting. This
; setting can list multiple files (separated by whitespace or
; newlines). It can also contain wildcards. The filenames are
; interpreted as relative to this file. Included files *cannot*
; include files themselves.
[include]
files = supervisord.d/*.ini
```
可以看出自定义的管理配置代码可以放在`/etc/supervisord.d`目录中,并且文件名以`ini`作为后缀即可。
3. 编写管理配置代码。
```Shell
cd /etc/supervisord.d
vim fangtx.ini
```
```INI
```
4. 启动Supervisor服务和查看状态。
```Shell
systemctl start supervisord
supervisorctl status
```
### 其他服务
1. 常用开源软件。
| 功能 | 开源方案 |
| ------------------- | ------------------------- |
| 版本控制工具 | Git、Mercurial、SVN |
| 缺陷管理 | Redmine、Mantis |
| 负载均衡 | Nginx、LVS、HAProxy |
| 邮件服务 | Postfix、Sendmail |
| HTTP服务 | Nginx、Apache |
| 消息队列 | RabbitMQ、ZeroMQ、Redis |
| 文件系统 | FastDFS |
| 基于位置服务LBS | MongoDB、Redis |
| 监控服务 | Nagios、Zabbix |
| 关系型数据库 | MySQL、PostgreSQL |
| 非关系型数据库 | MongoDB、Redis、Cassandra |
| 搜索引擎 | ElasticSearch、Solr |
| 缓存服务 | Mamcached、Redis |
2. 常用云服务。
| 功能 | 可用的云服务 |
| -------------- | --------------------------------------- |
| 团队协作工具 | Teambition、钉钉 |
| 代码托管平台 | Github、Gitee、CODING |
| 邮件服务 | SendCloud |
| 云存储CDN | 七牛、OSS、LeanCloud、Bmob、又拍云、AWS |
| 移动端推送 | 极光、友盟、百度 |
| 即时通信 | 环信、融云 |
| 短信服务 | 云片、极光、Luosimao、又拍云 |
| 第三方登录 | 友盟、ShareSDK |
| 网站监控和统计 | 阿里云监控、监控宝、百度云观测、小鸟云 |

View File

@ -145,19 +145,26 @@
- 使用进程 - fork函数 / multiprocessing模块 / 进程池 / 进程间通信 - 使用进程 - fork函数 / multiprocessing模块 / 进程池 / 进程间通信
- 使用线程 - thread模块 / threading模块 / Thread类 / Lock类 - 使用线程 - thread模块 / threading模块 / Thread类 / Lock类
#### Day14 - [网络编程入门](./Day01-15/Day14/网络编程入门.md) #### Day14-A - [网络编程入门](./Day01-15/Day14/网络编程入门.md)
- 计算机网络基础 - 计算机网络发展史 / “TCP-IP”模型 / IP地址 / 端口 / 协议 / 其他相关概念 - 计算机网络基础 - 计算机网络发展史 / “TCP-IP”模型 / IP地址 / 端口 / 协议 / 其他相关概念
- 网络应用架构 - “客户端-服务器”架构 / “浏览器-服务器”架构 - 网络应用架构 - “客户端-服务器”架构 / “浏览器-服务器”架构
- Python网络编程 - 套接字的概念 / socket模块 / socket函数 / 创建TCP服务器 / 创建TCP客户端 / 创建UDP服务器 / 创建UDP客户端 / SocketServer模块 - Python网络编程 - 套接字的概念 / socket模块 / socket函数 / 创建TCP服务器 / 创建TCP客户端 / 创建UDP服务器 / 创建UDP客户端 / SocketServer模块
#### Day15 - [网络应用开发](./Day01-15/Day15/网络应用开发.md) #### Day14-B - [网络应用开发](./Day01-15/Day15/网络应用开发.md)
- 访问网络API - 网络API概述 / 访问URL / requests模块 / 解析JSON格式数据 - 访问网络API - 网络API概述 / 访问URL / requests模块 / 解析JSON格式数据
- 文件传输 - FTP协议 / ftplib模块 / 交互式FTP应用 - 文件传输 - FTP协议 / ftplib模块 / 交互式FTP应用
- 电子邮件 - SMTP协议 / POP3协议 / IMAP协议 / smtplib模块 / poplib模块 / imaplib模块 - 电子邮件 - SMTP协议 / POP3协议 / IMAP协议 / smtplib模块 / poplib模块 / imaplib模块
- 短信服务 - twilio模块 / 国内的短信服务 - 短信服务 - twilio模块 / 国内的短信服务
#### Day15 - 图像和文档处理
- 用Pillow处理图片
- 读写Word文档
- 读写Excel文件
- 生成PDF文件
### Day16~Day20 - [Python语言进阶 ](./Day16-20/Python语言进阶.md) ### Day16~Day20 - [Python语言进阶 ](./Day16-20/Python语言进阶.md)
- 常用数据结构 - 常用数据结构

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -1,4 +1,4 @@
## 玩转PyCharm(上) ## 玩转PyCharm
PyCharm是由JetBrains公司开发的提供给Python专业的开发者的一个集成开发环境它最大的优点是能够大大提升Python开发者的工作效率为开发者集成了很多用起来非常顺手的功能包括代码调试、高亮语法、代码跳转、智能提示、自动补全、单元测试、版本控制等等。此外PyCharm还提供了对一些高级功能的支持包括支持基于Django框架的Web开发、。 PyCharm是由JetBrains公司开发的提供给Python专业的开发者的一个集成开发环境它最大的优点是能够大大提升Python开发者的工作效率为开发者集成了很多用起来非常顺手的功能包括代码调试、高亮语法、代码跳转、智能提示、自动补全、单元测试、版本控制等等。此外PyCharm还提供了对一些高级功能的支持包括支持基于Django框架的Web开发、。