不得不说。。。这个用法。。。。实在是。。。。太。。。。丧病了!!!!!!!!!
看了一下午都不是很清楚他怎么运作的。。。。我说各位写blog的大大们。。。。能说人话嘛。。。。。。。TAT
25.11.14补充:W3C讲得更清楚一些。
我先记录一下我的学习成果。。。。以防止以后忘记了。。。。。真是看了前面忘后面啊。。。。我勒个去。。。
-----------------华丽丽的分割线-----------------------
with as 呢,就是个python控制流语句,像 if 啊,while啊 一样。这个很好理解
那么当在网上搜 with as语句的时候呢,几乎每篇文章上来就说,这个语句啊,是简化版的try except finally语句,变得非常友好。。。。。我勒个擦!!友好泥煤啊。。。。try except finally是什么。。。有考虑过初学小白的心情吗。。。当然我就不说有些解释try finally语句的文章 上来就说with as语句。。。。。死循环了好吗。。。。。
呼。。。言归正传,那我们先理解一下try except finally语句是干啥的吧。。。
实际上呢,try/except语句和try/finally语句是两种语句,用于不同的场景。但是当二者结合在一起时,可以“实现稳定性和灵活性更好的设计”。很高大上的样子!!
1. try/except语句
用于处理程序执行过程中的异常情况,比如语法错误、从未定义变量上取值等等,也就是一些python货程序本身引发的异常、报错。比如你在python下面输入 1 / 0:
系统会给你一个ZeroDivisionError的报错。
说白了就是为了防止一些报错影响你的程序继续运行,就用try语句把他们抓出来(捕获)。
try/except的标准格式:
-->执行normal block
-->发现有A错误,执行 exc A block(即处理异常)
-->结束
如果没有A错误呢?
-->执行normal block
-->发现B错误,开始寻找匹配B的异常处理方法,发现A,跳过,发现except others(即except:),执行exc other block
-->结束
如果没有错误呢?
-->执行normal block
-->全程没有错误,跳入else 执行noError block
-->结束
Tips: 我们发现,一旦跳入了某条except语句,就会执行相应的异常处理方法(block),执行完毕就会结束。不会再返回try的normal block继续执行了。(这里W3C没有说人话请注意,但意思一样。) 因此,如果想要一次发现多个错误,就得用try嵌套。(这个就深了,以后再补充)
举例(这段程序可以直接复制粘贴运行):
结果是,先打出了一个0,又打出了一个Error。就是把ZeroDivisionError错误捕获了。
程序怎么流转的呢?
先执行try后面这一堆语句,由上至下:
step1: a 正常,打印a. 于是打印出0 (因为a不是浮点型,0.5就会打印出0)
step2: b, 不正常了,0 不能做除数,所以这是一个错误。也别打印b了,直接跳到except报错去。于是打印了Error。
step3: 其实没有step3,因为程序结束了。c,是在错误发生之后的b语句后才出现,根本轮不到执行它。也就看不到打印出的c了
但这还不是try/except的所有用法
except后面还能跟表达式的! 所谓的表达式,就是错误的定义。也就是说,我们可以捕捉一些我们想要捕捉的异常。而不是什么异常都报出来。
异常分为两类:
python标准异常
自定义异常。
我们先抛开自定义异常(因为涉及到类的概念),看看except都能捕捉到哪些python标准异常。
W3C有个总结表格:http://www.w3cschool.cc/python/python-exceptions.html 哈哈 偷懒结束。直接来个栗子吧!
我们看到,当程序执行到print m的时候 发现了一个NameError: name 'm' is not defined,于是控制流去寻找匹配的except异常处理语句。咦!发现了第一条匹配,执行对应block。执行完结束。
好了。那这个语句到底有什么应用场景呢?之前我觉得毫无用处啊! 系统有报错就报错嘛,把代码改好就行噜,干啥还要捕捉。不过当我知道可以自拟定异常的时候,就觉得。。。哦!!原来还是有用的。 比如,让一个人try取钱,如果他取的钱小于他的存款,那就else成功取出。如果要取的钱大于他的存款,那就报错。而这种错误系统并不能识别,因为可以把钱搞成负值....银行怎么能干呢?
不过自拟定错误怎么做呢?网上一堆例子全是用类,但是我真的还没学啦。。。。这部分我以后补充好了。。。。
2.try/finallly语句
用于无论执行过程中有没有异常,都要执行清场工作。比如try后面语句执行时报错了,Dont worry babe!! 直接执行finally语句即可哦!不会直接系统报错。
好的 现在我们看看他俩合在一起怎么用!!
通过前面的解释,这段代码应该很好理解了。
tips: 注意顺序不能乱,否则会有语法错误
except, else, finally 都不是必须的。注意排列组合。但如果用else就必须有except,否则会有语法错误。
来个栗子---
你也可以用上面代码注释掉几行看看会有什么不同的结果。
不过我也发现了一个问题:当缩进有错的时候(比如应该缩进四个space,结果打成了一个tab),缩进错误IndentationError捕捉不到。直接跳出了。好奇怪。。。
-----------------------------------------分割。。。分割。。。。--------------------------------------
try语句终于搞清楚了! 那么可以继续with as的探险了
with as 语句的结构如下
看这个结构我们可以获取至少两点信息 1. as可以省略 2. 有一个句块要执行
好了。那么这句话如何执行呢?容我先粘一段别人写的,然后再一点点解谜......
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
from: http://blog.csdn.net/elevenqiao/article/details/6796653
with是一个控制流语句,跟if/for/while/try之类的是一类的,with可以用来简化try finally代码,看起来可以比try finally更清晰。
这里新引入了一个"上下文管理协议"context management protocol,实现方法是为一个类定义__enter__和__exit__两个函数。
with expresion as variable的执行过程是,首先执行__enter__函数,它的返回值会赋给as后面的variable,想让它返回什么就返回什么,只要你知道怎么处理就可以了,如果不写as variable,返回值会被忽略。
然后,开始执行with-block中的语句,不论成功失败(比如发生异常、错误,设置sys.exit()),在with-block执行完成后,会执行__exit__函数。
这样的过程其实等价于:
try:
执行 __enter__的内容
执行 with_block.
finally:
执行 __exit__内容
只不过,现在把一部分代码封装成了__enter__函数,清理代码封装成__exit__函数。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
是不是有点儿晕了?。。哈哈 别捉急容我慢慢道来:
所谓上下文管理协议,其实是指with后面跟的expression。这个expression一般都是一个类的实体。这个类的实体里面要包含有对__enter__和__exit__函数的定义才行。
--那么,首先,什么是类呢? 类的定义好多书上都讲过,我就简单举个栗子:
比如,公司员工,就是一个类咯。公司员工基本信息大家都一样,比如姓名,性别,岗位名称,工资,从属部门等等。我们就可以定义个类名叫做employee。除了给类定义一些上述属性之外,还可以定义类的方法。也就是允许对类的内容有哪些操作,比如可以对employee这个类添加门禁权限操作add_permission,或者加减工资操作review_salary等等。
所以,我们其实可以意识到,比如aaa.txt这种文件对象,其实就是文件类的一个实例。我们可以打开它,阅读它,关闭它,是因为python里面定义了对这个类的属性和方法。或者举个更明白的例子: string。我们知道一个字符串"This is" 和另外一个字符串" a dog." 可以用加号+ 加起来变成"This is a dog." 是因为python定义了字符串str这个类,允许对这个类进行加操作。
最直观的方法就是用dir()函数来看一个类的属性和方法。比如要查看字符串类有哪些属性和方法:
我们能看到可以添加新的str可以计算它的长度,拆分字符串等等很多方法。
好了回过头来,这个with语句跟的expression所用到的类(好拗口),必须含有__enter__和__exit__方法才行。(所以,字符串类型看起来就不行咯!!) 类,除了python内置的,当然还可以自己定义咯! 所以除了expression表示一些含有这两种方法的内置类,还可以自己定义类,让他们含有__enter__和__exit__方法。
其实,所谓的方法,看到这里大家应该会有感觉,就像函数调用一样嘛! 是的,没错! 类的方法几乎和普通的函数一样,只有一点区别,那就是类的方法“必须有一个额外的第一个函数名称,但是在调用这个方法的时候并不为这个参数赋值,Python会提供。这个特别的变量指对象本身,按照惯例它的名称是self”---from《简明Python教程》
来个梨子
Hello, how are you?
self就是这么用哒!---->用法就是木有用! 嘻嘻....
可能大家一直奇怪,为什么有的方法,比如上图中的‘split’ ‘title’什么的都没有下划线,而又有很多,,比如__enter__和__exit__要写下划线呢?
其实加下划线这种,是python的一种特殊方法,叫做魔术方法。这些个魔术方法,都是带有下划线的。魔术方法可以看成是建立python这些牛人提供给你的好用的内置方法,这样你自己定义出来的类,就可以和python内置的类,比如字符串、文件等等一样“高端”,而不是傻不拉几的。
我们再举个栗子:
是不是很奇怪?
因此,我们可以看出,方法也是属性,就是可以干什么的属性,属性也是方法。融会贯通!!
关于魔术方法,可以看这个网页:http://pycoders-weekly-chinese.readthedocs.org/en/latest/issue6/a-guide-to-pythons-magic-methods.html那么__enter__和__exit__是怎么用的方法呢?我们直接来看一个栗子好了。
from http://pycoders-weekly-chinese.readthedocs.org/en/latest/issue6/a-guide-to-pythons-magic-methods.html
这个是在定义一个叫做Closer的类。我们来看看这个类里有啥:1. 一个 __init__方法。__init__,大家也看出来了,这是个魔术方法。它的用途是,在这个类的一个对象(实例)被建立时,马上运行。也就是初始化(initiate)的作用。
2. 一个__enter__方法。专门用于with语句的一个魔术方法。它的返回值要直接返回给 with as语句中跟在as后面的variable。
3. 一个__exit__方法。用于with语句的一个魔术方法,我理解放在其他地方也行如果有用的话。当with as语句中with-block被执行或者终止后,这个类对象应该做什么。如果这个码块执行成功,则exception_type, exception_val, trace的输入值都是null。如果码块出错了,就会变成像try/except/finally语句一样,exception_type, exception_val, trace 这三个值系统会分配值。
---------------------------------------------------------
看到这里想必聪明的你肯定知道with as语句大概如何执行了吧?我们来总结一下
(from http://longzhiwen-478.blog.163.com/blog/static/2980974920124151356498/)
的执行过程是
-->首先执行expression里面的__enter__函数,它的返回值会赋给as后面的variable,想让它返回什么就返回什么,只要你知道怎么处理就可以了,如果不写as variable,返回值会被忽略。
-->然后,开始执行with-block中的语句,不论成功失败(比如发生异常、错误,设置sys.exit()),在with-block执行完成后,会执行expression中的__exit__函数。
这个和try finally函数有什么关系呢?其实,这样的过程等价于:
#######
这,就是with as语法的精髓了!! 好了,让我们来找一个不错的李子更好的理解一下吧!! 网上有些李子太高大上,什么封装ip,关闭ftp之类。。。都还没到那步嘛。。。
找到两个不错的李子,来自http://python.42qu.com/11155501
1. 没有报错李子:
--> 调用get_sample()函数,返回Sample()类;
--> 执行Sample类中的__enter__()方法,打印"In__enter_()"字符串,并将字符串“Foo”赋值给as后面的sample变量;
--> 执行with-block码块,即打印"sample: %s"字符串,结果为"sample: Foo"
--> 执行with-block码块结束,返回Sample()类,执行类方法__exit__()。因为在执行with-block码块时并没有错误返回,所以type,value,trace这三个arguments都没有值。直接打印 print "In__exit__()"
结果真是这样吗? 输出结果如下:
In __enter__()
sample: Foo
In __exit__()
看来我们的解释是正确的。
2. 有报错的李子
--> 调用Sample()类,执行类方法__enter__(),返回值self也就是实例自己赋值给sample。即sample是Sample()的一个实例(对象);
-->执行with-block码块: 实例sample调用方法do_something();
-->执行do_something()第一行 bar = 1 / 0,发现ZeroDivisionError,直接结束with-block码块运行
-->执行类方法__exit__(),带入ZeroDivisionError的错误信息值,也就是type, value, trace,并打印他们。
输出结果:
type: <type 'exceptions.ZeroDivisionError'>
value: integer division or modulo by zero
trace: <traceback object at 0x024B8260>
Traceback (most recent call last):
File "ex37_mo03.py", line 15, in <module>
sample.do_something()
File "ex37_mo03.py", line 11, in do_something
bar = 1/0
ZeroDivisionError: integer division or modulo by zero
结果可以看出,确实打印出了需要的内容。并且返回了一个错误。
联系客服