1. 单线程同步流程控制
python与多数其他语言一样默认是单线程同步的顺序执行语句.这也是所有语言的基础.
python中语句会一行一行地被解释执行,而流控制语句则是用于控制当前解释执行哪条语句的工具
1.1. 流程分支
python只有if语句没有switch,这也带来了语义上的歧义:
if/else代表判断逻辑,一般用于根据一条语句的结果正误来确定下一步的执行内容if/elif/else代表分支,一般用于根据一个变量的取值不同来确定下一步执行的内容.
虽然这种方式看似减少了关键字数量,但实际上增加了错误语义的使用可能.个人认为是python设计的一大败笔.
1.1.1. 使用字典结合匿名函数更加优雅的实现简单流程分支
if/elif来实现switch并取优雅,需要为一个变量写多次判断语句,因此如果分支逻辑简单,完全可以使用字典+lambda函数
def which_type(x):     return {         int:lambda x:print(x,"is int"),         float:lambda x:print(x,"is float"),         list:lambda x:print(x,"is list")     }.get(type(x),lambda x:print("i dont know"))(x)  which_type(2)  2 is int 1.2. 循环语句
循环语句主要目的就是让一段代码反复顺序执行,python有两种循环语句:
for循环
主要用于根据先验循环次数执行循环内代码的情况.他会遍历一个迭代器,先验的次数就是这个迭代器的长度,每取出一个对象都会执行for块中的语句
while循环
主要用于没有先验循环次数,而是根据判断条件执行循环内代码的情况
for i in range(10):     print(i)  0 1 2 3 4 5 6 7 8 9 i = 0 while i<10:     print(i)     i+=1  0 1 2 3 4 5 6 7 8 9 1.2.1. 打断语句break和continue
循环中可以使用break跳出循环或者使用continue跳出当次循环
i = 0 while True:     i += 1     if i > 10 :         break     if i % 2 == 0 :         continue     print(i)  1 3 5 7 9 1.3. try/except 不仅用于处理错误,还常用于控制流程
在Python中,try/except不仅用于处理错误,还常用于控制流程.为此,Python 官方词汇表还定义了一个缩略词(口号):
- EAFP
 
取得原谅比获得许可容易(easier to ask for forgiveness than permission).这是一种常见的Python编程风格,先假定存在有效的键或属性,如果假定不成立,那么捕获异常.这种风格简单明快,特点是代码中有很多try和except语句.与其他很多语言一样(如C 语言),这种风格的对立面是LBYL风格.
- LBYL
 
三思而后行(look before you leap).这种编程风格在调用函数或查找属性或键之前显式测试前提条件.与EAFP风格相反,这种风格的特点是代码中有很多if 语句.在多线程环境中,LBYL 风格可能会在"检查"和"行事"的空当引入条件竞争.例如对if key in mapping: return mapping[key]这段代码来说,如果在测试之后,但在查找之前,另一个线程从映射中删除了那个键,那么这段代码就会失败.这个问题可以使用锁或者EAFP风格解决.
如果选择使用EAFP风格,那就要更深入地了解else子句,并在try/except语句中合理使用
1.4. 特殊的else语句
else子句不仅能在if语句中使用,还能在for、while和try语句中使用.for/else、while/else和try/else的语义关系紧密,不过与if/else差别很大.
此处的else语句理解为then可能更加合适,它代表的是"没有遇到特殊情况就执行以下代码"这样的语义.具体到不同的语句,语义如下:
for
仅当
for循环运行完毕时)即for循环没有被break语句终止)才运行else块while
仅当
while循环因为条件为假值而退出时(即while循环没有被break语句中止)才运行else块
循环语句中使用else语句可以省去大量的状态变量,最典型的就是为避免网络异常而多次访问某个地址的场景
import requests  which_type(2) 0 which_type(2) 1 which_type(2) 2 which_type(2) 3which_type(2) 4 which_type(2) 5- try
 
