提交 6d7fb903 编写于 作者: 骆昊的技术专栏's avatar 骆昊的技术专栏

更新了第1部分的文档

上级 c3a9b74b
...@@ -47,58 +47,44 @@ Linux环境自带了Python 2.x版本,但是如果要更新到3.x的版本, ...@@ -47,58 +47,44 @@ Linux环境自带了Python 2.x版本,但是如果要更新到3.x的版本,
安装依赖库(因为没有这些依赖库可能在源代码构件安装时因为缺失底层依赖库而失败)。 安装依赖库(因为没有这些依赖库可能在源代码构件安装时因为缺失底层依赖库而失败)。
```Shell ```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
yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel
``` ```
下载Python源代码并解压缩到指定目录。 下载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.6.1/Python-3.6.1.tar.xz xz -d Python-3.7.0.tar.xz
xz -d Python-3.6.1.tar.xz tar -xvf Python-3.7.0.tar
tar -xvf Python-3.6.1.tar
``` ```
切换至Python源代码目录并执行下面的命令进行配置和安装。 切换至Python源代码目录并执行下面的命令进行配置和安装。
```Shell ```Shell
cd Python-3.7.0
cd Python-3.6.1 ./configure --prefix=/usr/local/python37 --enable-optimizations
./configure --prefix=/usr/local/python3.6 --enable-optimizations
make && make install make && make install
``` ```
配置PATH环境变量并使其生效,这需要修改用户主目录下名为.bash_profile的文件 修改用户主目录下名为.bash_profile的文件,配置PATH环境变量并使其生效
```Shell ```Shell
cd ~ cd ~
vim .bash_profile vim .bash_profile
``` ```
```Shell ```Shell
# ... 此处省略上面的代码 ...
# 此处省略上面的代码 export PATH=$PATH:/usr/local/python37/bin
PATH=$PATH:/usr/local/python3.6/bin
# 此处省略下面的代码 # ... 此处省略下面的代码 ...
``` ```
```Shell ```Shell
source .bash_profile source .bash_profile
``` ```
最后还可以创建一个符号链接(如果不知道为什么也可以暂时不管这个问题啦)。
```Shell
ln -s /usr/local/python3.6/bin/python3 /usr/bin/python3
```
#### 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解释器的命令。
...@@ -110,13 +96,11 @@ MacOS也是自带了Python 2.x版本的,可以通过[Python的官方网站](ht ...@@ -110,13 +96,11 @@ MacOS也是自带了Python 2.x版本的,可以通过[Python的官方网站](ht
在终端或命令行提示符中键入下面的命令。 在终端或命令行提示符中键入下面的命令。
```Shell ```Shell
python --version python --version
``` ```
当然也可以先输入python进入交互式环境,再执行以下的代码检查Python的版本。 当然也可以先输入python进入交互式环境,再执行以下的代码检查Python的版本。
```Python ```Python
import sys import sys
print(sys.version_info) print(sys.version_info)
...@@ -128,7 +112,6 @@ print(sys.version) ...@@ -128,7 +112,6 @@ print(sys.version)
可以用文本编辑工具(推荐使用Sublime、Atom、TextMate、VSCode等高级文本编辑工具)编写Python源代码并将其命名为hello.py保存起来,代码内容如下所示。 可以用文本编辑工具(推荐使用Sublime、Atom、TextMate、VSCode等高级文本编辑工具)编写Python源代码并将其命名为hello.py保存起来,代码内容如下所示。
```Python ```Python
print('hello, world!') print('hello, world!')
``` ```
...@@ -137,7 +120,6 @@ print('hello, world!') ...@@ -137,7 +120,6 @@ print('hello, world!')
切换到源代码所在的目录并执行下面的命令,看看屏幕上是否输出了"hello, world!"。 切换到源代码所在的目录并执行下面的命令,看看屏幕上是否输出了"hello, world!"。
```Shell ```Shell
python hello.py python hello.py
``` ```
...@@ -149,16 +131,13 @@ python hello.py ...@@ -149,16 +131,13 @@ python hello.py
2. 多行注释 - 三个引号开头,三个引号结尾 2. 多行注释 - 三个引号开头,三个引号结尾
```Python ```Python
""" """
第一个Python程序 - hello, world! 第一个Python程序 - hello, world!
向伟大的Dennis M. Ritchie先生致敬 向伟大的Dennis M. Ritchie先生致敬
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-26 Date: 2018-02-26
""" """
print('hello, world!') print('hello, world!')
...@@ -243,7 +222,6 @@ PyCharm的安装、配置和使用我们在后面会进行介绍。 ...@@ -243,7 +222,6 @@ PyCharm的安装、配置和使用我们在后面会进行介绍。
1. 在Python交互环境中下面的代码查看结果并将内容翻译成中文。 1. 在Python交互环境中下面的代码查看结果并将内容翻译成中文。
```Python ```Python
import this import this
Beautiful is better than ugly. Beautiful is better than ugly.
...@@ -270,7 +248,6 @@ PyCharm的安装、配置和使用我们在后面会进行介绍。 ...@@ -270,7 +248,6 @@ PyCharm的安装、配置和使用我们在后面会进行介绍。
2. 学习使用turtle在屏幕上绘制图形。 2. 学习使用turtle在屏幕上绘制图形。
```Python ```Python
import turtle import turtle
turtle.pensize(4) turtle.pensize(4)
......
...@@ -2,7 +2,7 @@ ...@@ -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://www.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)科普一下。
### 变量和类型 ### 变量和类型
...@@ -35,13 +35,11 @@ ...@@ -35,13 +35,11 @@
```Python ```Python
""" """
使用变量保存数据并进行算术运算 使用变量保存数据并进行算术运算
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27 Date: 2018-02-27
""" """
a = 321 a = 321
...@@ -58,7 +56,6 @@ print(a ** b) ...@@ -58,7 +56,6 @@ print(a ** b)
```Python ```Python
""" """
使用input函数输入 使用input函数输入
使用int()进行类型转换 使用int()进行类型转换
用占位符格式化输出的字符串 用占位符格式化输出的字符串
...@@ -66,7 +63,6 @@ print(a ** b) ...@@ -66,7 +63,6 @@ print(a ** b)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27 Date: 2018-02-27
""" """
a = int(input('a = ')) a = int(input('a = '))
...@@ -83,13 +79,11 @@ print('%d ** %d = %d' % (a, b, a ** b)) ...@@ -83,13 +79,11 @@ print('%d ** %d = %d' % (a, b, a ** b))
```Python ```Python
""" """
使用type()检查变量的类型 使用type()检查变量的类型
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27 Date: 2018-02-27
""" """
a = 100 a = 100
...@@ -140,13 +134,11 @@ Python支持多种运算符,下表大致按照优先级从高到低的顺序 ...@@ -140,13 +134,11 @@ Python支持多种运算符,下表大致按照优先级从高到低的顺序
```Python ```Python
""" """
运算符的使用 运算符的使用
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27 Date: 2018-02-27
""" """
a = 5 a = 5
...@@ -181,14 +173,12 @@ print(flag2 is not False) ...@@ -181,14 +173,12 @@ print(flag2 is not False)
```Python ```Python
""" """
将华氏温度转换为摄氏温度 将华氏温度转换为摄氏温度
F = 1.8C + 32 F = 1.8C + 32
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27 Date: 2018-02-27
""" """
f = float(input('请输入华氏温度: ')) f = float(input('请输入华氏温度: '))
...@@ -201,13 +191,11 @@ print('%.1f华氏度 = %.1f摄氏度' % (f, c)) ...@@ -201,13 +191,11 @@ print('%.1f华氏度 = %.1f摄氏度' % (f, c))
```Python ```Python
""" """
输入半径计算圆的周长和面积 输入半径计算圆的周长和面积
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27 Date: 2018-02-27
""" """
import math import math
...@@ -224,13 +212,11 @@ print('面积: %.2f' % area) ...@@ -224,13 +212,11 @@ print('面积: %.2f' % area)
```Python ```Python
""" """
输入年份 如果是闰年输出True 否则输出False 输入年份 如果是闰年输出True 否则输出False
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-27 Date: 2018-02-27
""" """
year = int(input('请输入年份: ')) year = int(input('请输入年份: '))
......
...@@ -10,13 +10,11 @@ ...@@ -10,13 +10,11 @@
```Python ```Python
""" """
用户身份验证 用户身份验证
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28 Date: 2018-02-28
""" """
username = input('请输入用户名: ') username = input('请输入用户名: ')
...@@ -28,7 +26,6 @@ if username == 'admin' and password == '123456': ...@@ -28,7 +26,6 @@ if username == 'admin' and password == '123456':
print('身份验证成功!') print('身份验证成功!')
else: else:
print('身份验证失败!') print('身份验证失败!')
``` ```
唯一需要说明的是和C/C++、Java等语言不同,Python中没有用花括号来构造代码块而是使用了缩进的方式来设置代码的层次结构,如果`if`条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了,换句话说连续的代码如果又保持了相同的缩进那么它们属于同一个代码块,相当于是一个执行的整体。 唯一需要说明的是和C/C++、Java等语言不同,Python中没有用花括号来构造代码块而是使用了缩进的方式来设置代码的层次结构,如果`if`条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了,换句话说连续的代码如果又保持了相同的缩进那么它们属于同一个代码块,相当于是一个执行的整体。
...@@ -39,7 +36,6 @@ $$f(x)=\begin{cases} 3x-5&\text{(x>1)}\\x+2&\text{(-1}\leq\text{x}\leq\text{1)}\ ...@@ -39,7 +36,6 @@ $$f(x)=\begin{cases} 3x-5&\text{(x>1)}\\x+2&\text{(-1}\leq\text{x}\leq\text{1)}\
```Python ```Python
""" """
分段函数求值 分段函数求值
3x - 5 (x > 1) 3x - 5 (x > 1)
...@@ -49,7 +45,6 @@ f(x) = x + 2 (-1 <= x <= 1) ...@@ -49,7 +45,6 @@ f(x) = x + 2 (-1 <= x <= 1)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28 Date: 2018-02-28
""" """
x = float(input('x = ')) x = float(input('x = '))
...@@ -60,14 +55,12 @@ elif x >= -1: ...@@ -60,14 +55,12 @@ elif x >= -1:
else: else:
y = 5 * x + 3 y = 5 * x + 3
print('f(%.2f) = %.2f' % (x, y)) print('f(%.2f) = %.2f' % (x, y))
``` ```
当然根据实际开发的需要,分支结构是可以嵌套的,例如判断是否通关以后还要根据你获得的宝物或者道具的数量对你的表现给出等级(比如点亮两颗或三颗星星),那么我们就需要在`if`的内部构造出一个新的分支结构,同理`elif``else`中也可以再构造新的分支,我们称之为嵌套的分支结构,也就是说上面的代码也可以写成下面的样子。 当然根据实际开发的需要,分支结构是可以嵌套的,例如判断是否通关以后还要根据你获得的宝物或者道具的数量对你的表现给出等级(比如点亮两颗或三颗星星),那么我们就需要在`if`的内部构造出一个新的分支结构,同理`elif``else`中也可以再构造新的分支,我们称之为嵌套的分支结构,也就是说上面的代码也可以写成下面的样子。
```Python ```Python
""" """
分段函数求值 分段函数求值
3x - 5 (x > 1) 3x - 5 (x > 1)
f(x) = x + 2 (-1 <= x <= 1) f(x) = x + 2 (-1 <= x <= 1)
...@@ -76,7 +69,6 @@ f(x) = x + 2 (-1 <= x <= 1) ...@@ -76,7 +69,6 @@ f(x) = x + 2 (-1 <= x <= 1)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28 Date: 2018-02-28
""" """
x = float(input('x = ')) x = float(input('x = '))
...@@ -98,13 +90,11 @@ print('f(%.2f) = %.2f' % (x, y)) ...@@ -98,13 +90,11 @@ print('f(%.2f) = %.2f' % (x, y))
```Python ```Python
""" """
英制单位英寸和公制单位厘米互换 英制单位英寸和公制单位厘米互换
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28 Date: 2018-02-28
""" """
value = float(input('请输入长度: ')) value = float(input('请输入长度: '))
...@@ -115,20 +105,17 @@ elif unit == 'cm' or unit == '厘米': ...@@ -115,20 +105,17 @@ elif unit == 'cm' or unit == '厘米':
print('%f厘米 = %f英寸' % (value, value / 2.54)) print('%f厘米 = %f英寸' % (value, value / 2.54))
else: else:
print('请输入有效的单位') print('请输入有效的单位')
``` ```
#### 练习2:掷骰子决定做什么 #### 练习2:掷骰子决定做什么
```Python ```Python
""" """
掷骰子决定做什么事情 掷骰子决定做什么事情
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28 Date: 2018-02-28
""" """
from random import randint from random import randint
...@@ -147,7 +134,6 @@ elif face == 5: ...@@ -147,7 +134,6 @@ elif face == 5:
else: else:
result = '讲冷笑话' result = '讲冷笑话'
print(result) print(result)
``` ```
> **说明:**上面的代码中使用了random模块的randint函数生成指定范围的随机数来模拟掷骰子。 > **说明:**上面的代码中使用了random模块的randint函数生成指定范围的随机数来模拟掷骰子。
...@@ -155,7 +141,6 @@ print(result) ...@@ -155,7 +141,6 @@ print(result)
```Python ```Python
""" """
百分制成绩转等级制成绩 百分制成绩转等级制成绩
90分以上 --> A 90分以上 --> A
80分~89分 --> B 80分~89分 --> B
...@@ -166,7 +151,6 @@ print(result) ...@@ -166,7 +151,6 @@ print(result)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28 Date: 2018-02-28
""" """
score = float(input('请输入成绩: ')) score = float(input('请输入成绩: '))
...@@ -181,20 +165,17 @@ elif score >= 60: ...@@ -181,20 +165,17 @@ elif score >= 60:
else: else:
grade = 'E' grade = 'E'
print('对应的等级是:', grade) print('对应的等级是:', grade)
``` ```
#### 练习4:输入三条边长如果能构成三角形就计算周长和面积 #### 练习4:输入三条边长如果能构成三角形就计算周长和面积
```Python ```Python
""" """
判断输入的边长能否构成三角形 判断输入的边长能否构成三角形
如果能则计算出三角形的周长和面积 如果能则计算出三角形的周长和面积
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28 Date: 2018-02-28
""" """
import math import math
...@@ -209,7 +190,6 @@ if a + b > c and a + c > b and b + c > a: ...@@ -209,7 +190,6 @@ if a + b > c and a + c > b and b + c > a:
print('面积: %f' % (area)) print('面积: %f' % (area))
else: else:
print('不能构成三角形') print('不能构成三角形')
``` ```
> **说明:**上面的代码中使用了`math`模块的`sqrt`函数来计算平方根。用边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。 > **说明:**上面的代码中使用了`math`模块的`sqrt`函数来计算平方根。用边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。
...@@ -217,13 +197,11 @@ else: ...@@ -217,13 +197,11 @@ else:
```Python ```Python
""" """
输入月收入和五险一金计算个人所得税 输入月收入和五险一金计算个人所得税
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-02-28 Date: 2018-02-28
""" """
salary = float(input('本月收入: ')) salary = float(input('本月收入: '))
...@@ -256,7 +234,6 @@ else: ...@@ -256,7 +234,6 @@ else:
tax = abs(diff * rate - deduction) tax = abs(diff * rate - deduction)
print('个人所得税: ¥%.2f元' % tax) print('个人所得税: ¥%.2f元' % tax)
print('实际到手收入: ¥%.2f元' % (diff + 3500 - tax)) print('实际到手收入: ¥%.2f元' % (diff + 3500 - tax))
``` ```
>**说明:**上面的代码中使用了Python内置的`abs()`函数取绝对值来处理`-0`的问题。 >**说明:**上面的代码中使用了Python内置的`abs()`函数取绝对值来处理`-0`的问题。
...@@ -10,13 +10,11 @@ ...@@ -10,13 +10,11 @@
```Python ```Python
""" """
用for循环实现1~100求和 用for循环实现1~100求和
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01 Date: 2018-03-01
""" """
sum = 0 sum = 0
...@@ -35,13 +33,11 @@ print(sum) ...@@ -35,13 +33,11 @@ print(sum)
```Python ```Python
""" """
用for循环实现1~100之间的偶数求和 用for循环实现1~100之间的偶数求和
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01 Date: 2018-03-01
""" """
sum = 0 sum = 0
...@@ -54,13 +50,11 @@ print(sum) ...@@ -54,13 +50,11 @@ print(sum)
```Python ```Python
""" """
用for循环实现1~100之间的偶数求和 用for循环实现1~100之间的偶数求和
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01 Date: 2018-03-01
""" """
sum = 0 sum = 0
...@@ -77,7 +71,6 @@ print(sum) ...@@ -77,7 +71,6 @@ print(sum)
```Python ```Python
""" """
猜数字游戏 猜数字游戏
计算机出一个1~100之间的随机数由人来猜 计算机出一个1~100之间的随机数由人来猜
计算机根据人猜的数字分别给出提示大一点/小一点/猜对了 计算机根据人猜的数字分别给出提示大一点/小一点/猜对了
...@@ -85,7 +78,6 @@ print(sum) ...@@ -85,7 +78,6 @@ print(sum)
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01 Date: 2018-03-01
""" """
import random import random
...@@ -105,7 +97,6 @@ while True: ...@@ -105,7 +97,6 @@ while True:
print('你总共猜了%d次' % counter) print('你总共猜了%d次' % counter)
if counter > 7: if counter > 7:
print('你的智商余额明显不足') print('你的智商余额明显不足')
``` ```
> **说明:**上面的代码中使用了`break`关键字来提前终止循环,需要注意的是`break`只能终止它所在的那个循环,这一点在使用嵌套的循环结构(下面会讲到)需要引起注意。除了`break`之外,还有另一个关键字是`continue`,它可以用来放弃本次循环后续的代码直接让循环进入下一轮。 > **说明:**上面的代码中使用了`break`关键字来提前终止循环,需要注意的是`break`只能终止它所在的那个循环,这一点在使用嵌套的循环结构(下面会讲到)需要引起注意。除了`break`之外,还有另一个关键字是`continue`,它可以用来放弃本次循环后续的代码直接让循环进入下一轮。
...@@ -114,7 +105,6 @@ if counter > 7: ...@@ -114,7 +105,6 @@ if counter > 7:
```Python ```Python
""" """
输出乘法口诀表(九九表) 输出乘法口诀表(九九表)
Version: 0.1 Version: 0.1
...@@ -127,7 +117,6 @@ for i in range(1, 10): ...@@ -127,7 +117,6 @@ for i in range(1, 10):
for j in range(1, i + 1): for j in range(1, i + 1):
print('%d*%d=%d' % (i, j, i * j), end='\t') print('%d*%d=%d' % (i, j, i * j), end='\t')
print() print()
``` ```
### 练习 ### 练习
...@@ -136,13 +125,11 @@ for i in range(1, 10): ...@@ -136,13 +125,11 @@ for i in range(1, 10):
```Python ```Python
""" """
输入一个正整数判断它是不是素数 输入一个正整数判断它是不是素数
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01 Date: 2018-03-01
""" """
from math import sqrt from math import sqrt
...@@ -158,20 +145,17 @@ if is_prime and num != 1: ...@@ -158,20 +145,17 @@ if is_prime and num != 1:
print('%d是素数' % num) print('%d是素数' % num)
else: else:
print('%d不是素数' % num) print('%d不是素数' % num)
``` ```
#### 练习2:输入两个正整数,计算最大公约数和最小公倍数。 #### 练习2:输入两个正整数,计算最大公约数和最小公倍数。
```Python ```Python
""" """
输入两个正整数计算最大公约数和最小公倍数 输入两个正整数计算最大公约数和最小公倍数
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01 Date: 2018-03-01
""" """
x = int(input('x = ')) x = int(input('x = '))
...@@ -190,7 +174,6 @@ for factor in range(x, 0, -1): ...@@ -190,7 +174,6 @@ for factor in range(x, 0, -1):
```Python ```Python
""" """
打印各种三角形图案 打印各种三角形图案
* *
...@@ -214,7 +197,6 @@ for factor in range(x, 0, -1): ...@@ -214,7 +197,6 @@ for factor in range(x, 0, -1):
Version: 0.1 Version: 0.1
Author: 骆昊 Author: 骆昊
Date: 2018-03-01 Date: 2018-03-01
""" """
row = int(input('请输入行数: ')) row = int(input('请输入行数: '))
...@@ -238,6 +220,5 @@ for i in range(row): ...@@ -238,6 +220,5 @@ for i in range(row):
for _ in range(2 * i + 1): for _ in range(2 * i + 1):
print('*', end='') print('*', end='')
print() print()
``` ```
## 总结和练习 ## 练习
### 练习清单
1. 寻找[“水仙花数”](https://baike.baidu.com/item/%E6%B0%B4%E4%BB%99%E8%8A%B1%E6%95%B0)
2. 寻找[“完美数”](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E6%95%B0/370913)
3. [“百钱百鸡”](https://baike.baidu.com/item/%E7%99%BE%E9%B8%A1%E7%99%BE%E9%92%B1/5857320)问题。
4. 生成[“斐波拉切数列”](https://baike.baidu.com/item/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97/99145)
5. Craps赌博游戏。
\ No newline at end of file
...@@ -12,9 +12,7 @@ $$C_M^N =\frac{M!}{N!(M-N)!}, \text{(M=7, N=3)} $$ ...@@ -12,9 +12,7 @@ $$C_M^N =\frac{M!}{N!(M-N)!}, \text{(M=7, N=3)} $$
```Python ```Python
""" """
输入M和N计算C(M,N) 输入M和N计算C(M,N)
""" """
m = int(input('m = ')) m = int(input('m = '))
...@@ -48,7 +46,6 @@ def factorial(num): ...@@ -48,7 +46,6 @@ def factorial(num):
求阶乘 求阶乘
:param num: 非负整数 :param num: 非负整数
:return: num的阶乘 :return: num的阶乘
""" """
result = 1 result = 1
...@@ -61,7 +58,6 @@ m = int(input('m = ')) ...@@ -61,7 +58,6 @@ m = int(input('m = '))
n = int(input('n = ')) n = int(input('n = '))
# 当需要计算阶乘的时候不用再写循环求阶乘而是直接调用已经定义好的函数 # 当需要计算阶乘的时候不用再写循环求阶乘而是直接调用已经定义好的函数
print(factorial(m) // factorial(n) // factorial(m - n)) print(factorial(m) // factorial(n) // factorial(m - n))
``` ```
> **说明:**Python的math模块中其实已经有一个factorial函数了,事实上要计算阶乘可以直接使用这个现成的函数而不用自己定义。下面例子中的某些函数其实Python中也是内置了,我们这里是为了讲解函数的定义和使用才把它们又实现了一遍,实际开发中不建议做这种低级的重复性的工作。 > **说明:**Python的math模块中其实已经有一个factorial函数了,事实上要计算阶乘可以直接使用这个现成的函数而不用自己定义。下面例子中的某些函数其实Python中也是内置了,我们这里是为了讲解函数的定义和使用才把它们又实现了一遍,实际开发中不建议做这种低级的重复性的工作。
...@@ -80,7 +76,6 @@ def roll_dice(n=2): ...@@ -80,7 +76,6 @@ def roll_dice(n=2):
摇色子 摇色子
:param n: 色子的个数 :param n: 色子的个数
:return: n颗色子点数之和 :return: n颗色子点数之和
""" """
total = 0 total = 0
...@@ -103,7 +98,6 @@ print(add(1, 2)) ...@@ -103,7 +98,6 @@ print(add(1, 2))
print(add(1, 2, 3)) print(add(1, 2, 3))
# 传递参数时可以不按照设定的顺序进行传递 # 传递参数时可以不按照设定的顺序进行传递
print(add(c=50, a=100, b=200)) print(add(c=50, a=100, b=200))
``` ```
我们给上面两个函数的参数都设定了默认值,这也就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值,所以在上面的代码中我们可以用各种不同的方式去调用`add`函数,这跟其他很多语言中函数重载的效果是一致的。 我们给上面两个函数的参数都设定了默认值,这也就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值,所以在上面的代码中我们可以用各种不同的方式去调用`add`函数,这跟其他很多语言中函数重载的效果是一致的。
...@@ -111,8 +105,8 @@ print(add(c=50, a=100, b=200)) ...@@ -111,8 +105,8 @@ print(add(c=50, a=100, b=200))
其实上面的`add`函数还有更好的实现方案,因为我们可能会对0个或多个参数进行加法运算,而具体有多少个参数是由调用者来决定,我们作为函数的设计者对这一点是一无所知的,因此在不确定参数个数的时候,我们可以使用可变参数,代码如下所示。 其实上面的`add`函数还有更好的实现方案,因为我们可能会对0个或多个参数进行加法运算,而具体有多少个参数是由调用者来决定,我们作为函数的设计者对这一点是一无所知的,因此在不确定参数个数的时候,我们可以使用可变参数,代码如下所示。
```Python ```Python
# 在参数前使用*表示args是可变参数 # 在参数名前面的*表示args是一个可变参数
# 也就是说调用add函数时传入的参数个数可以是0个或多个 # 即在调用add函数时可以传入0个或多个参数
def add(*args): def add(*args):
total = 0 total = 0
for val in args: for val in args:
...@@ -125,7 +119,6 @@ print(add(1)) ...@@ -125,7 +119,6 @@ print(add(1))
print(add(1, 2)) print(add(1, 2))
print(add(1, 2, 3)) print(add(1, 2, 3))
print(add(1, 3, 5, 7, 9)) print(add(1, 3, 5, 7, 9))
``` ```
### 用模块管理函数 ### 用模块管理函数
...@@ -142,7 +135,6 @@ def foo(): ...@@ -142,7 +135,6 @@ def foo():
foo() # 输出goodbye, world! foo() # 输出goodbye, world!
``` ```
当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。 当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。
...@@ -171,7 +163,6 @@ foo() # 输出hello, world! ...@@ -171,7 +163,6 @@ foo() # 输出hello, world!
from module2 import foo from module2 import foo
foo() # 输出goodbye, world! foo() # 输出goodbye, world!
``` ```
也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。 也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。
...@@ -184,7 +175,6 @@ import module2 as m2 ...@@ -184,7 +175,6 @@ import module2 as m2
m1.foo() m1.foo()
m2.foo() m2.foo()
``` ```
但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个`foo`,因为后导入的foo覆盖了之前导入的`foo` 但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个`foo`,因为后导入的foo覆盖了之前导入的`foo`
...@@ -196,7 +186,6 @@ from module1 import foo ...@@ -196,7 +186,6 @@ from module1 import foo
from module2 import foo from module2 import foo
foo() # 输出goodbye, world! foo() # 输出goodbye, world!
``` ```
test.py test.py
...@@ -206,7 +195,6 @@ from module2 import foo ...@@ -206,7 +195,6 @@ from module2 import foo
from module1 import foo from module1 import foo
foo() # 输出hello, world! foo() # 输出hello, world!
``` ```
需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是“\_\_main\_\_”。 需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是“\_\_main\_\_”。
...@@ -229,7 +217,6 @@ if __name__ == '__main__': ...@@ -229,7 +217,6 @@ if __name__ == '__main__':
foo() foo()
print('call bar()') print('call bar()')
bar() bar()
``` ```
test.py test.py
...@@ -238,7 +225,6 @@ test.py ...@@ -238,7 +225,6 @@ test.py
import module3 import module3
# 导入module3时 不会执行模块中if条件成立时的代码 因为模块的名字是module3而不是__main__ # 导入module3时 不会执行模块中if条件成立时的代码 因为模块的名字是module3而不是__main__
``` ```
### 练习 ### 练习
...@@ -286,7 +272,6 @@ if __name__ == '__main__': ...@@ -286,7 +272,6 @@ if __name__ == '__main__':
num = int(input('请输入正整数: ')) num = int(input('请输入正整数: '))
if is_palindrome(num) and is_prime(num): if is_palindrome(num) and is_prime(num):
print('%d是回文素数' % num) print('%d是回文素数' % num)
``` ```
通过上面的程序可以看出,当我们将代码中重复出现的和相对独立的功能抽取成函数后,我们可以组合使用这些函数来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。 通过上面的程序可以看出,当我们将代码中重复出现的和相对独立的功能抽取成函数后,我们可以组合使用这些函数来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。
...@@ -299,8 +284,8 @@ def foo(): ...@@ -299,8 +284,8 @@ def foo():
def bar(): # Python中可以在函数内部再定义函数 def bar(): # Python中可以在函数内部再定义函数
c = True c = True
print(a) print(a)
print(b) print(b)
print(c) print(c)
bar() bar()
...@@ -311,7 +296,6 @@ if __name__ == '__main__': ...@@ -311,7 +296,6 @@ if __name__ == '__main__':
a = 100 a = 100
# print(b) # NameError: name 'b' is not defined # print(b) # NameError: name 'b' is not defined
foo() foo()
``` ```
上面的代码能够顺利的执行并且打印出100和“hello”,但我们注意到了,在`bar`函数的内部并没有定义`a``b`两个变量,那么`a``b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些隐含标识符`min``len`等都属于内置作用域)。 上面的代码能够顺利的执行并且打印出100和“hello”,但我们注意到了,在`bar`函数的内部并没有定义`a``b`两个变量,那么`a``b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些隐含标识符`min``len`等都属于内置作用域)。
...@@ -328,7 +312,6 @@ if __name__ == '__main__': ...@@ -328,7 +312,6 @@ if __name__ == '__main__':
a = 100 a = 100
foo() foo()
print(a) # 100 print(a) # 100
``` ```
在调用`foo`函数后,我们发现`a`的值仍然是100,这是因为当我们在函数`foo`中写`a = 200`的时候,是重新定义了一个名字为`a`的局部变量,它跟全局作用域的`a`并不是同一个变量,因为局部作用域中有了自己的变量`a`,因此`foo`函数不再搜索全局作用域中的`a`。如果我们希望在`foo`函数中修改全局作用域中的`a`,代码如下所示。 在调用`foo`函数后,我们发现`a`的值仍然是100,这是因为当我们在函数`foo`中写`a = 200`的时候,是重新定义了一个名字为`a`的局部变量,它跟全局作用域的`a`并不是同一个变量,因为局部作用域中有了自己的变量`a`,因此`foo`函数不再搜索全局作用域中的`a`。如果我们希望在`foo`函数中修改全局作用域中的`a`,代码如下所示。
...@@ -344,7 +327,6 @@ if __name__ == '__main__': ...@@ -344,7 +327,6 @@ if __name__ == '__main__':
a = 100 a = 100
foo() foo()
print(a) # 200 print(a) # 200
``` ```
我们可以使用`global`关键字来指示`foo`函数中的变量`a`来自于全局作用域,如果全局作用域中没有`a`,那么下面一行的代码就会定义变量`a`并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用`nonlocal`关键字来指示变量来自于嵌套作用域,请大家自行试验。 我们可以使用`global`关键字来指示`foo`函数中的变量`a`来自于全局作用域,如果全局作用域中没有`a`,那么下面一行的代码就会定义变量`a`并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用`nonlocal`关键字来指示变量来自于嵌套作用域,请大家自行试验。
...@@ -363,6 +345,5 @@ def main(): ...@@ -363,6 +345,5 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
...@@ -54,7 +54,6 @@ def main(): ...@@ -54,7 +54,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典。 除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典。
...@@ -98,7 +97,6 @@ def main(): ...@@ -98,7 +97,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。 和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。
...@@ -127,7 +125,6 @@ def main(): ...@@ -127,7 +125,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
下面的代码实现了对列表的排序操作。 下面的代码实现了对列表的排序操作。
...@@ -152,7 +149,6 @@ def main(): ...@@ -152,7 +149,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
我们还可以使用列表的生成式语法来创建列表,代码如下所示。 我们还可以使用列表的生成式语法来创建列表,代码如下所示。
...@@ -183,7 +179,6 @@ def main(): ...@@ -183,7 +179,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
除了上面提到的生成器语法,Python中还有另外一种定义生成器的方式,就是通过`yield`关键字将一个普通函数改造成生成器函数。下面的代码演示了如何实现一个生成[斐波拉切数列](https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97)的生成器。所谓斐波拉切数列可以通过下面[递归](https://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92)的方法来进行定义: 除了上面提到的生成器语法,Python中还有另外一种定义生成器的方式,就是通过`yield`关键字将一个普通函数改造成生成器函数。下面的代码演示了如何实现一个生成[斐波拉切数列](https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97)的生成器。所谓斐波拉切数列可以通过下面[递归](https://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92)的方法来进行定义:
...@@ -211,7 +206,6 @@ def main(): ...@@ -211,7 +206,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 使用元组 ### 使用元组
...@@ -311,7 +305,6 @@ def main(): ...@@ -311,7 +305,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
> **说明**:Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符(后面的章节中会讲到),上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如`&`运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。 > **说明**:Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符(后面的章节中会讲到),上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如`&`运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。
...@@ -350,7 +343,6 @@ def main(): ...@@ -350,7 +343,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 练习 ### 练习
...@@ -375,7 +367,6 @@ def main(): ...@@ -375,7 +367,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
#### 练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成。 #### 练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成。
...@@ -410,7 +401,6 @@ def get_suffix(filename, has_dot=False): ...@@ -410,7 +401,6 @@ def get_suffix(filename, has_dot=False):
:param filename: 文件名 :param filename: 文件名
:param has_dot: 返回的后缀名是否需要带点 :param has_dot: 返回的后缀名是否需要带点
:return: 文件的后缀名 :return: 文件的后缀名
""" """
pos = filename.rfind('.') pos = filename.rfind('.')
...@@ -419,7 +409,6 @@ def get_suffix(filename, has_dot=False): ...@@ -419,7 +409,6 @@ def get_suffix(filename, has_dot=False):
return filename[index:] return filename[index:]
else: else:
return '' return ''
``` ```
#### 练习4:设计一个函数返回传入的列表中最大和第二大的元素的值。 #### 练习4:设计一个函数返回传入的列表中最大和第二大的元素的值。
...@@ -444,7 +433,6 @@ def is_leap_year(year): ...@@ -444,7 +433,6 @@ def is_leap_year(year):
判断指定的年份是不是闰年 判断指定的年份是不是闰年
:param year: 年份 :param year: 年份
:return: 闰年返回True平年返回False :return: 闰年返回True平年返回False
""" """
return year % 4 == 0 and year % 100 != 0 or year % 400 == 0 return year % 4 == 0 and year % 100 != 0 or year % 400 == 0
...@@ -457,7 +445,6 @@ def which_day(year, month, date): ...@@ -457,7 +445,6 @@ def which_day(year, month, date):
:param year: 年 :param year: 年
:param month: 月 :param month: 月
:param date: 日 :param date: 日
:return: 第几天 :return: 第几天
""" """
days_of_month = [ days_of_month = [
...@@ -479,7 +466,6 @@ def main(): ...@@ -479,7 +466,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
#### 练习6:打印[杨辉三角](https://zh.wikipedia.org/wiki/%E6%9D%A8%E8%BE%89%E4%B8%89%E8%A7%92%E5%BD%A2)。 #### 练习6:打印[杨辉三角](https://zh.wikipedia.org/wiki/%E6%9D%A8%E8%BE%89%E4%B8%89%E8%A7%92%E5%BD%A2)。
...@@ -501,7 +487,6 @@ def main(): ...@@ -501,7 +487,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 综合案例 ### 综合案例
...@@ -529,13 +514,7 @@ def random_select(): ...@@ -529,13 +514,7 @@ def random_select():
""" """
red_balls = [x for x in range(1, 34)] red_balls = [x for x in range(1, 34)]
selected_balls = [] selected_balls = []
for _ in range(6): selected_balls = sample(red_balls, 6)
index = randrange(len(red_balls))
selected_balls.append(red_balls[index])
del red_balls[index]
# 上面的for循环也可以写成下面这行代码
# sample函数是random模块下的函数
# selected_balls = sample(red_balls, 6)
selected_balls.sort() selected_balls.sort()
selected_balls.append(randint(1, 16)) selected_balls.append(randint(1, 16))
return selected_balls return selected_balls
...@@ -549,19 +528,16 @@ def main(): ...@@ -549,19 +528,16 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
> **说明**:可以使用random模块的sample函数来实现从列表中选择不重复的n个元素。 > **说明**:上面使用random模块的sample函数来实现从列表中选择不重复的n个元素。
#### 综合案例2:[约瑟夫环问题](https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98) #### 综合案例2:[约瑟夫环问题](https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98)
```Python ```Python
""" """
《幸运的基督徒》 《幸运的基督徒》
有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人扔到海里面去,有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15个人。由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置是基督徒哪些位置是非基督徒。 有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人扔到海里面去,有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15个人。由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置是基督徒哪些位置是非基督徒。
""" """
...@@ -631,7 +607,6 @@ def main(): ...@@ -631,7 +607,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
>**说明**:最后这个案例来自[《Python编程快速上手:让繁琐工作自动化》](https://item.jd.com/11943853.html)一书(这本书对有编程基础想迅速使用Python将日常工作自动化的人来说还是不错的教材),对代码做了一点点的调整。 >**说明**:最后这个案例来自[《Python编程快速上手:让繁琐工作自动化》](https://item.jd.com/11943853.html)一书(这本书对有编程基础想迅速使用Python将日常工作自动化的人来说还是不错的选择),对代码做了一点点的调整。
\ No newline at end of file \ No newline at end of file
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
![](./res/oop-zhihu.png) ![](./res/oop-zhihu.png)
> **说明**:以上的内容来自于网络,不代表作者本人的观点和看法,与作者本人立场无关,相关责任不由作者承担。(终于有机会享受一下把这段话反过来说的乐趣了,乐得牙都快碎了。) > **说明**:以上的内容来自于网络,不代表作者本人的观点和看法,与作者本人立场无关,相关责任不由作者承担。
之前我们说过“程序是指令的集合”,我们在程序中书写的语句在执行时会变成一条或多条指令然后由CPU去执行。当然为了简化程序的设计,我们引入了函数的概念,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些功能的时候只要调用函数即可;如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数继续切分为子函数来降低系统的复杂性。但是说了这么多,不知道大家是否发现,所谓编程就是程序员按照计算机的工作方式控制计算机完成各种任务。但是,计算机的工作方式与正常人类的思维模式是不同的,如果编程就必须得抛弃人类正常的思维方式去迎合计算机,编程的乐趣就少了很多,“每个人都应该学习编程”这样的豪言壮语就只能说说而已。当然,这些还不是最重要的,最重要的是当我们需要开发一个复杂的系统时,代码的复杂性会让开发和维护工作都变得举步维艰,所以在上世纪60年代末期,“[软件危机](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%8D%B1%E6%9C%BA)”、“[软件工程](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B)”等一系列的概念开始在行业中出现。 之前我们说过“程序是指令的集合”,我们在程序中书写的语句在执行时会变成一条或多条指令然后由CPU去执行。当然为了简化程序的设计,我们引入了函数的概念,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些功能的时候只要调用函数即可;如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数继续切分为子函数来降低系统的复杂性。但是说了这么多,不知道大家是否发现,所谓编程就是程序员按照计算机的工作方式控制计算机完成各种任务。但是,计算机的工作方式与正常人类的思维模式是不同的,如果编程就必须得抛弃人类正常的思维方式去迎合计算机,编程的乐趣就少了很多,“每个人都应该学习编程”这样的豪言壮语就只能说说而已。当然,这些还不是最重要的,最重要的是当我们需要开发一个复杂的系统时,代码的复杂性会让开发和维护工作都变得举步维艰,所以在上世纪60年代末期,“[软件危机](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%8D%B1%E6%9C%BA)”、“[软件工程](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B)”等一系列的概念开始在行业中出现。
...@@ -68,7 +68,6 @@ def main(): ...@@ -68,7 +68,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 访问可见性问题 ### 访问可见性问题
...@@ -96,7 +95,6 @@ def main(): ...@@ -96,7 +95,6 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()
``` ```
但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。 但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。
...@@ -120,7 +118,6 @@ def main(): ...@@ -120,7 +118,6 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()
``` ```
在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻,关于这一点可以看看我的[《Python - 那些年我们踩过的那些坑》](http://blog.csdn.net/jackfrued/article/details/79521404)文章中的讲解。 在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻,关于这一点可以看看我的[《Python - 那些年我们踩过的那些坑》](http://blog.csdn.net/jackfrued/article/details/79521404)文章中的讲解。
...@@ -179,7 +176,6 @@ def main(): ...@@ -179,7 +176,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
#### 练习2:定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法。 #### 练习2:定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法。
...@@ -246,7 +242,6 @@ def main(): ...@@ -246,7 +242,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
> **说明**:本章中的插图来自于Grady Booch等著作的[《面向对象分析与设计》](https://item.jd.com/20476561918.html)一书,该书是讲解面向对象编程的经典著作,有兴趣的读者可以购买和阅读这本书来了解更多的面向对象的相关知识。 > **说明**:本章中的插图来自于Grady Booch等著作的[《面向对象分析与设计》](https://item.jd.com/20476561918.html)一书,该书是讲解面向对象编程的经典著作,有兴趣的读者可以购买和阅读这本书来了解更多的面向对象的相关知识。
\ No newline at end of file
...@@ -45,7 +45,6 @@ def main(): ...@@ -45,7 +45,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### \_\_slots\_\_魔法 ### \_\_slots\_\_魔法
...@@ -87,7 +86,6 @@ def main(): ...@@ -87,7 +86,6 @@ def main():
person._gender = '男' person._gender = '男'
# AttributeError: 'Person' object has no attribute '_is_gay' # AttributeError: 'Person' object has no attribute '_is_gay'
# person._is_gay = True # person._is_gay = True
``` ```
### 静态方法和类方法 ### 静态方法和类方法
...@@ -134,7 +132,6 @@ def main(): ...@@ -134,7 +132,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。 和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。
...@@ -185,7 +182,6 @@ def main(): ...@@ -185,7 +182,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
### 类之间的关系 ### 类之间的关系
...@@ -287,7 +283,6 @@ def main(): ...@@ -287,7 +283,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。 子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。
...@@ -330,7 +325,6 @@ def main(): ...@@ -330,7 +325,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
在上面的代码中,我们将`Pet`类处理成了一个抽象类,所谓抽象类就是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它。Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过`abc`模块的`ABCMeta`元类和`abstractmethod`包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,`Dog``Cat`两个子类分别对`Pet`类中的`make_voice`抽象方法进行了重写并给出了不同的实现版本,当我们在`main`函数中调用该方法时,这个方法就表现出了多态行为(同样的方法做了不同的事情)。 在上面的代码中,我们将`Pet`类处理成了一个抽象类,所谓抽象类就是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它。Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过`abc`模块的`ABCMeta`元类和`abstractmethod`包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,`Dog``Cat`两个子类分别对`Pet`类中的`make_voice`抽象方法进行了重写并给出了不同的实现版本,当我们在`main`函数中调用该方法时,这个方法就表现出了多态行为(同样的方法做了不同的事情)。
...@@ -492,7 +486,7 @@ def display_info(ultraman, monsters): ...@@ -492,7 +486,7 @@ def display_info(ultraman, monsters):
def main(): def main():
u = Ultraman('骆昊', 1000, 120) u = Ultraman('骆昊', 1000, 120)
m1 = Monster('舒小玲', 250) m1 = Monster('狄仁杰', 250)
m2 = Monster('白元芳', 500) m2 = Monster('白元芳', 500)
m3 = Monster('王大锤', 750) m3 = Monster('王大锤', 750)
ms = [m1, m2, m3] ms = [m1, m2, m3]
...@@ -530,13 +524,12 @@ def main(): ...@@ -530,13 +524,12 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
#### 案例2:扑克游戏 #### 案例2:扑克游戏
```Python ```Python
from random import randrange import random
class Card(object): class Card(object):
...@@ -555,7 +548,6 @@ class Card(object): ...@@ -555,7 +548,6 @@ class Card(object):
return self._suite return self._suite
def __str__(self): def __str__(self):
all_suites = ('♠', '♥', '♣', '♦')
if self._face == 1: if self._face == 1:
face_str = 'A' face_str = 'A'
elif self._face == 11: elif self._face == 11:
...@@ -566,19 +558,20 @@ class Card(object): ...@@ -566,19 +558,20 @@ class Card(object):
face_str = 'K' face_str = 'K'
else: else:
face_str = str(self._face) face_str = str(self._face)
return '%s%s' % (all_suites[self._suite], face_str) return '%s%s' % (self._suite, face_str)
def __repr__(self):
return self.__str__()
class Poker(object): class Poker(object):
"""一副牌""" """一副牌"""
def __init__(self): def __init__(self):
self._cards = [] self._cards = [Card(suite, face)
for suite in '♠♥♣♦'
for face in range(1, 14)]
self._current = 0 self._current = 0
for suite in range(4):
for face in range(1, 14):
card = Card(suite, face)
self._cards.append(card)
@property @property
def cards(self): def cards(self):
...@@ -587,11 +580,7 @@ class Poker(object): ...@@ -587,11 +580,7 @@ class Poker(object):
def shuffle(self): def shuffle(self):
"""洗牌(随机乱序)""" """洗牌(随机乱序)"""
self._current = 0 self._current = 0
cards_len = len(self._cards) random.shuffle(self._cards)
for index in range(cards_len):
pos = randrange(cards_len)
self._cards[index], self._cards[pos] = \
self._cards[pos], self._cards[index]
@property @property
def next(self): def next(self):
...@@ -645,14 +634,11 @@ def main(): ...@@ -645,14 +634,11 @@ def main():
for player in players: for player in players:
print(player.name + ':', end=' ') print(player.name + ':', end=' ')
player.arrange(get_key) player.arrange(get_key)
for card in player.cards_on_hand: print(player.cards_on_hand)
print(card, end=' ')
print()
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
>**说明**:大家可以自己尝试在上面代码的基础上写一个简单的扑克游戏,例如21点(Black Jack),游戏的规则可以自己在网上找一找。 >**说明**:大家可以自己尝试在上面代码的基础上写一个简单的扑克游戏,例如21点(Black Jack),游戏的规则可以自己在网上找一找。
...@@ -661,13 +647,11 @@ if __name__ == '__main__': ...@@ -661,13 +647,11 @@ if __name__ == '__main__':
```Python ```Python
""" """
某公司有三种类型的员工 分别是部门经理、程序员和销售员 某公司有三种类型的员工 分别是部门经理、程序员和销售员
需要设计一个工资结算系统 根据提供的员工信息来计算月薪 需要设计一个工资结算系统 根据提供的员工信息来计算月薪
部门经理的月薪是每月固定15000元 部门经理的月薪是每月固定15000元
程序员的月薪按本月工作时间计算 每小时150元 程序员的月薪按本月工作时间计算 每小时150元
销售员的月薪是1200元的底薪加上销售额5%的提成 销售员的月薪是1200元的底薪加上销售额5%的提成
""" """
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
...@@ -761,6 +745,5 @@ def main(): ...@@ -761,6 +745,5 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
...@@ -58,7 +58,6 @@ def main(): ...@@ -58,7 +58,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
需要说明的是,GUI应用通常是事件驱动式的,之所以要进入主事件循环就是要监听鼠标、键盘等各种事件的发生并执行对应的代码对事件进行处理,因为事件会持续的发生,所以需要这样的一个循环一直运行着等待下一个事件的发生。另一方面,Tk为控件的摆放提供了三种布局管理器,通过布局管理器可以对控件进行定位,这三种布局管理器分别是:Placer(开发者提供控件的大小和摆放位置)、Packer(自动将控件填充到合适的位置)和Grid(基于网格坐标来摆放控件),此处不进行赘述。 需要说明的是,GUI应用通常是事件驱动式的,之所以要进入主事件循环就是要监听鼠标、键盘等各种事件的发生并执行对应的代码对事件进行处理,因为事件会持续的发生,所以需要这样的一个循环一直运行着等待下一个事件的发生。另一方面,Tk为控件的摆放提供了三种布局管理器,通过布局管理器可以对控件进行定位,这三种布局管理器分别是:Placer(开发者提供控件的大小和摆放位置)、Packer(自动将控件填充到合适的位置)和Grid(基于网格坐标来摆放控件),此处不进行赘述。
...@@ -93,7 +92,6 @@ def main(): ...@@ -93,7 +92,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
#### 在窗口中绘图 #### 在窗口中绘图
...@@ -128,7 +126,6 @@ def main(): ...@@ -128,7 +126,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
####加载图像 ####加载图像
...@@ -165,7 +162,6 @@ def main(): ...@@ -165,7 +162,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
####实现动画效果 ####实现动画效果
...@@ -202,7 +198,6 @@ def main(): ...@@ -202,7 +198,6 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
#### 碰撞检测 #### 碰撞检测
...@@ -275,7 +270,6 @@ class Ball(object): ...@@ -275,7 +270,6 @@ class Ball(object):
"""在窗口上绘制球""" """在窗口上绘制球"""
pygame.draw.circle(screen, self.color, pygame.draw.circle(screen, self.color,
(self.x, self.y), self.radius, 0) (self.x, self.y), self.radius, 0)
``` ```
#### 事件处理 #### 事件处理
...@@ -329,10 +323,7 @@ def main(): ...@@ -329,10 +323,7 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
上面的两段代码合在一起,我们就完成了“大球吃小球”的游戏(如下图所示),准确的说它算不上一个游戏,但是做一个小游戏的基本知识我们已经通过这个例子告诉大家了,有了这些知识已经可以开始你的小游戏开发之旅了。其实上面的代码中还有很多值得改进的地方,比如刷新窗口以及让球移动起来的代码并不应该放在事件循环中,等学习了多线程的知识后,用一个后台线程来处理这些事可能是更好的选择。如果希望获得更好的用户体验,我们还可以在游戏中加入背景音乐以及在球与球发生碰撞时播放音效,利用pygame的mixer和music模块,我们可以很容易的做到这一点,大家可以自行了解这方面的知识。事实上,想了解更多的关于pygame的知识,最好的教程是[pygame的官方网站](https://www.pygame.org/news),如果英语没毛病就可以赶紧去看看啦。 如果想开发[3D游戏](https://zh.wikipedia.org/wiki/3D%E6%B8%B8%E6%88%8F),pygame就显得力不从心了,对3D游戏开发如果有兴趣的读者不妨看看[Panda3D](https://www.panda3d.org/) 上面的两段代码合在一起,我们就完成了“大球吃小球”的游戏(如下图所示),准确的说它算不上一个游戏,但是做一个小游戏的基本知识我们已经通过这个例子告诉大家了,有了这些知识已经可以开始你的小游戏开发之旅了。其实上面的代码中还有很多值得改进的地方,比如刷新窗口以及让球移动起来的代码并不应该放在事件循环中,等学习了多线程的知识后,用一个后台线程来处理这些事可能是更好的选择。如果希望获得更好的用户体验,我们还可以在游戏中加入背景音乐以及在球与球发生碰撞时播放音效,利用pygame的mixer和music模块,我们可以很容易的做到这一点,大家可以自行了解这方面的知识。事实上,想了解更多的关于pygame的知识,最好的教程是[pygame的官方网站](https://www.pygame.org/news),如果英语没毛病就可以赶紧去看看啦。 如果想开发[3D游戏](https://zh.wikipedia.org/wiki/3D%E6%B8%B8%E6%88%8F),pygame就显得力不从心了,对3D游戏开发如果有兴趣的读者不妨看看[Panda3D](https://www.panda3d.org/)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册