Docsify/language/Python区块链/Python语法基础-all.md
2023-06-13 16:37:18 +08:00

65 KiB
Raw Blame History

第1章 Python语法基础

Python 是一种面向对象的、解释型的、通用的、开源的脚本编程语言。

Python是由荷兰数学和计算机科学研究学会的Guido van Rossum 于1990 年代初设计出来的。Python最大的特点就是书写简单因此随着时间的推移获得了更多人的青睐。Python是一种解释型语言这使得它具有了跨平台的优势随着近些年来版本的不断更新功能逐渐完善越来越多的大型项目开始选择Python。

目前主流的Python版本已经到3.x从2020年开始Python官方已不再对Python2进行维护。官方建议开发者尽快迁移到Python 3上需要注意的是虽然两个版本的语法十分接近但Python 3并不兼容Python 2也就是说此前使用Python 2编写的程序并不能保证可以在Python 3环境下正常运行。对于开发者来说入门学习自然是要紧跟官方建议本书的语法内容也是基于Python 3编写而成的。

1.1 初识Python

Python因为其语法简洁、优雅、易于理解一直被认为是最“简单”的开发语言。从入门角度来说这样的评价不无道理对于使用过其他语言的开发者来说Python的基础语法只需要几天就能够轻松上手了。但是想要真正地掌握Python这门语言仅仅熟悉语法基础还远远不够。

1.1.1 为什么要学习Python

人生苦短我用Python这是入坑Python之后听到的第一句名言Python的优势在于丰富的第三方库尤其是人工智能方向。事实证明当一门语言和某个火热的行业绑定时它必将前途无量正如10年前Android的崛起带动Java的再次起飞最近几年人工智能技术的火热使得Python也变得炙手可热起来。

下图1-1是截至2021年10月的TIOBE编程语言排行榜。

图1-1-1 TIOBE编程语言排行榜

图1-1-1 TIOBE编程语言排行榜

从排行榜上可以看出时至今日Python已经力压C和Java成为最受欢迎的编程语言。

无数的开发者为Python提供了庞大的第三方开源类库为了实现一个功能在其他语言中往往需要几十数百行的代码在Python中也许一句类库的调用就足够了。Python以其优雅和易于理解的语法著称就连《Thinking in C++》和《Thinking in Java》的作者Bruce Eckel也曾感叹“Life is shortyou need Python(人生苦短你该用Python)”。作为一门脚本语言它具有极强的适应性也许是你正在使用的PC上也许是在公司的服务器上也许就只是在一台Open Wrt路由器上Python程序都可以无需编译就直接运行。

可能很多读者会问Python是不是更适合搞人工智能不适合区块链行业呢其实不然。正如前面提到的人生苦短你需要使用Python在区块链领域也没有问题。区块链系统就像是数据库一样会为大多数开发者提供各种语言的SDK作为这么受欢迎的语言任何一个区块链平台都不会放弃Python的市场。就拿以太坊来说它的节点客户端就有Go语言、Java、C++、Python等语言的不同实现版本。以太坊创始人维塔利克·布特林正在努力开发vyperlang它是一款类似Python的智能合约开发语言。由此可见Python可以和区块链很好的结合Python+区块链绝对是强强组合。

1.1.2 Python开发环境搭建

