Python02进阶
Python进阶
词典 dictionary
创建词典
1 | dic = {'tom':11,'sam':45,'lily':32} |
词典和表类似的地方,是包含有多个元素,每个元素以逗号分隔。但词典的元素包含有两部分,键和值,常见的是以字符串来表示键,也可以使用数字等表示键(不可变的对象可以作为键)。值可以是任意对象。键和值两者一一对应。
与表不同的是,词典的元素没有顺序。你不能通过下标引用元素。词典是通过键来引用。
词典元素的循环调用
1 | dic = {'tom':11,'sam':45,'lily':32} |
在循环中,dict的每个键,被提取出来,赋予给key变量。
词典的常用方法
1 | dic = {'tom':11,'sam':45,'lily':32} |
文本文件的输入输出
Python具有基本的文本文件读写功能。Python的标准库提供有更丰富的读写功能。
文本文件的读写主要通过**open()**所构建的文件对象来实现。
创建文件对象
1 | 对象名 = open(文件名,模式) |
1 | 模式如下: |
1 | # 如: |
文件对象的方法
1 | f = open('test.txt','r+',encoding='utf-8') |
模块引入
1 | # first.py |
在second.py中,使用first.py中定义的laugh()函数。
引入模块后,可以通过 模块.对象 的方式来调用引入模块中的某个对象。上面例子中,first 为引入的模块,laugh() 是我们所引入的对象。
Python中还有其它的引入方式:
1 | # 引入模块a,并将模块a重命名为b |
模块包
可以将功能相似的模块放在同一个文件夹(比如说this_dir)中,构成一个模块包。通过
1 | import this_dir.module # 引入this_dir文件夹中的module模块。 |
该文件夹中必须包含一个 __init__.py 的文件,提醒Python,该文件夹为一个模块包。__init__.py 可以是一个空文件。
函数的参数传递
函数(function)的参数(arguments)传递。根据位置,传递对应的参数。Python还支持更多的参数传递方式。
关键字传递
关键字(keyword)传递是根据每个参数的名字传递参数。关键字并不用遵守位置的对应关系。依然沿用上面f的定义,更改调用方式:
1 | print(f(c=3,b=2,a=1)) |
参数默认值
在定义函数的时候,使用形如a=19的方式,可以给参数赋予默认值(default)。如果该参数最终没有被传递值,将使用该默认值。
1 | def f(a,b,c=10): |
包裹传递
在定义函数时,我们有时候并不知道调用的时候会传递多少个参数。这时候,包裹(packing)位置参数,或者包裹关键字参数,来进行参数传递,会非常有用。
1 | def func(*name): |
在func的参数表中, 所有的参数被name收集, 根据位置合并成一个元组(tuple), 赋值给name, 这就是包裹传递。
1 | def func(**dict): |
与上面一个例子类似,dict是一个字典,收集所有的关键字,传递给函数func。为了提醒Python,参数dict是包裹关键字传递所用的字典,在dict前加 **
即 包裹传递的关键在于定义函数时,在相应元组或字典前加* 或 ** 。
解包裹
* 和 **,也可以在调用的时候使用,即解包裹(unpacking), 下面为例:
1 | def func(a,b,c): |
在传递tuple时,让tuple的每一个元素对应一个位置参数。在调用func时使用 * ,是为了提醒Python:要把args拆成分散的三个元素,分别传递给a,b,c
相应的,也存在对词典的解包裹,使用相同的func定义
1 | def func(a,b,c): |
在传递词典dict时,让词典的每个键值对作为一个关键字传递给func。
混合
在定义或者调用参数时,参数的几种传递方式可以混合。但在过程中要小心前后顺序。基本原则是:先位置,再关键字,再包裹位置,再包裹关键字
循环设计
range()
1 | str = 'abcdefghijk' |
我们利用 len() 函数和 range() 函数,用 i 作为 S 序列的下标来控制循环。在range函数中,可以分别定义上限,下限和每次循环的步长。
enumerate()
利用enumerate()枚举函数,可以在每次循环中同时得到下标和元素:
1 | str = 'abcdefghijk' |
1 | # 枚举统计文件行数 |
enumerate()在每次循环中,返回的是一个包含两个元素的定值表(tuple),两个元素分别赋予index和char。
zip()
多个等长的序列,然后想要每次循环时从各个序列分别取出一个元素,可以利用zip()
zip()函数的功能,就是从多个列表中,依次各取出一个元素。每次取出的(来自不同列表的)元素合成一个元组,合并成的元组放入zip()返回的列表中。zip()函数起到了聚合列表的功能。
1 | tupleA = [1,2,3] |

zip_longest()
1 | # zip_longest() 最长列表为基准,空值指定为0而不是None |

可以分解聚合后的列表,如下:
1 | # 分解聚合后的列表 |

