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

855 lines
42 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.2.1 基础数据类型
Python 3 为了便于大家编写程序支持多种数据类型。主要由六类标准的数据类型构成它们分别是Number数字、String字符串、List列表、Tuple元组、Set集合、Dictionary字典
Python语言通过对多个数据元素进行组合为开发者们提供了几个非常有特色的复合数据类型。List、Tuple、Set、Dictionary都是非常典型的复合数据类型。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。
$$
```python
>>> i = 5e3
>>> print(i1)
5000.0
>>> j = 3E-3
>>> print(j)
0.003
```
- String字符串
除了数字Python支持的另一种常见数据类型为字符串在程序中定义一个字符串类型的数据是非常容易的。使用单引号')或双引号("将内容圈起来引号内的内容即为字符串本身。Python没有单独定义字符类型一个字符就是一个只有一个字符的字符串。
```python
>>> str1 = '床前明月光'
>>> print(str1)
床前明月光
>>> str2 = "疑是地上霜"
>>> print(str2)
疑是地上霜
```
那么如果在字符串中出现单引号或双引号该怎么办python提供了\来对其中的引号进行转义。避免和用于定义字符串所使用的引号混淆。
```python
>>> str3 = '床前\'明月\'光'
>>> print(str3)
床前'明月'
```
如果我们要表达的字符串内容是多行的,可以采用三个单引号 ''' 或三个双引号 """ 将多行内容圈起来。当然也可以使用特定字符 \r\n 来实现换行效果。
```python
>>> 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中的元素个数。
```python
>>> 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。
```python
>>> 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。
```python
>>> 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前边。
```python
>>> 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]。
```python
>>> 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。
```python
>>> Tuple1 = (1)>>> type(Tuple1)<class 'int'>>>> Tuple2 = ( 1, )>>> type(Tuple2)<class 'tuple'>
```
实际上Python在显示只有1个元素的Tuple类型数据时也会加一个号避免误会。
```python
>>> 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。
```python
>>> 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。
```python
>>> Dict1 = { 'A' : 1 , 'B' : 2 , 'C' : 3 }>>> 'A' in Dict1True>>> 'D' in Dict1False
```
通过内置方法get()来判断key是否存在存在返回key对应的value不存在则返回get()函数中预先定义的值没有预先定义值则返回None。
```python
>>> 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中。
```python
>>> 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。
```python
>>> 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() ) )
```python
>>> 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集合是一个无序、无重复元素的集合。元素间用逗号分隔再用大括号 {} 将所有元素括起来。当Set中无元素时要用set()表示,而不是用{}表示因为这会和空的Dict混淆。 例如Set1 = {1, 2, 3}。为了便于和 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}。
```python
>>> 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。
```python
>>> Set1 = set([1, 2, 3])>>> 1 in Set1True>>> 4 in Set1False
```
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 = '文本内容1'#注释内容1>>> print(str1)文本内容1
```
通常单行注释使用井号。当需要注释的内容涉及多行时,通常采用三个单引号 ''' 或三个双引号 """来标记注释内容 ,即在注释的多行内容首位添加该符号。这种情况一般被叫做块注释或批量注释。这样的注释还常常被称为文档字符串。
- 转义
在Python中使用反斜杠\转义特定字符倘若不想转义只需在字符串前加r或R。就可以在字符串中显示原始的反斜线符号。
```python
>>> 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条件判断语句编写样式如下所示
```python
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子句可以不使用如果使用必须作为最后一个判断子句。
```python
#多个elifif condition1: statement1 elif condition2: statement2 elif condition3: statement3 else: statement4 #缺失elifif condition1: statement1 else: statement2 #缺失elseif condition1: statement1 elif condition2: statement2 #仅有ifif condition1: statement1
```
由于 Python 3 中并没有提供 switch、case 类的条件判断语句,所以开发者们可以通过在 if 子句后边追加多个 elif 子句的方法实现类似效果。
```python
score = 60if score < 60: print('不及格')elif score < 85: print('还不错')elif score <= 100: print('优秀')else: print('数据异常')
```
- while 循环
while 语句是构建一个循环的最简单方法,只需要定义一个条件,当条件满足时,就执行 while 子句中的内容。当条件不满足时循环终止,并执行 else 子句中的内容。
完整的 while 循环语句编写样式如下所示:
```python
while condition: statement1else: statement2
```
while 后边的 condition 是循环的判断条件。
当 condition 为 True 时,执行 while 子句的代码块 statement1。
当 condition 为 False 时,跳出 while 循环。执行 else 子句的代码块 statement2。
由于else子句可缺省在没有else子句的情况下当condition条件不满足时直接跳出while循环。
```python
while condition: statement
```
以下代码通过 while 循环来计算 5 个 10 相加之和:
```python
num = 1res = 0while num <= 5: res += 10 num += 1else: print(res)
```
- for 循环
for 语句在构建一个循环时需要指定用于循环的序列通常是一个List或者Tuple。
完整的 for 循环语句编写样式如下所示:
```python
for i in seq: statement1else: statement2
```
变量 i 遍历序列 seq 中的每一个元素,并在遍历的过程中不断重复执行代码块 statement1。
当 i 遍历完 seq 中的所有元素后for 循环结束。
当循环无法启动时,执行 else 子句对应的代码块 statement2。
以下代码通过 for 循环来计算 5 个 10 相加之和:
```python
res = 0for i in [10, 10, 10, 10, 10]: res = res + ielse: print(res)
```
由于else子句可缺省在没有else子句的情况下for循环可简化成以下形式
```python
for i in seq: statement1
```
开发者们在编写程序时遇到的情况往往是多种多样的。为了让循环更灵活python 提供了循环控制命令。
| 命令 | 说明 |
| -------- | ---------------------------------------------------------- |
| break | 终止循环。 用break结束循环后循环对应的else 子句不会执行 |
| continue | 跳过本次循环剩余操作,直接进入下次循环 |
| pass | 站位语句,没有实际意义 |
### 1.2.6 了解Python的编码风格
不同的开发语言都有各自不同特点,这也导致各自的开发规范不尽相同。
Python的语法比较简单和其他语言相比定义了相对较少的关键字并采用了缩进的方式来控制代码块之间的逻辑关系这也是Python最大特色。所以代码看起来显得结构清晰简单易懂。
- 缩进
使用缩进来划分语句块,缩进的空格数是可变的,相同缩进程度的的语句隶属于同一个语句块。摒弃了其他开发语言常用的大括号 {}代码看起来更简洁。一般建议使用4个空格缩进避免使用制表符。
单行代码不宜过长,最好保持在几十个字符以内。需要注释时,尽量保证注释放单独的一行。
- 变量命名
Python 规定变量的命名只能是字母、数字、下划线 _ 的组合。且变量名的首位只能字母或者下划线。这里所说的字母并不局限于26个英文字母还可以使用中文字符、日文字符等。当然这建议大家给变量命名是尽量在字母这一块只是用26个英文字母。例如num_1、\_num1 是合法变量名,而 1num_ 就是非法的。
需要注意的是,系统定义的关键字不能当变量名使用。另外 Python 是大小写敏感的这就意味着同一个字母大小写不同时代表着不同的意义。例如Num 和 num 表示不同的变量。
```python
>>> Num = 1>>> num = 2>>> print(Num)1>>> print(num)2
```
Python简化了变量的定义过程。变量和变量的数据类型不需要事先声明在给变量赋值时既定义了变量名有定义了变量的数据类型。即代表你给变量赋值了什么类型的数据变量就是什么数据类型。
- 关键字
Python 3 定义的关键字并不多,且关键字本身也很简洁。我们可以通过 keyword 模块的 kwlist 查看到所有的关键词。
```python
>>> 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并将其转成字符串。
```python
>>> input()good morning'good morning'
```
print() 方法将接收到的 'good morning' 显示输出。
```python
>>> str = 'good morning'>>> print(str)good morning
```
### 1.3.1 文件的打开与读取
读写文件也是常见的IO操作Python 3 内置了相关的方法。通过内置方法 open() 打开文件,返回对应的 file 对象,通过 file 对象的提供的方法读取并解析文件内容。
```python
>>> 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()方法的结果如下:
```python
>>> 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'])
```
显示目录下所有路径:
```python
>>> 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\part1E:\data\part2E:\data\1.txtE:\data\part1\4.txtE:\data\part1\5.txtE:\data\part2\8.txtE:\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) | 返回文件名 <br />os.path.basename("E:\\Data\\1.txt") 返回值:'1.txt' |
| os.path.join( dir, file/dir ) | 拼接目录与文件名/目录<br />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()方法实现解码。
```python
>>> import json>>> j = { 1:'attr1', 2:'attr2', 3:'attr3' }>>> json.dumps(j)'{"1": "attr1", "2": "attr2", "3": "attr3"}'
```
通过dump()方法将输入 j 转换成 JSON 格式:'{"1": "attr1", "2": "attr2", "3": "attr3"}'。需要注意的是标准JSON格式的字符串必须使用双引号",而不能使用单引号 '。
## 1.4 函数
在软件开发过程中,编写函数上开发者们经常要遇到的工作。针对不同业务开发了诸多函数后,接下来的开发工作中可以很方便的重复使用。函数帮助程序实现模块化,便于代码的复用,提高了代码的可读性。函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
我们前面用到的print(),open()等方法就是非常典型的函数他们都是由Python预先准备好的被称为内建函数。
### 1.4.1 函数的基本定义
定义函数使用 def 关键字,一般格式如下:
```python
def func( p1, p2 ...): codes return res1, res2 ...
```
函数以关键字def开头后边的func表示函数名称圆括号 () 中的 p1、p2 表示函数的输入参数,简称入参。函数的入参必须放在圆括号内,且支持多个入参。
圆括号 () 后必须跟冒号 : ,冒号 :表示后边的内容都是函数内容,通常称为函数体。本例中,第二行开始表示函数的具体内容,需要相对第一行进行缩进。
关键词return后跟res1、res2表示函数的返回值为res1、res2定义函数时可以没有关键字return此时函数返回值为 None。return是一个函数结束的标志即执行完return整个函数就会结束运行。res可以是任意数据类型且return的返回值没有个数限制当返回多个值时返回值之间用逗号分隔。
### 1.4.2 函数调用
想要在程序中调用某个函数,只需要知道该函数的名称和参数限制既可。
```python
>>> def func(i): print(i) # 调用函数func>>> func('Hello')Hello
```
```
# def func():# print('1111')# print('222')# print('333')4函数调用阶段执行代码4、函数定义的三种形式定义函数时的参数就是函数体接收外部传值的一种媒介其实就是一个变量名1、无参函数#在函数定义阶段括号内没有参数注意:定义无参,意味着调用时也无需传入参数应用:如果函数体的代码逻辑不需要依赖外部传入的值,必须定义无参函数# def func():# print('hello world')# func()2、有参函数#在函数定义阶段括号内有参数,称为有参函数注意:定义时有参,意味着调用时也必须传入参数应用:如果函数体代码逻辑需要依赖外部传入的值,必须定义成有参函数# def sum2(x,y):# # x=10# # y=20# res=x+y# print(res)## sum2(10,20)# sum2(30,40)3空函数# def func():# pass
```
### 1.4.3 递归函数
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
举个例子,我们来计算阶乘`n! = 1 x 2 x 3 x ... x n`,用函数`fact(n)`表示,可以看出:
fact(n)=n!=1\times2\times3\times\cdot\cdot\cdot\times(n-1)\times n=(n-1)!\times n=fact(n-1)\times n*f**a**c**t*(*n*)=*n*!=1×2×3×⋅⋅⋅×(*n*1)×*n*=(*n*1)!×*n*=*f**a**c**t*(*n*1)×*n*
所以,`fact(n)`可以表示为`n x fact(n-1)`只有n=1时需要特殊处理。
于是,`fact(n)`用递归的方式写出来就是:
```
def fact(n): if n==1: return 1 return n * fact(n - 1)
```
上面就是一个递归函数。可以试试:
```
>>> fact(1)1>>> fact(5)120>>> fact(100)93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
```
如果我们计算`fact(5)`,可以根据函数定义看到计算过程如下:
```ascii
===> fact(5)===> 5 * fact(4)===> 5 * (4 * fact(3))===> 5 * (4 * (3 * fact(2)))===> 5 * (4 * (3 * (2 * fact(1))))===> 5 * (4 * (3 * (2 * 1)))===> 5 * (4 * (3 * 2))===> 5 * (4 * 6)===> 5 * 24===> 120
```
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中函数调用是通过栈stack这种数据结构实现的每当进入一个函数调用栈就会加一层栈帧每当函数返回栈就会减一层栈帧。由于栈的大小不是无限的所以递归调用的次数过多会导致栈溢出。可以试试`fact(1000)`
```
>>> fact(1000)Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in fact ... File "<stdin>", line 4, in factRuntimeError: maximum recursion depth exceeded in comparison
```
解决递归调用栈溢出的方法是通过**尾递归**优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
尾递归是指在函数返回的时候调用自身本身并且return语句不能包含表达式。这样编译器或者解释器就可以把尾递归做优化使递归本身无论调用多少次都只占用一个栈帧不会出现栈溢出的情况。
上面的`fact(n)`函数由于`return n * fact(n - 1)`引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中:
### 1.4.4 匿名函数
### Lambda 表达式
可以用 [`lambda`](https://docs.python.org/zh-cn/3.8/reference/expressions.html#lambda) 关键字来创建一个小的匿名函数。这个函数返回两个参数的和: `lambda a, b: a+b` 。Lambda函数可以在需要函数对象的任何地方使用。它们在语法上限于单个表达式。从语义上来说它们只是正常函数定义的语法糖。与嵌套函数定义一样lambda函数可以引用所包含域的变量:
\>>>
```
>>> def make_incrementor(n):... return lambda x: x + n...>>> f = make_incrementor(42)>>> f(0)42>>> f(1)43
```
上面的例子使用一个lambda表达式来返回一个函数。另一个用法是传递一个小函数作为参数:
\>>>
```
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]>>> pairs.sort(key=lambda pair: pair[1])>>> pairs[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
```
python 使用 lambda 来创建匿名函数。
所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
- lambda 只是一个表达式,函数体比 def 简单很多。
- lambda的主体是一个表达式而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
lambda 函数的语法只包含一个语句,如下:
```
lambda [arg1 [,arg2,.....argn]]:expression
```
如下实例:
\#!/usr/bin/python3 # 可写函数说明 sum = lambda arg1, arg2: arg1 + arg2 # 调用sum函数 print ("相加后的值为 : ", sum( 10, 20 )) print ("相加后的值为 : ", sum( 20, 20 ))
以上实例输出结果:
```
相加后的值为 : 30相加后的值为 : 40
```
### 1.4.5 装饰器
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
```
>>> def now():... print('2015-3-25')...>>> f = now>>> f()2015-3-25
```
函数对象有一个`__name__`属性,可以拿到函数的名字:
```
>>> now.__name__'now'>>> f.__name__'now'
```
现在,假设我们要增强`now()`函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改`now()`函数的定义这种在代码运行期间动态增加功能的方式称之为“装饰器”Decorator
本质上decorator就是一个返回函数的高阶函数。所以我们要定义一个能打印日志的decorator可以定义如下
```
def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
```
观察上面的`log`因为它是一个decorator所以接受一个函数作为参数并返回一个函数。我们要借助Python的@语法把decorator置于函数的定义处
```
@logdef now(): print('2015-3-25')
```
调用`now()`函数,不仅会运行`now()`函数本身,还会在运行`now()`函数前打印一行日志:
```
>>> now()call now():2015-3-25
```
`@log`放到`now()`函数的定义处,相当于执行了语句:
```
now = log(now)
```
由于`log()`是一个decorator返回一个函数所以原来的`now()`函数仍然存在,只是现在同名的`now`变量指向了新的函数,于是调用`now()`将执行新函数,即在`log()`函数中返回的`wrapper()`函数。
`wrapper()`函数的参数定义是`(*args, **kw)`,因此,`wrapper()`函数可以接受任意参数的调用。在`wrapper()`函数内,首先打印日志,再紧接着调用原始函数。
如果decorator本身需要传入参数那就需要编写一个返回decorator的高阶函数写出来会更复杂。比如要自定义log的文本
```
def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator
```
这个3层嵌套的decorator用法如下
```
@log('execute')def now(): print('2015-3-25')
```
执行结果如下:
```
>>> now()execute now():2015-3-25
```
和两层嵌套的decorator相比3层嵌套的效果是这样的
```
>>> now = log('execute')(now)
```
我们来剖析上面的语句,首先执行`log('execute')`,返回的是`decorator`函数,再调用返回的函数,参数是`now`函数,返回值最终是`wrapper`函数。
以上两种decorator的定义都没有问题但还差最后一步。因为我们讲了函数也是对象它有`__name__`等属性但你去看经过decorator装饰之后的函数它们的`__name__`已经从原来的`'now'`变成了`'wrapper'`
----
需要注意的是python中并没有定义“常量”这样的数据类型。
- 在运算符前后和逗号后使用空格,但不能直接在括号内使用: `a = f(1, 2) + g(3, 4)`
- 以一致的规则为你的类和函数命名;按照惯例应使用 `UpperCamelCase` 来命名类,而以 `lowercase_with_underscores` 来命名函数和方法。 始终应使用 `self` 来命名第一个方法参数 (有关类和方法的更多信息请参阅 [初探类](https://docs.python.org/zh-cn/3.8/tutorial/classes.html#tut-firstclasses))。
- 如果你的代码旨在用于国际环境请不要使用花哨的编码。Python 默认的 UTF-8 或者纯 ASCII 在任何情况下都能有最好的表现。
- 同样哪怕只有很小的可能遇到说不同语言的人阅读或维护代码也不要在标识符中使用非ASCII字符。
开发者们 编写程序
Python Python 3