如果图片出不来请去https://www.bilibili/read/cv261407,都是我写的




上一讲少给了一张图,用walk代替递归实现题目1,给出来

这一讲我们先来学习pickle模块

pickle模块的必要性(这段可以跳过)

pickle就是泡菜的意思,之所以有这个模块,首先我们来当然是想一个问题,假如你现在在a

列表了存了四个水仙花数,你想把它们存进硬盘,要求不能修改a(这要求也很正常),以备以后用,假如我们现在就想求和(笔者这里举的例子元素量很小,现实中可能会有很多个元素,还有可能是元组,字典,集合等存储了一些数字的对象,它们都差不多,这以列表为例,而且不要说笔者有病,非要把a存进去再读出来做加和,嗯,对于这个问题,我只能说有的时候你是会用到这种情况,比如说你要给很远的总公司的你不知道任何联系方式的只能通过公司的系统才能联系到的会计发一串数字过去,他要对这些数字做一些你不知道的比加和复杂的多的操作,这里做加和也只是一个例子),你该如何做?我下面来试一试看官老爷们可能会使用的方法。



存好像没有问题唉,倒是读取好像有些问题。这是因为普通的的文件或者写文件都是写进去字符串,读出来的还是字符串对于int和float这种类型只能想办法先转化为字符串再存进去,但是我们看到读的时候出了问题,会计就不给你发工资了哦,因为他读不出来数据。你可能会说这有什么读不出来的



也许是笔者水平有限,一顿操作也才弄成这样,而且还是你知道这是个列表和里面是什么,有可能对方是不知道你的数据装在什么类型里(所以对方不知道该去掉什么东西,我们上面是知道要去掉中括号,因为我们知道它是列表),他只是知道你发了一些数据而已。也许你会这样

虽然是字符串了,但是你这写进文件谁能看出来原来是什么。注意对方只知道你要发数据过去,不知道数据的个数,大小,你说这怎么可能?Nothing is imposible.世界之大无奇不有,你怎么知道不会遇到这种情况,或许你说,你有更好的办法来做这件事,是的,也许是有的,但是一定会有一顿操作。pickle很好解决了不全是字符串类型对象的存储和从文件中调用问题。

上面就是pickle模块引入的必要性,也许没能说服到你,那么笔者在这里对自己能力不足感到很抱歉。

pickle模块正文

使用pickle模块有几个要点:

  1. 首先一定要先import pickle,这个笔者上一讲也强调过

  2. 打开文件的格式一定要是b模式,读要用rb模式,写要用wb模式

  3. 写要用dunp函数,读要用load函数

下面就对上面几个要点进行尝试(1就不试了)

先来看dump和load函数

dump第一个参数是对象,第二个是文件名,一般只用这两个,返回值情况目前不知道

load第1个参数是文件名,返回一个文件里存储的‘泡菜’数据的对象。

下面先来一波正确示范

dump返回值是None

我们看到这个白点一直在跳动,似乎不能保存为.exe但是我们可以修改后缀啊

把a的扩展名改为.txt就会看到乱码,很正常,因为它是以b二进制文件保存的

下面读这个文件的内容

load返回的就是原来的列表

我们就可以sum了需要注意的是如果你接下来又load一次

关了再打开是可以再load一次的

但是其实本质还是文件指针的问题

可能会出现的错误

得出dump一次只能对一对对象和文件进行操作。

我在E盘新建了一个txt并写入


l看出oad一次只能加载一个文件

看出必须是'wb'的写模式,不然会报错,这种异常我们下面可能会涉及到,看起来前面那个参数已经是字符串了它还是说must be str必须是字符串类型,看来是后面有问题咯,或者就是前面没有加b导致的问题。

我们再看读的问题

上面两个错误都和buffer interface缓冲区接口有关系。下面可能也会介绍,没介绍到还请大佬在评论区指点。

下面来看一道练习

pickle模块练习

0这次使用pickle把前面的test0.txt按照前面第七讲的要求编程实现。





pickle模块练习的答案


看到写进去的是二进制我们来试着读它们

可以看到用pickle是可以读的。下面是虽然用的是二进制打开,但是没有用pickle.load去读,读出来的仅仅是一串十六进制码。

异常处理

异常(exception)。python里有这么多的函数,每个函数都有自己固定的参数个数还有每个位置的参数代表什么意义都是有格式的,再牛的程序员也不可能会牢记所有的,只能对自己常用的比较熟悉,在敲代码时,你很可能就会遗忘了某一个语法格式点,导致出现异常,这很正常,也不必担心,异常出现了我们可以改嘛。怎么改呢?我们说知己知彼,才能百战不殆,如果你并不知道这个异常的意思,在那里瞎改,可能一次两次是可以把代码改ok的,但是是不会屡试不爽(屡次试验都没有差错。爽:差错的意思。)的,而且如果一段代码很长,即使python提醒了你哪一行有错,如果你哪一行也有很长,用了很多函数,你应该去改哪一个?瞎改可能就是大海捞针。所以学习异常处理很有必要,看懂异常的类型,清楚异常为什么会发生,才能对症下药,药到病除,而且屡试不爽。