仅当try块中没有异常抛出时才运行else块。官方文档还指出--else子句抛出的异常不会由前面的except子句处理.
在所有情况下,如果异常或者return,break或continue语句导致控制权跳到了复合语句的主块之外,else子句也会被跳过
1.4.1. EAFP风格的python代码
上面说道EAFP风格,需要注意的是这种风格下try/else语句应当被大量有针对性地使用,它应当细粒度的防守单条语句而不是像多数人那样防守一段代码.这会让代码看起来很啰嗦,但相对来说更加安全
1.5. 上下文管理器和with块
上下文管理器对象存在的目的是管理with语句,就像迭代器的存在是为了管理for语句一样.
with语句的目的是简化try/finally模式.这种模式用于保证一段代码运行完毕后执行某项操作,即便那段代码由于异常、return语句或sys.exit()调用而中止,也会执行指定的操作.finally子句中的代码通常用于释放重要的资源,或者还原临时变更的状态.
上下文管理器协议包含__enter__和__exit__两个方法.with语句开始运行时,会在上下文管理器对象上调用__enter__方法.with语句运行结束后,会在上下文管理器对象上调用__exit__ 方法,以此扮演finally子句的角色.
最常见的例子是确保关闭文件对象.这在前文已经有所描述.注意,与函数和模块不同,with块没有定义新的作用域.
__enter__()方法__enter__()方法要求最好返回一个对象(如果不返回一个对象,as语句会捕获一个None),一般是self,但不一定.除了返回上下文管理器之外,还可能返回其他对象.
__exit__(exc_type, exc_value, traceback)方法- exc_type 异常类(例如ZeroDivisionError)
 - exc_value   异常实例.有时会有参数传给异常构造方法,例如错误消息,这些参数可以使用
exc_value.args获取 traceback traceback对象
不管控制流程以哪种方式退出with块,都会在上下文管理器对象上调用
__exit__方法,而不是在__enter__方法返回的对象上调用.with语句的as子句是可选的.对open函数来说,必须加上as子句,以便获取文件的引用.不过,有些上下文管理器会返回None,因为没什么有用的对象能提供给用户.
下面看一个上下文管理器修改上下文环境中print函数行为的例子:
which_type(2) 6 which_type(2) 7 which_type(2) 8which_type(2) 9 2 is int 02 is int 1 2 is int 2抛开了with语句,上下文管理器也可以这样使用
2 is int 3 2 is int 4使用try/finally可以这样写
2 is int 5 2 is int 41.5.1. contextlib模块
contextlib模块中提供了一些类和其他函数,用于快速的构建上下文管理器
- closing
 
如果对象提供了close()方法,但没有实现__enter__/__exit__协议,那么可以使用这个函数构建上下文管理器
- suppress
 
构建临时忽略指定异常的上下文管理器
- @contextmanager
 
这个装饰器把简单的生成器函数变成上下文管理器,这样就不用创建类去实现管理器协议了
- ContextDecorator
 
这是个基类,用于定义基于类的上下文管理器.这种上下文管理器也能用于装饰函数,在受管理的上下文中运行整个函数.
- ExitStack
 
这个上下文管理器能进入多个上下文管理器.with块结束时,ExitStack按照后进先出的顺序调用栈中各个上下文管理器的__exit__方法.如果事先不知道with块要进入多少个上下文管理器,可以使用这个类.例如同时打开任意一个文件列表中的所有文件.
1.5.2. 使用@contextmanager
@contextmanager装饰器能减少创建上下文管理器的样板代码量,因为不用编写一个完整的类,定义__enter__和__exit__方法,而只需实现有一个yield语句的生成器,生成想让__enter__方法返回的值. 在使用@contextmanager装饰的生成器中,yield语句的作用是把函数的定义体分成三部分:
- yield 语句前面的所有代码在with块开始时(即解释器调用
__enter__方法时)执行 - yield 语句,用于抛出
__enter__要返回的对象,并可以接收异常 - yield 语句后面的代码在with块结束时(即调用
__exit__方法时)执行 
2 is int 7 2 is int 8 2 is int 9其实,contextlib.contextmanager装饰器会把函数包装成实现__enter__和__exit__方法的类.
这个类的__enter__方法有如下作用:
- 调用生成器函数,保存生成器对象(这里把它称为gen)
 - 调用
next(gen),执行到yield关键字所在的位置. - 返回
next(gen)产出的值,以便把产出的值绑定到with/as语句中的目标变量上 
with块终止时,__exit__方法会做以下几件事
- 检查有没有把异常传给
exc_type;如果有,调用gen.throw(exception),在生成器函数定义体中包含yield关键字的那一行抛出异常. - 否则调用
next(gen),继续执行生成器函数定义体中yield语句之后的代码 
如果在with块中抛出了异常,Python解释器会将其捕获,然后在looking_glass函数的yield表达式里再次抛出.但是那里没有处理错误的代码,因此looking_glass函数会中止,永远无法恢复成原来的sys.stdout.write方法,导致系统处于无效状态.因此上面的例子并不完整,下面给出完整的例子
for i in range(10):     print(i) 0 for i in range(10):     print(i) 1 for i in range(10):     print(i) 2 

		
		
		
		

还没有评论,来说两句吧...