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

450 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 第1章 Python语法基础
Python 是一种面向对象的、解释型的、通用的、开源的脚本编程语言。
Python是由荷兰数学和计算机科学研究学会的Guido van Rossum 于1990 年代初设计作为一门叫做ABC语言的替代品。Python提供了高效的高级数据结构还能简单有效地面向对象编程。Python语法和动态类型以及解释型语言的本质使它成为多数平台上写脚本和快速开发应用的编程语言随着版本的不断更新和语言新功能的添加逐渐被用于独立的、大型项目的开发。
目前主流的Python版本已经到3.x从2020年开始Python官方将不再对Python2进行维护官方建议开发者尽快迁移到Python3上。需要注意的是虽然两个版本多数语法比较类似Python3并不兼容Python2也就是说此前使用Python2编写的程序并不能保证可以在Python3环境下正常运行。对于开发者来说入门学习自然是要紧跟官方建议本书的语法内容也是基于Python3编写而成的。
## 1.1 初识Python
Python因为其语法简洁、优雅、易于理解一直被认为是最“简单”的开发语言。从入门角度来说这样的评价不无道理对于有其他开发语言经验的学习者来说Python的基础语法也许只需要短短几天就能比较熟悉了但是如果想要真正地掌握Python这门语言仅仅是熟悉语法基础还远远不够。Python最大的特点是拥有海量的第三方类库作为开发者如何用最小的代价开发出所需要的功能则需要对Python的常用类库有所了解。从这个方面来讲Python的学习熟悉语法后不断去使用、练习才能在日后的使用中得心应手。
### 1.1.1 为什么要学习Python
<font color=red>人生苦短我用Python这是入坑Python之后听到的第一句名言Python的优势在于丰富的第三方库尤其是人工智能方向。事实证明当一门语言和某个火热的行业绑定时它必将前途无量</font>正如10年前Android的崛起带动Java的再次起飞最近几年人工智能技术的火热使得Python炙手可热了起来。
下图1-1是截至2021年9月的TIOBE编程语言排行榜。
![图1-1-1 TIOBE编程语言排行榜](https://i.loli.net/2021/10/11/qRE2pGmlA8a4nxj.png "图1-1 TIOBE编程语言排行榜")
<center>图1-1-1 TIOBE编程语言排行榜</center>
从排行榜上可以看出时至今日Python已经力压Java成为第二受欢迎的编程语言距离排名第一的C语言也只有0.16%的微弱差距从趋势来看Python登顶最受欢迎的编程语言榜首似乎只是时间问题。
正是因为有了如此繁荣的生态环境无数的开发者也为Python提供了庞大的第三方开源类库供用户调用。为了实现一个功能在其他语言中往往需要几十数百行的代码在Python中也许就只是一句类库的调用就足够了。Python以其优雅和易于理解的语法著称就连《Thinking in C++》和《Thinking in Java》的作者Bruce Eckel也曾感叹“Life is shortyou need Python(人生苦短你该用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下载](https://i.loli.net/2021/10/09/vn5q1ZUeBW6PJpl.png)
<center>图1-1-2 官网Python下载</center>
在Python官网主页的Downloads下拉框中会根据你当前正在使用的系统在右侧提供最新版的Python版本的下载链接如果读者需要下载指定版本或指定平台的Python安装包可以在左侧的列表中根据实际情况自行选择。
本书以Windows系统为例点击下载Python3.10.0后双击安装包,开始安装。
![图1-1-3 开始安装Python](https://i.loli.net/2021/10/09/VolFBgHO3QTqJ1P.png)
<center>图1-1-3 开始安装Python</center>
Install Now会自动安装到默认目录中Customize installation可以自定义Python的安装目录修改是否下载附加组件等内容。
安装完成后,点击键盘的<kbd>Win</kbd>+<kbd>R</kbd>,输入“cmd”进入Windows的命令行模式。
输入:
```python
python --version
```
如果命令行返回
```python
Python 3.10.0
```
说明你的Python环境已经安装完成了。
### 1.1.3 选择一个适合的IDE
工欲善其事必先利其器。想要快速开发Python程序使用一款适合的IDE是非常关键的。下面我们介绍可以用于开发Python代码的IDE。
* 文本编辑器
文本编辑器其实不能称之为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工具这款工具提供代码编写、调试、测试工具的一整套工具链。
你可以通过这个地址下载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时也不例外。
打开你的IDE在任意的英文目录中新建一个HelloWorld.py文件
![图1-1-4 创建HelloWorld.py](https://i.loli.net/2021/10/09/mb4ZNhInRQj2yrw.png)
<center>图1-1-4 创建HelloWorld.py</center>
打开这个文件后,在文件内写下
```python
print("Hello World")
```
随后命令行进入这个文件所在目录,使用
```python
python HelloWorld.py
```
运行这个程序你将会在控制台中看到打印出来的“Hello World”语句。
![图1-1-5 运行HelloWorld.py](https://i.loli.net/2021/10/09/TDrZMNCq1aWB2ku.png)
<center>图1-1-5 运行HelloWorld.py</center>
只是简单打印一句语句看起来还不够“酷”?没问题,可以让我们的程序更加“智能”一点。
```python
name = input("请输入您的姓名:") # 获取用户输入内容
print("Hello", name)
```
这样在运行程序后,界面会打印出一行文字:
在输入您的姓名后,程序会自动跟您打招呼。
![图1-1-6 再次运行HelloWorld.py](https://i.loli.net/2021/10/09/VAbF5fy6wxNhB7i.png)
<center>图1-1-6 再次运行HelloWorld.py</center>
## 1.5 异常处理
Python中一般会出现两类错误问题第一种是在编写时不符合语言规范的语法错误另外一种则是所谓的程序异常。
语法错误如下:
```python
# 1.5.1 语法错误,后一个单引号是中文字符,这也是很多初学编程的开发者经常犯的错误
name = 'name
```
使用Pycharm编写代码时当出现语法错误时一般会通过下划红线提示出来。如下图1-1-7所示
![图1-1-7 语法错误](https://i.loli.net/2021/09/23/wRFtpzWNGedkTgh.png)
<center>图1-1-7 语法错误</center>
这样的错误一般无需运行程序在编写代码时IDE就能检测出来运行程序也会有对应的错误提示。
另外一种情况则要稍微复杂一些,这也是我们本节要重点介绍的内容。代码语法是正确的,但是到了运行时,却会因为用户输入内容有误、程序获取的数据不符合预期等原因,导致程序无法获得期望的输入数据,或者不能按照期望的流程执行。
例如下面这段代码,程序期望获取的是一个数字,计算这个数字的一半是多少。
```python
# 1.5.2 期望获取一个数字,并计算它的一半是多少
num = "50"
print(num/2)
```
但是num的值却是一个字符串这样在运行时就会出现下图这样的异常。
![图1-1-8 异常](https://i.loli.net/2021/09/24/6snzvhBUTrf2NIa.png)
<center>图1-1-8 程序异常</center>
可以看到编译器会提示我们“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|用户代码生成的警告|
<center>图1-1-9 Python异常分类列表</center>
Python处理异常主要通过预设在代码中的try-except语句来捕获可能发生的异常如果存在不在上表异常类型的情况Python同样支持自定义异常类型通过raise将该异常类型抛出由调用方通过try-except来捕获这个异常。
### 1.5.2 try语句使用
Python的异常捕获语句和Java语法极为相似try代码块内是检测异常的区域在该区域内存在except中定义的异常会直接跳转到对应的except代码块内。
```python
# 1.5.3 try-except的使用
try:
num = "50"
print(num/2)
except TypeError as e:
print("捕获到异常:", e)
```
这样就可以捕获到前面我们提到的异常。
如果在一段程序中无论是否出现异常都期望程序能执行一些特定的操作可以通过try-except-finally来实现finally代码段的内容无论是否出现异常情况都会执行。
```python
# 1.5.4 try-except-finally的使用
try:
num = "50"
print(num/2)
except TypeError as e:
print("捕获到异常:", e)
finally:
print("无论是否发生异常,这里都会执行")
```
这种情况多数用在一些对数据资源的访问上例如打开一个文本文件无论是否在读取过程中出错都需要最终关闭这个文件以释放资源这个操作就可以在finally中来完成。
如果想要在一个except中同时捕获多个预定义异常有两种方式来处理
* 多个并行的except
```python
# 1.5.5 多个except的使用
try:
num = "50"
print(num / 2)
except TypeError as e:
print("捕获到的字符串与数字数学运算的异常:", e)
except ZeroDivisionError as e:
print("捕获到的除数为0的异常", e)
finally:
print("无论是否发生异常,这里都会执行")
```
* 一个except中包含多个类型的异常
```python
# 1.5.6 多个except的使用
try:
num = 50
print(num / 0)
except (TypeError, ZeroDivisionError) as e:
print("捕获到的的异常:", e)
finally:
print("无论是否发生异常,这里都会执行")
```
如果不确定try代码块中的异常类型也可以不指定异常类型或者捕获所有异常的父类BaseException这样捕捉到的任何异常都会在except代码块中处理。
* 不指定异常类型
```python
# 1.5.7 捕获所有异常
try:
num = 50
print(num / 0)
except:
print("捕获到的任意异常")
finally:
print("无论是否发生异常,这里都会执行")
```
* 捕获异常的基类
默认情况下所有异常都是继承自BaseException所以捕获BaseException就可以匹配到所有的异常类型。
```python
# 1.5.8 捕获所有异常
try:
num = 50
print(num / 0)
except BaseException as e:
print("捕获到的任意异常:", e)
finally:
print("无论是否发生异常,这里都会执行")
```
可以看到相比于不指定异常类型捕获BaseException的优点在于可以获取到导致程序异常的具体信息我们也可以在except中对异常做相应的判断和针对性的处理。
* 使用raise向外抛出异常
使用raise可以不对异常做处理而是将该异常抛给上一层也就是调用方由调用方来处理这个异常。这样的处理多是在方法定义中可以让外部的调用方有更灵活的异常处理方式。
```python
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获取到的输入内容都是string字符串我们通过字符串内置的isdigit()方法来判断其是否为数字类型如果不是函数会通过raise向外抛出一个Exception异常并提示用户“您输入的不是数字这样在调用该函数时通过try-except就可以实时判断用户输入的内容是否符合函数的要求。
![20211012231541](https://i.loli.net/2021/10/12/6Bazc1gN3vXiELD.png)
### 1.5.3 断言语句
断言一般用于单元测试中用于判断一个表达式以及这个表达式为False时触发的异常。在Python中断言的语法为
> assert expression [, arguments]
当assert中的条件为假时会抛出第二个参数定义的异常信息。
```python
num1Str = "m"
assert(num1Str.isdigit(), "num1Str不是数字")
```
等价于
```python
num1Str = "m"
if(num1Str.isdigit()):
pass
else:
raise AssertionError("num1Str不是数字")
```
需要注意的是断言只是作为调试程序的一种手段一般是用来发现Python脚本中的异常。断言所检测的异常在执行Python代码时是可以被忽略的。因此当我们通过断言定位并修改问题后断言内容随后也就可以去掉。
## 1.6 面向对象编程
几乎所有的高级语言都是面向对象的,相比于面向过程,面向对象的编程方式更适合人类的思维模式。在中大型项目中,面向对象的编程风格也可以极大地节约代码量。
Python提供了对面向对象编程的支持有着完善的类、对象以及方法的定义和使用规范。
### 1.6.1 面向对象的编程思想
面向对象最重要的三个特点即为封装、继承、多态。
封装性可以保证在编程时可以将同一类事物的属性和方法封装到一个类中, 通过方法来修改和执行业务, 有利于后期的修改和维护。继承可以实现方法的多态性和代码的可重用性。多态则是为了解决现实生活中的多样性问题, 相同的指令对应不同的子对象,会有该对象特有的执行方式。
举一个简单的例子,假设有一个学生类,学生又都有年龄、年级的属性以及学习这样的方法。在面向对象编程中,我们就可以定义一个学生的父类,其中包含了上述的属性和方法,这里就体现了面向对象的封装性,将同一类事物封装在一个类中。学生中又分为小学生、中学生和大学生,可以根据这些分类创建不同的子类,这些子类也就继承了父类中的属性和方法,这里就体现了面向对象的继承性。不同的学生学习这个方法最终实现会有所不同,例如小学生学习的是小学知识,中学生学习的自然是中学知识,因此仅就“学习”这一个相同的方法,不同的子类可能会有不同的处理,这里就体现出了面向对象的多态性。
### 1.6.2 Python的面向对象特色
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()
```
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方法
```python
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和JuniorStudent虽然都没有在类中定义自己的age、grade以及name但是因为在定义类时继承了Student它们也就继承了父类Student定义的这些属性。而在两个子类中各自重写了父类的study方法后也会自动调用子类中的方法。
> 类属性和方法的访问限制
Python中对类属性和方法的访问限制主要是通过名称来控制的。当名称包含两个下划线“__”则说明这是一个私有变量或私有方法。上面的例子中当我们把name改为__name在外部就无法访问到这个属性了。
```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+"在学习基础知识")
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
### 2. 为什么都说Python相比于其它语言更简单
事实上想要真正精通Python一点也不简单只是Python的入门基础知识比较简单学习曲线较为平滑而这些基础知识已经可以应付生活工作中的80%以上的场景。不像有些语言那样,很多初学者往往都是倒在入门的门槛上,根本都没有机会能够流畅地学完所有的基础语法。
另外Python因为具备海量的第三方库很多即使是刚刚入门的同学也能迅速结合这些第三方库“黏合”出一个看上去比较复杂的程序就像造一辆汽车C语言往往需要从如何挖矿开始Java需要从如何拧螺丝钉开始而Python则是直接提供了轮子、方向盘、底盘你只需要按照自己的想法将这些组件搭建起来就可以“造”出一辆汽车这其中哪一个难度更低就显而易见了。
简单来讲Python可以在学习中给予学习者更多的正反馈这种正反馈也就让学习者有了Python相较其他语言更“简单”的感觉。
## 实训如何用Python轻松处理excel
![6ad7cbfac56f95fc468126f0a2439bb](https://i.loli.net/2021/10/13/S8Ct3TNMjziUDhk.png)
![cb3b015b907bd89f79a4e25b73ef9cb](https://i.loli.net/2021/10/13/o7yDKuVQSYNqExk.png)