python标准异常总结





以上图片内容来自来自http://bbs.fishc/thread-45814-1-1.html

常见异常类型示例

还记得assert 吗?它是用来判断一个条件是不是为真,不为真会报错,通常有些重要代码某些条件必须为真才能运行时,会使用assert断言,条件为假马上报错。异常类型是assertionerror


attibuterror是说尝试访问对象的未知属性,也就是说python里是没有你写的这个内置函数或者方法的

列表的倒置方法是reverse不是reversed,reversed是一个单独的函数。

注意python列表,元组,字符串这些序列的索引都是从0开始的,和MATLAB区别开。indexerror就是索引出错。

字典里如果没有这个关键字会报错,异常类型就是keyerror,如果你做一个软件,经常报红,用户体验就极差,我们可以用get方法,它不会报红。它返回一个Nonetype


keyboardinterrupt会在程序运行时,你按Ctrl+C时出现

中间一片红,这个递归要算很久,前面也有讲过

如果你尝试使用了一个不存在的变量,会出现nameerror

上面第一个错误是属于nameerror这个类里的一个异常类型,你们不信自己上去看图,第一种错误是在内部函数里未出现x的定义就直接使用时,python自动屏蔽掉了外部函数的x。可以用nonlocal或者容器变量比如列表来解决,前面讲函数的时候讲过,不再讲不清楚的请自己去看。

Syntaxerror,是从形式结构上来看就是错的,不符合python3语法规定。比如说用了中文标点,列表形式不对等

Syntaxerror后面都会有具体错误信息,比如第一个就是不合法的字符,print后面不加小括号是python2的格式,python3和2语法不兼容,我们看到它说少了parentheses括号。

typeerror是类型错误

类型不一样不可以相加,普通的写入文件必须是字符串类型,你写入int就会报typeerror

除数不能为0我们小学都知道,zerodivisionerror

同时我们看到python异常好像也有短路逻辑,我们看到e/0只报了一个nameerror,猜测python异常检查是从左向右依次检查,如果有错误,直接报错,不再向右检查,也就是说第一个错误右边的错误都没有被检查到,因为它还没有被检查到,python就已经停止检查了。

OSError

文件不存在FileNotFoundError是OSError里的一种类型

如果还有不懂得可以到http://www.runoob/python/python-exceptions.html去看看

上面只是讲了异常类型。上面是在程序员层面,也就是你让代码按照你的想法可以运行,不会被编译器检查出错误。下面介绍异常处理方法,这是你设计一个软件需要考虑的东西,是从用户层面考虑的,上面是保证了代码按照你的输入的想法不会报错,但是你的用户都是很皮的,他们不一定会输入你想要的内容,这个时候如果报错,红的一片,就会严重影响用户体验,而且用户也不一定懂错误是什么,因为爆出来一堆英文。事实上你也不能说他的输入是错的,比如说你数一个文件名,你知道要输扩展名,他可能就不知道,然后就会报错,但是他认为他的输入是没错的,所以他会认为你这个软件有问题。一般来说你用一个东西,爆了红你会认为是软件的问题,而如果没有爆红,而是出现了错误的提示信息,你会觉得自己的输入有问题,是不是这种感觉?感觉好像是程序员在甩锅一样233。下面就是教你如何告诉你的用户他们的想法哪里有问题,在不报红的前提下告诉他们应该怎么输入。

try-except-finally语句处理异常

语法格式

感觉这东西和软中断中断处理挺像的,异常就类比为中断,那么异常处理就是中断处理。先来说不带finally的

举个例子


我们看到把错误信息打出来了,图中的liyou可以识别的单词,但是打印的时候必须要加str。

有的时候错误不止一种,这个时候我们可以用多个except,下面举的例子不是很合适,因为本身就是不可能会这么写啊,只是为了讲多个except编出来的一个例子

图1


如果换为

图2


再换为

图3


继续换

图4



我们先看图3图4吧,对比一下,只有try里面的语句顺序反了一下

图3

图4

结果是不一样的,这是因为python如果遇到异常立刻会进入到对应的except去处理,try后面的语句相当于被短路了一样,python根本不去执行它们,即使里面有错也不管。所以图3的结果是进入了第二个excep,图4的结果是进入了第一个except。

上面是try里面语句先后对结果的影响,下面是except的先后对结果的影响

首先要说明FileNotFound是OSError的一个子类,你自己可以去上面看看。

图1和图2都是

这个语句顺序,这个C:/a我电脑上是没有的,不同的是图1


图2

我们看到对于都是文件不存在的错误(也属于OS的错误),也就是说,OSError和FileNotFound对应的处理语句都可以进去,这时候怎么办呢?就应该先来后到,哪个异常处理写在前,就进去哪个except。图2和图3对比,图3中

尽管TypeError的except写在所有异常处理except的最前面,但是b=1+'1'是在try里的最后一条语句,程序不会进入TypeError的except异常处理里。说明要先看try里的语句先后顺序,如果有错误,再看错误对应的except语句的先后顺序进行异常处理,这就是异常处理的过程。