可迭代对象
迭代对象
迭代对象是一个包含有 __iter__ 方法的对象。
一个可迭代对象的 __iter__ 方法实际上返回的一个迭代器,并且如期响应 next 方法。而在迭代完成后,再次使用 next 时,抛出 StopIteration错误。
列表其实也是一个迭代对象,包含有 __iter__方法。在我们使用 iter(lst) 返回一个迭代器 test_iter 后,使用 next()方法每次迭代一个元素,到迭代完成后继续使用 next 抛出StopIteration。
1 | list1 = [1,2,3,4,5] |

迭代器
迭代器是包含 __next__ 方法的任何对象,(因为包含 __next__ 方法,因此能响应 next 方法)
生成器也是一种迭代器。
生成器的编写方法和函数定义类似,只是在 return 的地方改为 yield。生成器中可以有多个 yield。当生成器遇到一个 yield 时,会暂停运行生成器,返回 yield 后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个 yield。每次使用一个 yield 返回的值。
1 | # 生成器 该生成器共有三个yield, 如果用作迭代器时,会进行三次循环。 |

可以写成**生成器表达式(Generator Expression)**:
1 | def genera2(): |

列表推导
列表推导(list comprehension)是快速生成列表的方法。语法简单,很有实用价值。
1 | list1 = [] |

函数对象
函数也是一个对象,具有属性(可以使用dir()查询)。作为对象,它还可以赋值给其它对象名,或者作为参数传递。
lambda函数
lambda生成一个函数对象。该函数参数为x,y,返回值为x+y。函数对象赋给func。func的调用与正常函数无异。
1 | def func1(x,y): |

函数作为参数传递
函数可以作为一个对象,进行参数传递。函数名(比如func)即该对象
1 | func = lambda x,y: x+y # 定义函数 |

map()函数
map()的功能是将函数对象依次作用于表的每一个元素,返回表存储每次作用的结果, 它的第一个参数是一个函数对象。
1 | test = map((lambda x: x**2),[1,2,3,4,5]) |

filter()函数
filter函数的第一个参数也是一个函数对象。它也是将作为参数的函数对象作用于多个元素。如果函数对象返回的是True,则该次的元素被储存于返回的表中。 filter通过读入的函数来筛选数据。
1 | def bigger(a): |

reduce()函数
reduce函数的第一个参数也是函数,但有一个要求,就是这个函数自身能接收两个参数。reduce可以累进地将函数作用于各个参数。如下例:
1 | from functools import reduce |

1 | reduce的第一个参数是lambda函数,它接收两个参数x,y, 返回x+y。 |
错误处理
异常处理
try-except语法:
1 | try: |
报错案例
1 | list_iter = iter(range(5)) |

使用try-except。在try程序段中,我们放入容易犯错的部分。我们可以跟上except,来说明如果在try部分的语句发生StopIteration时,程序该做的事情。如果没有发生异常,则except部分被跳过。
1 | list_iter = iter(range(5)) |

报错流程: try -> 异常 -> except -> finally
如果无法将异常交给合适的对象,异常将继续向上层抛出,直到被捕捉或者造成主程序报错。比如下面的程序:
1 | def test_func(): |

抛出异常
也可以使用raise自己主动抛出异常
1 | raise StopIteration # 主动抛出异常 |

动态类型
动态类型(dynamic typing)。即Python的变量(variable)不需要声明,而在赋值时,变量可以重新赋值为任意值。这些都与动态类型的概念相关。
动态类型
引用和对象分离,是动态类型的核心。引用可以随时指向一个新的对象:
1 | a = 3 # 通过赋值,引用a指向对象3。 |
第一个语句中,3是储存在内存中的一个整数对象。通过赋值,引用a指向对象3。
第二个语句中,内存中建立对象‘at’,是一个字符串(string)。引用a指向了’at’。此时,对象3不再有引用指向它。Python会自动将没有引用指向的对象销毁(destruct),释放相应内存。
对于小的整数和短字符串,Python会缓存这些对象,而不是频繁的建立和销毁。
1 | a = 5 |
即使是多个引用指向同一个对象,如果一个引用值发生变化,那么实际上是让这个引用指向一个新的引用,并不影响其他的引用的指向。从效果上看,就是各个引用各自独立,互不影响。
1 | L1 = [1,2,3] |
即列表(list)和字典(dictionary)可以通过引用其元素,改变自身对象(in-place change),称为: 可变数据对象(mutable object)
而数字/字符串, 不能改变对象本身, 只能改变引用指向, 称为不可变数据对象(immutable object)
元组(tuple)虽然可以调用引用元素, 但不可赋值, 所以也算不可变数据对象
从动态类型看函数的参数传递
函数的参数传递,本质上传递的是引用。
1 | def func(x): |
参数x是一个新的引用,指向a所指的对象。
如果参数是不可变(immutable)的对象,a和x引用之间相互独立。对参数x的操作不会影响引用a。
如果传递的是可变(mutable)的对象,那么改变函数参数,有可能改变原对象。所有指向原对象的引用都会受影响