Python官网(https://www.python.org/) 已经为各个主流操作系统提供了安装文件,按照自己的系统下载对应的版本即可。 图1-1-2 官网Python下载

图1-1-2 官网Python下载

在Python官网主页的Downloads下拉框中会根据你当前正在使用的系统在右侧提供最新版Python安装文件的下载链接如果读者需要下载指定版本或指定平台的安装文件可以在左侧的列表中结合实际情况自行选择。

本书以Windows系统为例点击下载Python3.10.0后双击安装文件,开始安装。 图1-1-3 开始安装Python

图1-1-3 开始安装Python

Install Now会自动安装到默认目录中Customize installation可以自定义Python的安装目录修改是否下载附加组件等内容。

安装完成后,点击键盘的Win+R,输入 cmd 进入Windows命令行模式。 输入:

python --version

如果命令行返回

Python 3.10.0

说明你的Python环境已经安装成功。

1.1.3 选择一个适合的IDE

工欲善其事必先利其器。想要快速开发Python程序一款合适的IDE是非常关键的。下面我们将介绍几款用于开发Python代码的IDE(Integrated Development Environment),即集成开发环境。

  • 文本编辑器

    文本编辑器其实不能称之为IDE作为初学者最开始学习Python基础语法时使用类记事本的编辑器也未尝不是一个好的选择。当然这里我们还是不建议使用windows自带的记事本程序因为该程序在保存时会默认在文本的开头部分添加一个头部标识(UTF-8 BOM),这会导致程序在运行时出现一些莫名其妙的问题。

    就轻量级的文本编辑器而言推荐使用Notepad++或Editra等第三方文本编辑器。

  • IDLE

    IDLE是Python默认提供的集成开发环境当我们安装好Python后也会默认安装上IDLE。IDLE具备基本的IDE功能具有自动缩进、语法高亮、关键词自动完成等功能。在这些功能的帮助下可以有效地提高开发者阅读和编写代码的效率。但是一般情况下不建议直接使用IDLE作为日常开发Python的IDE工具相较于下面介绍的几种IDE来说IDLE还是略显简易。

  • PyCharm

    使用文本编辑器写一些简单的小程序当然是没有问题的但是到了大型项目中往往需要同时管理数百个文件各个文件之间还存在相互引用关系此时使用智能IDE就成了必然的选择。如果有Java、JS等语言的编程经验想必一定听说过Jetbrains公司的大名PyCharm就是他们为Python开发者提供的一款IDE工具这款IDE为开发者提供了代码编写、调试和测试的整套工具链。

    你可以通过这个地址下载Pycharm (https://www.jetbrains.com/pycharm/download/)Jetbrains提供了Professional专业版和Community社区版两种版本相比于免费的社区版专业版内置了连接Sql数据库在线编写代码编辑Html、JS等强大功能但是对于大多数Python开发者而言社区版的功能就可以满足大多数的开发场景。

  • VS Code

    VS Code是微软推出的一款跨平台、支持插件扩展的智能IDE它会在安装后自动检测系统中是否已经安装过Python的开发环境并提示你安装所需插件同时支持语法高亮、自动补全。比文本编辑器功能完善又比PyCharm轻量级。

本书中接下来的Python代码都将在PyCharm中完成这里我们也建议读者朋友们使用PyCharm来作为开发Python的首选IDE。

1.1.4 写下你的第一个Python程序

所有学习编程的开发者都是从"Hello World"开始了编程之旅学习Python当然也不例外。

打开IDLE输入print('Hello World')系统将返回Hello World。执行结果如下

>>> print('Hello World')
Hello World

1.2 Python基础语法

Python 作为一种计算机编程语言和JavaJS 和 C等高级语言十分相似。都是定义了一套完整的语法体系确保编写的内容不会产生歧义。Python的基础语法主要涉及数据类型的定义与划分操作符的使用流程控制语句下面将一一介绍。

1.2.1 基础数据类型

Python 3 为了便于开发者编写程序支持多种数据类型。主要由六类标准的数据类型构成它们分别是Number数字、String字符串、List列表、Tuple元组、Set集合、Dictionary字典

Python通过对多个数据元素进行组合为开发者提供了几个非常有特色的复合数据类型。List、Tuple、Set、Dictionary都是非常典型的复合数据类型。String本质上也是一种复合数据类型String中的每个字符都被视为一个数据元素。

  • Number (数字)

数字是编程过程中最基本的类型。python 3 支持两种典型的数字类型:整数和浮点数。

整数的数据类型表示为 int例如1、3、50、100都是比较典型的整数。通常情况下我们定义一个整数默认是十进制的。python 3 在此基础上还支持2进制整数、8进制整数和16进制整数。

浮点数表示含有小数部分的数字类型表示为float。例如3.14、333.333、4.0都是比较典型的浮点数。在python 3 中允许开发者们使用科学计数法表示浮点数,写法也非常简单,两个数字中间加一个字母 e不区分大小写e或E都可以。例如5e3、6E4。其中字母e后边的数字表示10的幂次换算后乘以前面的数字得到实际数字。


5e3 表示 5*10^3。
>>> i = 5e3
>>> print(i)
5000.0
>>> j = 3E-3
>>> print(j)
0.003
  • String字符串

除了数字Python支持的另一种常见数据类型为字符串定义一个字符串类型的数据是非常容易的。使用单引号 ' 或双引号 " 将内容圈起来引号里的内容即为字符串本身。Python没有单独定义字符类型一个字符就是一个只有一个字符的字符串。

>>> str1 = '床前明月光'
>>> print(str1)
床前明月光
>>> str2 = "疑是地上霜"
>>> print(str2)
疑是地上霜

那么如果在字符串中出现单引号或双引号该怎么办python提供了反斜线 \ 来对其中的引号进行转义。避免和用于定义字符串所使用的引号混淆。

>>> str3 = '床前\'明月\'光'
>>> print(str3)
床前'明月'

如果我们要表达的字符串内容是多行的,可以采用三个单引号 ''' 或三个双引号 """ 将多行内容圈起来。当然也可以使用特定字符 \r\n 来实现换行效果。

>>> str4 = '''床前明月光
疑是地上霜
举头望明月
低头思故乡'''
>>> print(str4)
床前明月光
疑是地上霜
举头望明月
低头思故乡
>>> str5 = """床前明月光
疑是地上霜
举头望明月
低头思故乡"""
>>> print(str5)
床前明月光
疑是地上霜
举头望明月
低头思故乡
>>> str6 = '床前明月光\r\n疑是地上霜\r\n举头望明月\r\n低头思故乡'
>>> print(str6)
床前明月光
疑是地上霜
举头望明月
低头思故乡

1.2.2 学会使用list和tuple

List列表和Tuple元组是非常常用的复合数据类型它们都是将多个元素有序的集合在一起。

  • List列表

List列表是 python 提供的一种复合数据类型,它将多个元素集合起来并用逗号分隔,再由方括号 [] 括起来。例如List1 = [A, B, C] 。Python并没有对List中的元素约束数据类型所以一个 List 中的不同元素可以是不同的数据类型。

List是一种有序的集合。可以通过索引的方式访问List指定位置的元素。其中第一个元素的索引值为0即从0开始计数。另外索引还支持反向查找-1表示最后一个元素以此类推-2表示倒数第二个元素。

以List1 = [ A , B , C ] 为例:

元素 A B C
正向索引 List1[ 0 ] List1[ 1 ] List1[ 2 ]
反向索引 List1[ -3 ] List1[ -2 ] List1[ -1 ]

List支持基本的增加、删除、修改操作以List1 = ['A', 'B', 'C'] 、List2 = [ 'P', 'Q'] 为例:

操作 命令 结果 说明
增加 List1.append( 'D' ) ['A' , 'B' , 'C' , 'D' ] 末尾添加单个元素
增加 List1.extend( List2 ) ['A' , 'B' , 'C' , 'P' , 'Q' ] 末尾添加多个元素
增加 List1.insert( 1 , 'P' ) ['A' , 'P' , 'B' , 'C' ] 指定位置添加元素
删除 List1.pop() ['A' , 'B' ] 删除末尾元素
删除 List1.pop(1) ['A' , 'C' ] 删除指定位置元素
删除 del List1[1] ['A' , 'C' ] 删除指定位置元素
删除 del List1[1:] ['A' ] 删除指定范围内的元素
删除 List1.remove('C') ['A' , 'B' ] 删除第一次匹配到的元素
修改 List1[1]=K ['A' , 'K' , 'C' ] 修改指定位置元素

可以通过python 3 的内置函数len()获得当前List中的元素个数。

>>> List3 = [1, 2, 3]
>>> len(List3)
3
>>> List4 = ['A', 1, True]
>>> len(List4)
3

List自身的成员函数count()统计某个元素在List中出现的次数。例如List5 = [ 1 , 2 , 3 , 1 , 3 ]中的元素1出现了两次通过调用count()我们可以看到返回的结果为2。

>>> List5 = [ 1 , 2 , 3 , 1 , 3 ]
>>> List5.count(1)
2
>>> List5.count(2)
1

如上我们知道List5 = [ 1 , 2 , 3 , 1 , 3 ]中的元素1出现了两次那么如何找到它第一次出现的索引值呢List提供了index()函数用于确定某个元素在List中第一次出现时的索引。例如List5中的元素3第一次出现的索引值为2。而List5中的元素1第一次出现的索引值为0。

>>> List5 = [ 1 , 2 , 3 , 1 , 3 ]
>>> List5.index(3)
2
>>> List5.index(1)
0

为了使代码编写更简洁List还支持加号 + 运算符。加号实现多个List的顺序拼接。

例如已知两个列表List6 = [1 , 2]和List7 = [3 , 4],现在想把它们的元素合并到一起,生成一个新的序列[1 , 2 , 3 , 4 ]该序列中List6、List7各自元素内部顺序保持不变且List6的元素排在List7前边。

>>> List6 = [ 1 , 2 ]
>>> List7 = [ 3 , 4 ]
>>> List6 + List7
[1, 2, 3, 4]

List还支持乘法 * 运算符。乘法运算符后边追加数字n实现将List的元素顺序拼接n次。例如将List6 = [1 , 2]复制三次并顺次拼接到一起可以表示为List6*3即可得到列表[1, 2, 1, 2, 1, 2]。

>>> List6 = [1, 2]
>>> List6*3
[1, 2, 1, 2, 1, 2]
  • Tuple元组

和List列表相似Tuple元组同样是一种复合数据类型而且Tuple也是有序集合。与List最大的不同是Tuple中的元素一旦确定就不能再修改。Tuple的写法和List十分相似将多个元素集合起来并用逗号分隔再由小括号 () 括起来。例如Tuple1 = (A, B, C)。

定义只有一个元素的Tuple不能用Tuple1 = (1),此时 () 表示数学公式中的小括号相当于给变量Tuple1赋值1而并没有将Tuple1视作Tuple。正确的定义方法应该在第一个元素后边加一个逗号。例如Tuple2 = (1,)

python 3提供的type()方法能够查询变量的数据类型如下面的例子所示当Tuple1 = (1)时Tuple1的数据类型为int即为整数。而当Tuple2 = (1,)时Tuple2的数据类型为Tuple。

>>> Tuple1 = (1)
>>> type(Tuple1)
<class 'int'>
>>> Tuple2 = (1,)
>>> type(Tuple2)
<class 'tuple'>

实际上Python在显示只有1个元素的Tuple类型数据时也会加一个逗号避免误会。

>>> print(Tuple2)
(1,)

1.2.3 学会使用dict和set

Dictionary字典和 Set集合同样是非常常用的复合数据类型它们与 List 和 Tuple 最大的不同在于其内部的元素是无序的。其中 Set集合是一组无序元素构成的集合而Dictionary字典是一组无序的键值对构成的集合。

  • Dictionary字典

Dictionary字典是一组元素的集合每一个元素都是键值对key-value的形式元素间用逗号分隔最后用大括号 {} 将所有元素括起来。 例如Dict1 = { 'A' : 1 , 'B' : 2 , 'C' : 3 }。其中 'A' : 1 是一个键值对,也是 Dict1 的一个元素,'A' 是该键值对的键key1是该键值对的值value彼此间用冒号: 分隔。

Dictionary 中的元素是无序的。所有元素的key值是唯一且不可变的。Number、String 是不可变的可以作为key。而 List 是可变的,不能作为 key。

为了便于书写,后边的内容我们把 Dictionary字典简写为Dict。

Python 3 同样为 Dict 提供了很多方法,便于开发者使用。通过 Dict 的 key 值,可以直接获得对应的 value 值,如 Dict1[ 'A' ] 将获得数值 1。

>>> Dict1 = {'A': 1, 'B': 2, 'C': 3}
>>> Dict1['A']
1

Dict 支持基本的增加、删除、修改操作,以 Dict1 = { 'A' : 1 , 'B' : 2 , 'C' : 3 } 为例:

操作 命令 结果 说明
增加 Dict1[ 'D' ] = 4 { 'A' : 1 , 'B' : 2 , 'C' : 3 , 'D' : 4 } 添加key-value
删除 Dict1.pop( 'C' ) { 'A' : 1 , 'B' : 2 } 删除指定的key及其value
删除 del Dict1[ 'C' ] { 'A' : 1 , 'B' : 2 } 删除指定的key及其value
修改 Dict1[ 'C' ] = 4 { 'A' : 1 , 'B' : 2 , 'C' : 4 } 修改已有key对应的value

判断key在当前Dict中是否存在主要有两种方法in和get()。

通过in来判断key是否存在存在返回True不存在返回False。

>>> Dict1 = { 'A' : 1 , 'B' : 2 , 'C' : 3 }
>>> 'A' in Dict1
True
>>> 'D' in Dict1
False

通过内置方法get()来判断key是否存在存在返回key对应的value不存在则返回get()函数中预先定义的值没有预先定义值则返回None。

>>> Dict1 = { 'A' : 1 , 'B' : 2 , 'C' : 3 }
>>> Dict1.get('A' , -1)
1
>>> Dict1.get('D' , -1)
-1
>>> print(Dict1.get('D'))
None

如上面的例子所示,'A'是Dict1中的key其对应的value为1所以Dict1.get('A' , -1)的返回值为1。'D'不是Dict1中的key所以Dict1.get('D' , -1)的返回值为预先定义的-1。如果get()方法没有做预先定义如Dict1.get('D')则返回值为None。

实际开发中经常会有将两个Dict合并成一个Dict的情况。Python 3 提供了很多种办法。

以两个Dict为例

Dict3 = {1: [1, 11, 111], 2: [2, 22, 222]}

Dict4 = {3: [3, 33, 333], 4: [4, 44, 444]}

期望合并后的结果为:

Dict5 = {1: [1, 11, 111], 2: [2, 22, 222], 3: [3, 33, 333], 4: [4, 44, 444]}

下面为大家介绍三种方法实现Dict的合并。

方法1

Dict的copy()方法可以将Dict中的数据拷贝出来。

Dict的update()方法实现了对Dict中键值对的更新不仅可以修改现有key对应的value还可以添加新的键值对到Dict中。

>>> Dict3 = {1: [1, 11, 111], 2: [2, 22, 222]}
>>> Dict4 = {3: [3, 33, 333], 4: [4, 44, 444]}
>>> Dict5=Dict3.copy()
>>> Dict5.update(Dict4)
>>> Dict5
{1: [1, 11, 111], 2: [2, 22, 222], 3: [3, 33, 333], 4: [4, 44, 444]}

方法2

构造函数dict()可以直接将一组键值对创建为Dict。

>>> Dict3 = {1: [1, 11, 111], 2: [2, 22, 222]}
>>> Dict4 = {3: [3, 33, 333], 4: [4, 44, 444]}
>>> Dict5 = dict(Dict3)
>>> Dict5.update(Dict4)
>>> Dict5
{1: [1, 11, 111], 2: [2, 22, 222], 3: [3, 33, 333], 4: [4, 44, 444]}

方法3

Dict的items()方法可将键值对提取出来。

构造函数list()可以直接将一组数据创建为List。

Dict5 = dict( list( Dict3.items() ) + list( Dict4.items() ) )

>>> Dict3 = {1: [1, 11, 111], 2: [2, 22, 222]}
>>> Dict4 = {3: [3, 33, 333], 4: [4, 44, 444]}
>>> Dict5 = dict(list(Dict3.items()) + list(Dict4.items()))
>>> Dict5
{1: [1, 11, 111], 2: [2, 22, 222], 3: [3, 33, 333], 4: [4, 44, 444]}
  • Set集合

Set集合是一个无序、无重复元素的集合。元素间用逗号分隔再用大括号 {} 将所有元素括起来。例如Set1 = {1, 2, 3}。当Set中无元素时要用set()表示,而不是用{}表示因为这会和空的Dict混淆。 为了便于和 Dict 作区分,可以把 Set 看作是一组key的集合只存储key不存储value。由于key是无序且不重复的Set中的元素也都遵从此约束。

在创建一个Set时Python 3 会自动对Set中的元素进行去重确保Set中无重复记录。

例如用构造函数set()创建一个Set时当我们提供的数据有重复时Set2 = set([1, 1, 2, 2, 4, 4])最终生成的Set2为 {1, 2, 4}。

>>> Set2 = set([1, 1, 2, 2, 4, 4])
>>> Set2
{1, 2, 4}

Set支持基本的增加、删除操作以Set1 = {1, 2, 3}为例:

操作 命令 结果
增加 Set1.add(4) {1, 2, 3, 4}
删除 Set1.remove(3) {1, 2}

通过in来判断元素是否在Set中存在返回True不存在返回False。

>>> Set1 = set([1, 2, 3])
>>> 1 in Set1
True
>>> 4 in Set1
False

Set作为集合支持典型的集合运算。以Set1 = {1, 2, 3}、Set2 = {1, 2, 4}为例:

操作 命令 结果
交集 Set1 & Set2 {1 , 2}
并集 Set1 | Set2 {1 , 2 , 3 , 4}
差集 Set1 - Set2 {3}
对称差集 Set1 ^ Set2 {3 , 4}
交集 Set1.intersection(Set2) {1 , 2}
并集 Set1.union(Set2) {1 , 2 , 3 , 4}
差集 Set1.difference(Set2) {3}
对称差集 Set1.symmetric_difference(Set2) {3 , 4}

1.2.4 常用操作符

除了多种数据类型和对应的处理方法Python还提供了多种操作符使开发者能够方便地组合运用实现程序业务逻辑。

  • 注释

注释是开发者编写和阅读程序时的好帮手。可以大大提高代码的可读性。在Python 3 中使用井号 # 来标识注释,一段注释需要以井号开头,井号所在行后边所有的内容都被视作注释的内容。所以注释既可以发生在一行代码的开头,也可以发生在中间。

>>> #注释内容1
>>> print('文本内容1')
文本内容1
>>> str1 = '文本内容2'
>>> #注释内容2
>>> print(str1)
文本内容2

通常单行注释使用井号。当需要注释的内容涉及多行时,采用三个单引号 ''' 或三个双引号 """来标记注释内容 ,即在注释的多行内容的首尾添加该符号。这种情况一般被叫做块注释或批量注释。这样的注释还常常被称为文档字符串。

  • 转义

在Python中使用反斜杠\转义特定字符倘若不想转义只需在字符串前加r或R。就可以在字符串中显示原始的反斜线符号。

>>> print( '转义\n字符' )
转义
字符
>>> print( r'转义\n字符' )
转义\n字符
>>> print( R'转义\n字符' )
转义\n字符

以下为一些比较常用的转义字符:

转义字符 说明
\\ 生成一个反斜杠 \
\' 生成一个单引号 '
\" 生成一个双引号 "
\t tab
\r 回车
\n 换行
\000 NULL空值\0和\000效果一致

1.2.5 流程控制语句

python通过条件判断和循环的方式实现程序代码执行流程的控制。常用的条件判断语句为if常用的循环语句为for和while。

  • 条件判断

Python提供的完整if判断语句由if、elif、else三部分组成。 其中elif子句相当于其他编程语言常出现的else if。完整的if条件判断语句编写样式如下所示

if condition1:
    statement1 
elif condition2:
    statement2 
else:
    statement3

if 子句后边的 condition1 是该子句的判断条件。

当 condition1 为 True 时,执行 if 子句对应的代码块 statement1。

当 condition1 为 False 时,跳过 if 子句,执行 elif 子句。

elif 子句后边的 condition2 是该子句的判断条件。

当 condition2 为 True 时,执行 elif 子句对应的代码块 statement2。

当 condition2 为 False 时,跳过 elif 子句,执行 else 子句对应的代码块 statement3。

if判断语句的if、elif、else三个子句在使用上要遵守一定的原则

if 子句必须出现且为第一个判断子句。

elif 子句可以不使用或使用多次。

else子句可以不使用如果使用必须作为最后一个判断子句。

#多个elif
if condition1:
    statement1 
elif condition2:
    statement2 
elif condition3:
    statement3 
else:     
    statement4 
    
#缺失elif
if condition1:     
    statement1 
else:     
    statement2  
    
#缺失else
if condition1:     
    statement1 
elif condition2:     
    statement2 
    
#仅有if
if condition1:     
    statement1 

由于 Python 3 中并没有提供 switch、case 类的条件判断语句,所以开发者可以通过在 if 子句后边追加多个 elif 子句的方法实现类似效果。

score = 60
if score < 60:	
    print('不及格')
elif score < 85:	
    print('还不错')
elif score <= 100:	
    print('优秀')
else:	
    print('数据异常')
  • while 循环

while 语句是构建一个循环的最简单方法,只需要定义一个条件,当条件满足时,就执行 while 子句中的内容。当条件不满足时循环终止,并执行 else 子句中的内容。

完整的 while 循环语句编写样式如下所示:

while condition:
    statement1
else:
    statement2

while 后边的 condition 是循环的判断条件。

当 condition 为 True 时,执行 while 子句的代码块 statement1。

当 condition 为 False 时,跳出 while 循环。执行 else 子句的代码块 statement2。

由于else子句可缺省在没有else子句的情况下当condition条件不满足时直接跳出while循环。

while condition:
    statement

以下代码通过 while 循环来计算 5 个 10 相加之和:

num = 1
res = 0
while num <= 5:	
    res += 10	
    num += 1
else:	
    print(res)
  • for 循环

for 语句在构建一个循环时需要指定用于循环的序列通常是一个List或者Tuple。

完整的 for 循环语句编写样式如下所示:

for i in seq:
    statement1
else:
    statement2

变量 i 遍历序列 seq 中的每一个元素,并在遍历的过程中不断重复执行代码块 statement1。

当 i 遍历完 seq 中的所有元素后for 循环结束。执行 else 子句对应的代码块 statement2。

以下代码通过 for 循环来计算 5 个 10 相加之和:

res = 0
for i in [10, 10, 10, 10, 10]:
    res = res + i
else:    
    print(res)

由于else子句可缺省在没有else子句的情况下for循环可简化成以下形式

for i in seq:    
    statement

开发者在编写程序时遇到的情况往往是多种多样的。为了让循环更灵活python 提供了循环控制命令。

命令 说明
break 终止循环。 用break结束循环后循环对应的else 子句不会执行
continue 跳过本次循环剩余操作,直接进入下次循环
pass 占位语句,没有实际意义

1.2.6 了解Python的编码风格

不同的开发语言有各自不同特点,这也导致各自的开发规范不尽相同。

Python的语法比较简单和其他语言相比定义了相对较少的关键字并采用缩进的方式来控制代码块之间的逻辑关系这也是Python的最大特色。所以代码看起来显得结构清晰简单易懂。

  • 缩进

使用缩进来划分语句块缩进的空格数是可变的相同缩进程度的的语句隶属于同一个语句块。Python摒弃了其他开发语言常用的大括号 {}代码看起来更简洁。一般建议使用4个空格缩进避免使用制表符。

单行代码不宜过长,最好保持在几十个字符以内。需要注释时,尽量保证注释为单独的一行。

  • 变量命名

Python 规定变量的命名只能是字母、数字、下划线 _ 的组合。且变量名的首位只能是字母或者下划线。例如num_1、_num1 是合法变量名,而 1num_ 就是非法的。

命名中的字母并不局限于26个英文字母还可以使用中文字符、日文字符等。当然这里建议大家给变量命名时尽量在字母这一块只使用26个英文字母。

需要注意的是,系统定义的关键字不能作为变量名。另外 Python 是大小写敏感的这就意味着同一个字母大小写不同时代表着不同的意义。例如Num 和 num 表示不同的变量。

>>> Num = 1
>>> num = 2
>>> print(Num)
1
>>> print(num)
2

Python简化了变量的定义过程。变量和变量的数据类型不需要事先声明在给变量赋值时既定义了变量名又定义了变量的数据类型。即表示你给变量赋值了什么类型的数据变量就是什么数据类型。

  • 关键字

Python 3 定义的关键字并不多,且关键字本身也很简洁。我们可以通过 keyword 模块的 kwlist 查看到所有的关键词。

>>> import keyword
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

1.3 输入输出(IO)

在软件开发过程中必然要涉及到输入输出。Python在这方面的设计也非常简便。input() 方法用于支持标准输入print() 方法用于支持标准输出。

input() 能够接受一个标准输入数据。比如下面例子中 input() 方法获取到键盘输入的 good morning并将其转成字符串。

>>> input()
good morning
'good morning'

print() 方法将接收到的 'good morning' 显示输出。

>>> str = 'good morning'
>>> print(str)
good morning

1.3.1 文件的打开与读取

读写文件也是常见的IO操作Python 3 内置了相关的方法。通过内置方法 open() 打开文件,返回 file 对象,通过 file 对象提供的方法读取并解析文件内容。

>>> open(file, mode='r')

调用open() 方法比较常见的形式如上所示:

file打开的文件

mode打开文件的模式默认模式为只读 r ,可缺省。

mode的模式也有很多常见的几种模式如下

方法 说明
r 打开一个文件用于只读,文件指针将会放在文件的开头。
r+ 打开一个文件用于读写,文件指针将会放在文件的开头。
w 打开一个文件用于只写,如果该文件已存在,则将其覆盖。如果该文件不存在,创建新文件。
w+ 打开一个文件用于读写。如果该文件已存在,则将其覆盖。如果该文件不存在,创建新文件。
a 打开一个文件用于只写。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件。

open() 方法打开一个文件后返回file对象需要操作file对象从而获取文件的内容。

file 对象的属性如下:

属性 说明
file.closed 文件关闭为True否则为False
file.mode 文件访问模式
file.name 文件名称

file 对象的主要方法如下:

方法 说明
file.close() 关闭文件
file.truncate() 清空文件内容,必须是在可写的模式下
file.flush() 把缓冲区内容写入文件
file.read( [size] ) 读取文件返回结果的数据类型为List每个字节作为一个list元素。size参数用来说明读取的字节数可缺省
file.readline() 读取一行,包括每行结尾的 \n 符。返回结果的数据类型为List每个字节作为一个list元素
file.readlines( [size] ) 读取多行返回结果的数据类型为List文件的每一行作为一个list元素。 参数size读取的行数默认读取全部行可缺省
file.write( str ) 把str写入文件
file.writelines( list ) 把list中的所有元素写入文件

1.3.2 文件与目录操作

在操作中文件都是放在某个目录下的Python 3 内置的os模块可以让开发者们轻松地处理文件、目录和文件路径。

在介绍os模块之前我们想先来了解一下文件、目录和文件路径三者的关系。用公式表示就是

文件路径 = 目录 + 文件

下面的介绍中我们将用path表示文件路径dir表示目录file表示文件。path = dir + file

os模块提供的主要方法如下

方法 说明
os.getcwd() 获取当前工作目录
os.listdir( dir ) 返回指定目录dir下的所有文件和文件夹名
os.remove(path) 删除指定文件
os.rmdir(dir) 删除单级目录,要求该目录下无内容
os.removedirs(dir) 删除多级目录,要求每级目录下无内容
os.mkdir(dir) 创建单级目录
os.makedirs(dir) 创建多级目录,当多级文件夹都不存在,同时创建
os.rename (oldpath/file, newpath/file) 文件或目录的移动或重命名
os.walk( dir ) 遍历指定目录

os.sep 给出当前操作系统的路径分隔符windows下为 \。

os.linesep 给出当前操作系统的行终止符。Windows下为 \r\n。

下面我们通过例子详细介绍一下walk()方法os.walk( dir )。

dir为指定的目录返回值为tuple(dir, subdirs, files)。

dir起始目录。类型为string。

subdirs起始文件路径下的所有子目录。类型为list。

filenames起始文件路径下的所有文件。类型为list。

假设存在以下目录结构:

E:\data

part1\

4.txt

5.txt

part2\

8.txt

9.txt

1.txt

则os.walk()方法的结果如下:

>>> import os
>>> for i in os.walk('E:\\data'):	
    print(i)
    
# 输出结果为:
('E:\\data', ['part1', 'part2'], ['1.txt'])
('E:\\data\\part1', [], ['4.txt', '5.txt'])
('E:\\data\\part2', [], ['8.txt', '9.txt'])

显示目录下所有子目录和文件路径:

>>> import os
>>> for root, dirs, files in os.walk("E:\\data"):
	for dir in dirs:
		print(os.path.join(root, dir))
	for file in files:
		print(os.path.join(root, file))

# 输出结果为:	
E:\data\part1
E:\data\part2
E:\data\1.txt
E:\data\part1\4.txt
E:\data\part1\5.txt
E:\data\part2\8.txt
E:\data\part2\9.txt

os.path 模块提供针对文件更详细的方法:

方法 说明
os.path.isdir(dir) 判断是否是目录
os.path.isfile(path) 判断是否是文件
os.path.exists(path/dir) 判断文件路径/目录是否存在
os.path.dirname(path) 返回目录
os.path.basename(path) 返回文件名
os.path.basename("E:\Data\1.txt") 返回值:'1.txt'
os.path.join( dir, file/dir ) 拼接目录与文件名/目录
os.path.join("E:\Data","1.txt") 返回值:'E:\Data\1.txt' os.path.join("E:\Data","1") 返回值:'E:\Data\1'
os.path.getsize(path) 返回文件大小
os.path.split(path) 返回一个目录和文件构成的tuple
os.path.split("E:\Data\1.txt") 返回值:('E:\Data', '1.txt')
os.path.splitext(path) 返回一个文件路径和文件后缀名构成的tuple
os.path.splitext("E:\Data\1.txt") 返回值:('E:\Data\1', '.txt')

1.3.3 JSON格式处理

JSON (JavaScript Object Notation) 是目前软件开发中非常受欢迎的一种数据交换格式。Python3 中提供了 json 模块来对 JSON 格式的数据进行编解码主要用到两个方法dumps()方法实现编码。loads()方法实现解码。

>>> import json
>>> j = { 1:'attr1', 2:'attr2', 3:'attr3' }
>>> json.dumps(j)
'{"1": "attr1", "2": "attr2", "3": "attr3"}'

通过dumps()方法将输入的 j 转换成 JSON 格式:'{"1": "attr1", "2": "attr2", "3": "attr3"}'。需要注意的是标准JSON格式中的字符串必须使用双引号而不能使用单引号 。

1.4 函数

函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段。在软件开发过程中,开发者经常需要针对不同业务开发一些函数,接下来的开发工作中便可以重复使用这些函数。函数帮助程序实现模块化,便于代码的复用,提高了代码的可读性。

我们前面用到的print(),open()等方法就是非常典型的函数他们都是由Python预先准备好的被称为内建函数。

1.4.1 函数的基本定义

定义函数使用 def 关键字,一般格式如下:

def func( p1, p2...pn):
    codes
    return res1, res2...resn

函数以关键字def开头后边的func表示函数名称圆括号 () 中的 p1, p2...pn 表示函数的输入参数,简称入参。函数的入参必须放在圆括号内,且支持多个入参。

圆括号 () 后必须跟冒号 ,冒号后边都是函数的内容,通常称为函数体。本例中,从第二行开始表示函数的具体内容,需要相对第一行进行缩进。

关键词return后跟res1, res2...resn表示函数的返回值为res1, res2...resn定义函数时可以没有关键字return此时函数返回值为 None。return是一个函数结束的标志即执行完return整个函数就会结束运行。res可以是任意数据类型且return的返回值没有个数限制当返回多个值时返回值之间用逗号分隔。

1.4.2 函数调用

在程序中调用某个函数,只需要知道该函数的名称和参数限制即可。

>>> def func(i):
        print(i)
        
# 调用函数func
>>> func('Hello')
Hello

可以在定义函数时给指定的输入参数赋默认值。当调用函数时,如果没有为该参数赋值,则会使用默认值。

>>> def func(i, j = 'World'):
	print(i)
	print(j)

# j不赋值
>>> func('Hello')
Hello
World
# j赋值
>>> func('Hello', 'Lily')
Hello
Lily

如例子所示函数func()的参数j定义了默认值 'World'。当调用函数func()时如果没有给参数j赋值实际执行中j的取值为默认值 'World'。

1.4.3 递归函数

在软件开发过程中递归通常是指函数内部调用函数本身。这样的函数在Python中被称为递归函数。递归函数的优点是定义简单逻辑清晰。理论上所有的递归函数都可以改写成循环的形式。

下面我们来实现一个生成斐波那契数的函数:

def func(i):
    if i <= 1:
        return i
    else:
        return(func(i-1) + func(i-2))
    
# 执行结果:
>>> func(0)
0
>>> func(1)
1
>>> func(2)
1
>>> func(3)
2
>>> func(6)
8

我们都知道斐波那契数指的是这样一个数列0、1、1、2、3、5、8、13、21、34……其中第0条为0第1条为1第2条为1第3条为2且从第2条开始每一条都等于前两条之和。

func()函数根据输入的i值计算出斐波那契数第i条的值且为了实现第i条都等于前两条之和func()函数在函数内部递归调用了自己用func(i-1) + func(i-2)表示前两条之和。

1.4.4 匿名函数

python 提供了一种特别的函数称之为匿名函数。不同于普通的函数匿名函数在定义时不使用关键字def。而是采用关键词lambda。具体写法如下

lambda p1, p2... pn: expression

匿名函数通常只能在一行里实现。关键字lambda后边是函数的参数p1, p2... pn然后是冒号和函数的主体expression。不同于普通的函数体这里的expression不是代码块而仅仅是一个表达式。匿名函数不需要指定return的内容返回值就是表达式的结果。

以实现两个数字相加的函数为例,普通函数和匿名函数写法如下:

# 普通函数:
def func(i, j):
    return i+j

# 匿名函数:
lambda i, j: i+j

1.4.5 装饰器

装饰器Decorator本质上就是一个函数能够在不修改某个函数内部代码的前提下扩展该函数的功能。

def func1():
    print('hello')
    
# 执行结果:
>>> func1()
hello

在不修改func1()的情况下增加显示输出Lily就可以用装饰器来实现。

首先我们定义一个简单的装饰器语法如下用关键词def定义wrapper函数输入参数是func函数内嵌inner函数最后返回inner函数。再通过@wrapper命令完成对func1() 函数的装饰。

将 @wrapper放在func1() 前面相当于执行了func1= wrapper(func1)。由于wrapper()是一个装饰器返回inner函数此后再调用func1函数相当于调用wrapper()函数中返回的inner()函数。

此后再执行func1函数时就在hello的基础上增加显示输出Lily了。

def wrapper(func):
    def inner():
        func() 
        print('Lily')
    return inner

@wrapper
def func1():
    print('hello')

# 执行结果:
>>> func1()
hello
Lily

1.5 异常处理

Python中一般会出现两种错误问题第一种是不符合语言规范的语法错误另外一种则是所谓的异常。

语法错误又称解析错误也是开发者在学习Python 时最常见的错误。

# 1.5.1 语法错误,后一个单引号是中文字符,这也是很多初学编程的开发者经常犯的错误
>>> def func()
  File "<stdin>", line 1
    def func()
             ^
SyntaxError: invalid syntax

如上面的例子中所示定义函数func时缺少了冒号IDLE会在错误位置下方标记一个“箭头”。 文件名和行号也会被输出,以便开发者快速定位到错误位置。

在实际开发过程中,即使代码在语法上是正确的,不存在解析错误,执行时也可能发生错误。这种在执行时检测到的错误,被称为异常。

例如下面这段代码程序期望num是一个数字计算num的一半是多少。

num = "50"
print(num/2)

但是num却是一个字符串这样在运行时就会出现异常。

# 执行结果:
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    print(num/2)
TypeError: unsupported operand type(s) for /: 'str' and 'int'

可以看到编译器会提示我们“unsupported operand type(s) for /: 'str' and 'int'”不支持对str字符型和int整型做除法运算。

程序运行过程中出现预期之外的情况,导致后续的流程无法正常执行。这样的异常在大型项目中经常发生。提前预判此类情况并加以处理,确保程序的正常运行,就是所谓的异常处理。

Python提供了完善的异常检查机制可以将异常扼杀在摇篮里。

1.5.1 错误处理思想

Python中已经预设了多种异常情况具体如下表所示

异常名称 描述
BaseException 所有异常的基类
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
LookupError 无效数据查询的基类
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalError 访问未初始化的本地变量
ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedError 尚未实现的方法
SyntaxError Python 语法错误
IndentationError 缩进错误
TabError Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeError Unicode 相关的错误
UnicodeDecodeError Unicode 解码时的错误
UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误
Warning 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告

Python主要通过预设在代码中的try-except语句来捕获可能发生的异常except后对应的是异常类型当出现的异常同except预设的异常类型一致时该异常就会被捕获。Python同样支持自定义异常类型而不具体处理通过raise将该异常抛出由调用方通过try-except来捕获这个异常。

1.5.2 try语句使用

Python的异常捕获语句和Java语法极为相似try代码块内是检测异常的区域在该区域内存在except中定义的异常会直接跳转到对应的except代码块内。其中异常类型后的as是对该异常定义的别名开发者可以通过该别名获取到异常的详细信息。

try:
    num = '50'
    print(num/2)
except TypeError as e:
    print("捕获到异常:", e)

这样就可以捕获到前面提到的异常,示例中程序在捕获到异常后会打印出异常的信息。

如果在一段程序中无论是否出现异常都期望程序能执行一些特定的操作可以通过try-except-finally来实现。finally代码段中的内容无论是否出现异常都会执行。

try:
    num = '50'
    print(num/2)
except TypeError as e:
    print("捕获到异常:", e)
finally:
    print("无论是否发生异常,这里都会执行")

这种情况多数用在一些对数据资源的访问上例如打开一个文件无论是否在读取过程中出错都需要最终关闭这个文件以释放资源这个操作就可以在finally中实现。

如果想要在一个except中同时捕获多个预定义的异常有两种方式来处理

  • 多个并行的except

    try:
        num = '50'
        print(num / 2)
    except TypeError as e:
        print("捕获到的字符串与数字数学运算的异常:", e)
    except ZeroDivisionError as e:
        print("捕获到的除数为0的异常", e)
    finally:
        print("无论是否发生异常,这里都会执行")
    
  • 一个except中包含多个类型的异常

    try:
        num = 50
        print(num / 0)
    except (TypeError, ZeroDivisionError) as e:
        print("捕获到的的异常:", e)
    finally:
        print("无论是否发生异常,这里都会执行")
    

    代码执行时出现上面定义的任意一种异常都会被对应的except语句捕获到。并行捕获异常的方式可以针对不同的异常类型采取相对应的处理方法一个except对应多种异常则可以针对类似的几种异常采取相同的处理策略。

  • 不指定异常类型

    如果不确定try代码块中的异常类型也可以不指定异常类型或者捕获所有异常的父类BaseException这样捕获到的任何异常都会在except代码块中处理。

    try:
        num = 50
        print(num / 0)
    except:
        print("捕获到的任意异常")
    finally:
        print("无论是否发生异常,这里都会执行")
    
  • 捕获异常的基类

    默认情况下所有异常都是继承自BaseException所以捕获BaseException就可以匹配到所有的异常类型。

    try:
      num = 50
      print(num / 0)
    except BaseException as e:
      print("捕获到的任意异常:", e)
    finally:
      print("无论是否发生异常,这里都会执行")
    

    可以看到相比于不指定异常类型捕获BaseException的优点在于可以获取到导致程序异常的具体信息并在except中对异常做相应的判断和处理。

  • 使用raise向外抛出异常

    使用raise可以不对异常做处理而是将该异常抛给上一层也就是调用方由调用方来处理这个异常。这样的处理多是在方法定义中可以让外部的调用方有更灵活的异常处理方式。

    def addNum():
        num1Str = input("请输入第1个数字:")
        if(not num1Str.isdigit()):
            raise Exception("您输入的不是数字!", num1Str)
        num2Str = input("请输入第2个数字:")
        if(not num2Str.isdigit()):
            raise Exception("您输入的不是数字!", num2Str)
    
        floatNum1 = float(num1Str)
        floatNum2 = float(num2Str)
        return floatNum1 + floatNum2
    
    try:
        addNum()
    except BaseException as e:
        print(e)
    

    上面的示例中,我们定义了一个加法函数,提示用户输入两个数字,函数会自动计算两个数字的和。如果用户输入的内容不是数字,那么这个加法操作流程必然无法执行,因此我们需要提前判断用户输入的内容。

    在Python中通过input获取到的输入内容都是字符串我们通过字符串内置的isdigit()方法来判断其是否为数字如果不是函数会通过raise向外抛出一个Exception异常并提示用户“您输入的不是数字这样在调用该函数时通过try-except就可以实时判断用户输入的内容是否符合函数的要求。

1.5.3 断言语句

断言一般应用在单元测试中判断一个表达式以及这个表达式为False时触发的异常。在Python中断言的语法为

assert expression, arguments

当assert中的条件为假时就会将arguments以异常信息的形式输出。其中arguments可缺省。

num1Str = 'm'
assert(num1Str.isdigit(), 'num1Str不是数字')

等价于

num1Str = "m"
if(num1Str.isdigit()):
    pass
else:
    raise AssertionError('num1Str不是数字')

需要注意的是断言只是调试程序的一种手段一般是用来发现Python脚本中的异常。断言所检测的异常在执行Python代码时是可以被忽略的因此通过断言定位并修改程序问题后该断言便可以删除。

1.6 面向对象编程

几乎所有的高级语言都是面向对象的,相比于面向过程,面向对象的编程方式更适合人类的思维模式。在中大型项目中,面向对象的编程风格也可以极大地节约代码量。

Python支持面向对象的编程有着完善的类、对象以及方法的定义和使用规范。

1.6.1 面向对象的编程思想

面向对象最重要的三个特点即为封装、继承、多态。

封装是将拥有相同属性和方法的事物包装在一起成为一个类,对外尽可能隐藏具体的属性,外部通过给定的方法来修改类的属性或执行业务,各个类之间通过对外暴露的方法相互交流。

继承是使用已经存在的类定义新的类,新类中可以增加新的属性或方法,也可以使用父类定义的属性和方法,继承可以实现代码的可重用性。

多态则是为了解决现实生活中的多样性问题, 相同的指令对应不同的子对象,该对象具有特定的执行方式。

举一个简单的例子我们可以将学生这个群体封装为一个学生类该类中有age、grade两个属性还有一个study方法。学生分为小学生、中学生和大学生后三者就是前者的子类它们除了可以继承学生定义的属性和方法也可以定义各自的特点。例如大学生有不同的专业这样的属性并非所有学生共有只定义在大学生子类中即可。继承自父类中的study方法在不同的子类中可以有不同的表现小学生学习小学知识大学生则学习自己的专业知识这就是所谓的多态。

1.6.2 Python的面向对象特色

如下面的示例在Python中定义了一个类

class Student(object):
    def __init__(self, name, age, grade):
        self.name = name
        self.age = age
        self.grade = grade

    def study(self):
        print(self.name+"在学习基础知识")

# 类的调用
baseStudent = Student("学生0", 10, 3)
print("baseStudent的name:", baseStudent.name)
print("baseStudent的age:", baseStudent.age)
print("baseStudent的grade:", baseStudent.grade)
baseStudent.study()

# 执行结果
baseStudent的name: 学生0
baseStudent的age: 10    
baseStudent的grade: 3   
学生0在学习基础知识  

class是Python中用来定义类的标准字符Student(object)说明当前定义的类继承自object类如同Java中的类都是继承自Object类一样Python中的类也都是继承自object类。Student类中的__init__方法是构造函数的定义是这个类的对象被初始化时默认会调用的方法。

像__init__这样以前后双下划线的方式命名的方法名是Python中的“魔术方法magic method对Python系统来说这将确保不会与用户自定义的名称冲突。

Python中定义对象直接命名对象名即可不能像Java那样在对象名前显式指定类名。等号后的Student("学生0", 10, 3)就是对Student对象的实例化这行代码会自动调用类构造函数需要注意的是在Python类定义的函数第一个参数都是self这个参数代表当前正在使用的对象我们可以通过它获取到当前对象内的属性。

接下来我们定义Student的两个子类PrimaryStudent和JuniorStudent它们会各自实现自己的study方法

class Student(object):
    def __init__(self, name, age, grade):
        self.name = name
        self.age = age
        self.grade = grade

    def study(self):
        print(self.name+"在学习基础知识")

class PrimaryStudent(Student):
    def study(self):
        print(self.name+"在学习小学知识")


class JuniorStudent(Student):
    def study(self):
        print(self.name+"在学习初中知识")


primaryStudent = PrimaryStudent("小学生", 7, 1)
print("primaryStudent的name:", primaryStudent.name)
print("primaryStudent的age:", primaryStudent.age)
print("primaryStudent的grade:", primaryStudent.grade)
primaryStudent.study()

juniorStudent = JuniorStudent("中学生", 13, 7)
print("juniorStudent的name:", juniorStudent.name)
print("juniorStudent的age:", juniorStudent.age)
print("juniorStudent的grade:", juniorStudent.grade)
juniorStudent.study()

# 执行结果
primaryStudent的name: 小学生
primaryStudent的age: 7
primaryStudent的grade: 1
小学生在学习小学知识
juniorStudent的name: 中学生
juniorStudent的age: 13
juniorStudent的grade: 7
中学生在学习初中知识

上面的示例中PrimaryStudent和JuniorStudent虽然都没有在类中定义自己的age、grade以及name但是因为在定义类时继承了Student类所以它们就继承了Student定义的这些属性。此时称Student类为父类PrimaryStudent和JuniorStudent为子类。两个子类都重写了父类的study方法调用时会执行子类中的方法而非父类中的。

  • 类属性和方法的访问限制

类属性和方法的访问限制是通过名称来控制的。当名称以两个下划线开头时则表示这是一个私有变量或私有方法。将上面的例子中的name改为__name后外部就无法访问到这个属性了。

class Student(object):
    def __init__(self, name, age, grade):
        self.__name = name
        self.age = age
        self.grade = grade

    def study(self):
        print(self.__name+"在学习基础知识")

student = Student("小学生", 7, 1)
print("student的name:", student.__name)

执行上面的程序会提示Student没有__name这个属性因为__name对外部是不可见的。

AttributeError: 'Student' object has no attribute '__name'

在Python中如果属性名或方法名以单个下划线开头则说明这是一个受保护的属性或方法这是一种约定俗成的做法它表示“虽然我可以在外部被访问但是最好不要这样做”。

疑难解答

1. 为什么建议使用python 3.x 而不是python 2.x

Python 3.x 在设计时没有考虑向下兼容且官方对于Python 2.x的维护截止到2020年。因此为了能够在未来的开发中享受到Python的更新与改善。建议大家使用Python 3.x。

相较于 Python 2.x Python 3.x 的Python解释器、io、pickle等模块在性能上都有了大幅度的提升。Python 3.x 新增了tracemalloc、enum等模块新增了time.time_ns()、os.scandir()等方法。

Python 3.x 在使用上也做出了一些调整。去掉了print 语句提供了print()函数。将源码文件的编码默认为 utf-8去掉了不等运算符<>,只保留了不等运算符!=。去掉了 raw_input()函数优化了input() 函数,使其可以接收任意类型输入,并将输入默认为字符串,返回值为字符串类型。

2. 为什么都说Python相比于其它语言更简单

事实上想要真正精通Python一点也不简单只是Python的入门基础知识比较简单学习曲线较为平滑而这些基础知识已经可以应付工作中的大多数场景。不像有些语言初学者往往都倒在了入门的门槛上根本都没有机会流畅地学完所有基础语法。

另外Python因为具备海量的第三方库即便是刚刚入门的开发者也能轻易地使用这些第三方库“黏合”出一个看上去比较复杂的程序。就像造一辆汽车C语言往往需要从如何炼铁开始Java需要从如何拧螺丝钉开始而Python则是直接提供了轮子、方向盘、底盘你只需要按照自己的想法将这些组件搭建起来就可以“造”出一辆汽车这其中哪一个更简单就显而易见了。

学习者在学习Python时可以获得了更多的正反馈这更容易让学习者产生Python相较其他语言更“简单”的感觉。

实训如何用Python轻松处理excel

openpyxl 是一个支持读写xlsx、xlsm、xltx、xltm等文件格式的Python类库下面我们尝试使用这个类库完成一个简单的Excel文件读写案例。

  • 创建excel文件
# 引入openpyxl模块
import openpyxl

# 创建一个工作簿(workbook)即一个excel文件。默认情况下为该工作簿创建一个名为'Sheet'的工作表
wb = openpyxl.Workbook()
# 获取当前工作表,即名为'Sheet'的工作表
ws = wb.active

# 为工作表的单元格赋值
# 第1行第A列的单元格赋值为'姓名'
# 第1行第B列的单元格赋值为'年龄'
ws['A1'] = '姓名'
ws['B1'] = '年龄'
# 通过追加的方式为工作表赋值
ws.append(['小明', '张三'])

# 保存工作簿,当系统不存在该文件时,新建文件。
wb.save('D:\\sample.xlsx')
wb.close()

下图为程序运行后的结果:

6ad7cbfac56f95fc468126f0a2439bb

创建了一个名为sample的Excel文件并为工作表增加了4条数据。

  • 读取并修改excel文件
# 引入openpyxl模块
import openpyxl

# 打开已有的excel文件
wb = openpyxl.load_workbook('D:\\sample.xlsx')

# 新建一个工作表,并命名为'NewSheet1'index控制创建新表的位置
wb.create_sheet(index = 0, title = 'NewSheet1')
# 新建一个工作表,并命名为'NewSheet2'index控制创建新表的位置
wb.create_sheet(index = 2, title = 'NewSheet2')

# 获取所有工作表名, names的值为['NewSheet1', 'Sheet', 'NewSheet2']
names = wb.sheetnames
# 删除名为'Sheet'的工作表
wb.remove(wb['Sheet'])

# 打开名为'NewSheet1'的工作表
ws = wb['NewSheet1']
# 获取单元格A1中的值
a1 = ws['A1'].value
# 获取第5行第6列单元格中的值
cell56 = ws.cell(row=5, column=6).value

# 修改第3行第4列单元格的值为'X'
ws.cell(row=3, column=4, value='X')
# 将工作表'NewSheet1'改名为'New'
ws.title = 'New'

# 保存修改结果
wb.save('D:\\sample.xlsx')
wb.close()

下图为程序运行结果:

cb3b015b907bd89f79a4e25b73ef9cb

打开此前创建的Excel文件sample.xlsx删除了名为Sheet的工作表新增了名为New和NewSheet2的两个工作表。在工作表New的第3行D列的单元格内写入字符X。