我们还可以统一处理

这些参数都必须用元组装进去

显示类型错误,捕捉的类型不能从BaseException里继承(对象继承的问题后面的文章会讲到)。改为元组就可以了

但是异常类型是有很多的,你有可能会考虑不周全,这时候就会爆红,影响用户体验。

这时候怎么办?可以直接不写错误类型

但是不推荐这种做法,因为它是不能让程序员知道报错的类型的,也不知道让用户如何改正自己的输入,用户就像雾里看花,根本就是摸不着头脑,最后试了很多次,也还会报错。这是把程序员和用户都蒙在鼓里,不建议使用。

还有些情况,比如下面这种情况,由于a=1+'1'这个语句出错,f.close()没有被执行,内容只是写在python的缓冲区里,没有写到硬盘里面,如果此时突然断电或者程序崩溃,就可能会导致损失,我这个写进去的内容也不重要,但是可能有时候写的内容很重要,还很多,一断电就很让人崩溃。



这时我们用到finally




就写进去了。

但是这个finally的里面语句必须执行的特性有时候会带来一些问题,假如有下面的情况


报NameError是因为上面open右边有错误,直接进入except处理异常,a这个赋值行为没有发生,所以a是没有定义的。我现在就必须要求finally的语句必须还在finally里,你不能把里面的语句移到try里,来改程序。

此时

没有报错

我们来打开一个存在的文件


说明a.close()被执行了。

我们来看看locals()是个什么东西,它是一个当前变量对象符号表

我们看到当前的所有对象都在表里,增加一个a=1语句,a就出现在locals()返回的字典的key里,打开一个文件,文件名也出现在字典的key里

但是下面去掉finally也行



没有问题


目前看来finally完全可以不要,因为后面的代码都会执行,有了异常处理,程序不会停止,会继续向下运行,如果下面的程序也没有异常,就可以运行完,但是通常来说上面的代码是会影响下面的代码,即使上面用异常处理躲过一截,但是下面还是会爆红,正所谓,躲得过初一躲不过十五。对比下面不带异常处理b的变化。


我们还可以自己引发异常,用raise关键字,原来的代码是没有出错的,你是认为引发的异常,异常类型可以你自己定,因为事实上并没有发生这个异常,只是程序发生了你想让程序直接跳到某行代码执行的事件,比如下面会有一个例子,i==2就是这个事件,异常类型KeyboardInterrupt是你自己定的,只要和下面except后面的异常类型配套就行。总的来说,这就是一种'弄虚作假'的异常类型,别人你的代码如果不了解的话,很可能会被except后面的异常类型迷惑,会有这样的疑问:这个异常类型明明没有发生,怎么跳到异常处理里去了?

我们看到可以加上你想要的异常类型和想要显示的内容,这个内容不局限于字符串。但是异常类型只能给一种,也很好理解,给两种它根本不知道选哪一种,信息太少,因为这种异常是你认为引发的,不是实实在在发生的,没有更多的信息,你比如说,实实在在发生了类型错误,python会自己判断是类别错误,而raise就像灌输,你给python什么异常它就必须显示什么类型的异常。这有什么用呢?看下面一个例子

补齐上面模糊的地方让代码按照要求输出

或许你说我实现这个很简单啊

是的,其实结果都是一样的,但是可能有些情况就必须要用自己认为的去raise,世界之大无奇不有,将来工作或许老板对你的代码会有各种各样奇怪的要求,储存这些知识都是必要的。

异常处理还是很有用的比如我们上面猜数的游,这个猜数以前是不完整的因为可能会有人输入下面的内容,那么经过int就会报错,那么用户体验就极差。知道了异常的类型,我们就可以用异常处理来编程


笔者只编了猜一次的程序,如果你是从前面看过来的,你会知道 前面有过可以猜三次的程序代码 。这个代码就比之前好很多,它可以保证你的输入一定要正确才能进行比较(以前是没有考虑输入各种奇葩情况,比如字母和特殊字符的情况。)

你输入的各种不符合要求的形式都会提示你输入错误。如果上面的对于字母和各种特殊字符还有‘1’这样的ValueError异常处理我认为还是比较简单的,如果用别的方法实现,笔者感觉会比较麻烦,异常处理还是很有用的,当然使用异常处理的前提是你要知道异常类型,才能在except后面对症下药。当然上面代码还有问题


按Ctrl+C

按Ctrl+D

针对这两个问题修改一下就行了

再按Ctrl+C或者Ctrl+D就不会报红

你认为没有人会输这么奇葩的Ctrl+C或者Ctrl+D,这就错了,你必须要把用户想象成皮的不行的熊孩子去编程才能让你的代码可以应付很多突发情况,提高用户体验。

这样,我们一步一步把猜数字游戏的用户体验提高了。

最后补充一点,如果你没有关闭某个文件,也就是没有close,这个文件是没有办法被删除的。


只有close了才能删除




更多推荐

手把手教你学python第九讲(pickle模块和异常处理)