### 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)>>> Tuple2 = ( 1, )>>> type(Tuple2) ``` 实际上,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' 是该键值对的键(key),1是该键值对的值(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) | 返回文件名
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()方法实现解码。 ```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 "", line 1, in File "", line 4, in fact ... File "", 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