python详解(4)——异常、类、作用域and自定义模块

一、前言

这几天比较懒,没有动力了。最近几天又封家里了,所以也没啥其他玩的,还是逃不过屑代码。

封面中等…感觉还不如不加。

本文紧接着详解(3),对作用域,错误异常,自定义模块,类进行详细的讲解。写作不易,给个支持吧,详解(3)又没流量了。


二、错误异常(中等)

建议学习时长:1小时。

1、try与except开的一家正骨店(中等)

错误异常,大家应该都很熟悉。不过我们要讲的是异常捕捉。

举个例子,如果你做了一个游戏,想让用户输入整数,结果用户有反骨输入其他类型,就会报错,显得特别不美观,还会直接终止程序。于是,用户来到了try和except开的一家正骨店。

try负责接待客人和检查骨头,except负责帮忙正骨。

我们先要知道报的是什么错,比如这个除法运算器,用户就有反骨了:

while 1:
    a,b=map(int,input().split(" "))
    print(a/b)

这个用户他好奇,诶,输入了3 0,想看看3除以0能得到啥。

输出:Traceback (most recent call last):
  File "", line 3, in
ZeroDivisionError: division by zero
第一行的语句不用管他,第二行的line表明了报错的位置,第三行表明了报错的原因。

我们只用看第三行前面的ZeroDivisionError,我们知道报错的类型之后,就用try语句帮用户检查骨头问题,之后用except来正骨。

例如这个刎冥的案例:


输入:3 0

输出:你家0能做除数?我干死你

输入:3 1

输出:3.0

来说格式吧。

try:

    执行代码

except 报错类型:

    报错执行代码

执行代码就是要正常执行的代码(来正骨的语句),报错类型就是报错的第三行前面那一段写上去(except可以正什么骨),报错执行代码就是把报错转换成执行这些代码(正骨步骤)。

异常侦测不用专门放在循环里面,只要try后面的代码出异常就会转换。

还是不理解?这么说吧,try和except就像在隔空喊话:

try:我这边没错误!

except:好的,那我不执行!

try:md这用户脑子有问题吧,填个3除以0,你那边是ZeroDivisionError错误吗?

except:是的,那我执行我这边的代码。

try:这用户怎么填个str类型的啊,你那边是ValueError错误吗?

except:不是诶,这不归我管,你还是报错吧!

try:艹,那我报错了,什么破事。

输入:只因 你太美
输出:Traceback (most recent call last):
  File "", line 2, in
ValueError: invalid literal for int() with base 10: '只因'


2、处理多异常,正骨店开张(简单)

我们可以处理多个异常,把报错类型都逗号分隔,括号括起来形成一个元组。

元组这玩意儿不知名的用法其实很多,在详解(2)没怎么讲,慢慢学吧。

这就相当于一个客人他要正骨的地方太多,所以要都帮他正亿下。

例如:

while 1:
    try:
        a,b=map(int,input().split(" "))
        print(a/b)
    except (ZeroDivisionError,ValueError):
        print("你给我填正常点,qnmd!")

输入:3 0

输出:你给我填正常点,qnmd!

输入:只因 你太美

输出:你给我填正常点,qnmd!

输入:1 2 3 4 5

输出:你给我填正常点,qnmd!

填入的报错就相当于except可以正骨的能力,如果超出他的能力就不能正骨了。

如果不同的错误想要不同的报错,就是正不同地方的骨要不同的方式,可以这样使用。

while 1:
    try:
        a,b=map(int,input().split(" "))
        print(a/b)
    except ZeroDivisionError:
        print("你家0能做除数?我干死你") 
    except ValueError:
        print("你再给我填错一个字试试?")

输入:3 0

输出?你家0能做除数?我干死你

输入:只因你太美

输出:你再给我填错一个字试试?


3、不填报错类型,有骨就正(简单)

我们可以不填报错类型,就会只要报错就执行。属于有骨就正,这算是比较实用的了。

while 1:
    try:
        a,b=map(int,input().split(" "))
        print(a/b)
    except:
        print("错误了md给爷重新填")

输入:只因

输出:错误了md给爷重新填

输入:1 0

输出:错误了md给爷重新填


4、try-else语句,没有正的骨(简单)

我们还可以填else,放在所有except后面。

作用就是:如果没有发生异常执行,就是没有可以正的骨的时候执行。

while 1:
    try:
        a,b=map(int,input().split(" "))
        print(a/b)
    except:
        print("错误了md给爷重新填")
    else:
        print("太感动了居然没报错")

输入:1 0

输出:错误了md给爷重新填

输入:1 1

输出:1.0

太感动了居然没报错


5、try-finally语句,有没有正骨都执行(简单)

我们还可以用try-finally语句,放在所有语句后面。

作用是:有没有异常都会执行,比如正骨店无论有没有要正的骨都会和客人说“欢迎下次光临”。

咱就是说,异常执行,没有异常执行,有没有异常都执行全给安排明白了。

while 1:
    try:
        a,b=map(int,input().split(" "))
        print(a/b)
    except:
        print("错误了!!!")
    finally:
        print("这句话有没有异常都执行")

输入:1 0

输出:错误了!!!

这句话有没有异常都执行

注意一下,如果try用下文的raise抛出了异常而没有任何except语句接收他,会先运行finally的语句,报错。


6、raise抛出异常,知道要正哪儿的骨(中等)

raise是用户自己抛出的异常,还有用户自定义异常涉及class,先不讲。

这就是正骨店的客人自己说自己哪里的骨头有问题,让except直接正骨就行。

格式:raise(报错类型(输出))

举个例子:

while 1:
    a,b=map(int,input().split(" "))
    if b==0:
        raise(ZeroDivisionError("写0写上瘾了是吧?有反骨去正骨去!"))
    else:
        print(a/b)

输入:1 0

输出:Traceback (most recent call last):
  File "", line 4, in
ZeroDivisionError: 写0写上瘾了是吧?有反骨去正骨去!

嗯,十分的文明。

raise还是要搭配try使用,在没有异常的情况下生造异常让except接收。

就这样,我们学习了异常的处理方法,纠正了用户的反骨啊呸,能够正确处理异常了。


三、作用域(中等)

建议学习市场:一坤时(两小时半)。

1、基本定义(简单)

作用域,就是变量可以使用的范围。

比如我们上一篇文章:详解(3)里面讲过全局变量和局部变量,局部变量的作用域就是这个函数,全局变量的作用域就是这个文件。

作用域一共有四种:

内置作用域对应内置变量,英文Built-in,简写为B。

全局作用域对应全局变量,英文Global,简写为G。

闭包作用域对应非局部非全局变量,英文Enclosing,简写为E。

注:在写程序时用nonlocal。

局部作用域对应局部变量,英文Local,简写为L。

他们的关系如下图:

python详解(4)——异常、类、作用域and自定义模块_第1张图片

当我们要调用一个变量时,会按照L-E-G-B的顺序查找又没有这个变量,没有则放弃查找,报错nameError。这就叫做LEGB原则。 


2、闭包作用域(困难)

局部作用域和全局作用域我们已经在上一篇文章讲过了,重点讲一下闭包作用域和内置作用域。

闭包作用域,有人叫他为作用于闭包函数外的函数中的作用域,还有人叫嵌套作用域…还是闭包作用域好叫。

比如我现在定义了两个函数,他们互相嵌套:

def a():
  c=0
  def b():
  print("只因你太美")

这时候,对于b函数来说,变量c既不是全局作用域也不是局部作用域。

这时,a函数叫做外函数,b函数叫做内函数。

接下来我们要了解什么是闭包。不懂?没事,手把手教你!

闭包的形成条件:

1,有两个嵌套的函数。

2,内函数引用了外函数的变量。

3,外函数的返回值为内函数。

注意一下:嵌套函数中无法在全局作用域直接调用内函数。

例如这个代码:

def a():
    c=1
    def b():#达成成就:嵌套函数
        nonlocal c
        c+=1#达成成就:引入外函数变量
        return c
    return b#达成成就:返回值为内函数
d=a()
print(type(d))
print(d())

返回:

2

我们以程序执行的思维去梳理一下。

我们先要知道一点:函数是可以用变量保存的。

 

d=a(),调用a函数,之后在a函数里创建内函数b,返回b函数。

注意:返回b函数时一定别加括号,带参数也别加,这一步不会调用b函数,仅仅是创建。

这时候,d保存了b函数。函数也可以用变量保存,我们用type已经检测了。

因为d目前的值为函数b,调用d就是调用b,完成内函数b的c自增。

 

只要你把上面的话理解清楚,你就学会闭包了。

学习闭包最好的方法就是实例。把内函数硬生生拉出来单独用,作用很大。

作用一:可以在外部自己决定使不使用内函数。

作用二:这时候c就具有记忆功能,如果我们接着print(d()),会输出6,7,8…可见c=5不会一直生效。

作用三:避免了使用全局变量,占内存并且不容易寻找。


3、内置作用域(简单)

内置作用域包含程序提前写好的变量。

我们输入:

import builtins
print(dir(builtins))

就会蹦出来一个包含很多东西的列表,这就是所有的内置变量。

就是一些内置的函数名,没什么讲的。


4、命名空间

①、基本定义与使用

命名空间,大部分语言的难点,学C++的时候大部分原因是因为那个using namespace std;咋也搞不懂才放弃的,还是python简单易懂,人生苦短我用python好吧。

命名空间的英文是namespace。

python的命名空间比较好学,有三种:

1,内置命名空间对应内置变量,英文Built-in namespace。

2,全局命名空间对应全局变量,英文Global namespace。

3,局部命名空间对应局部变量,英文Local namespace。

命名空间的真身是一个字典。键是变量名称,值是变量内容。

我们可以用查看这个命名空间里有什么变量。英文名加上括号返回结果。

a=0
b=1
def c():
  d=2
  e=3
  print(locals())
print(globals(),"\n\n")
c()

输出:

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'a': 0, 'b': 1, 'c':


{'d': 2, 'e': 3}

输出的全局命名空间所有变量中,builtins前面的那些都是系统自带的啥玩意儿不用管,并且可以看见函数也会记录下来。

print(locals())放在哪个函数里面就会输出哪个函数里面的局部变量,再做一个例子:

def a():
  b=0
  def e():
  f=0
  e()
  print(locals())
def c():
  d=0
a()

输出:{'b': 0, 'e': .e at 0x7f49581fee50>}

可以看出,locals只能侦测当前函数的局部变量,不能侦测其他函数的变量、全局变量、嵌套函数内变量。

print(built-ins())可以吗?

输出:Traceback (most recent call last):
File "", line 1, in
NameError: name 'built' is not defined
别想了!

其实,模块相当于是另一个全局命名空间,import导入时要声明他的所属命名空间所以是moudle.xxx,from…import导入时,就相当于把那个全局命名空间的函数直接拉过来,所以不用加,直接饮用。


②、生命周期(干货)

不同的命名空间有不同的生命周期。

内置命名空间,在解释器启动时创建,会一直保留,不会删除。

全局命名空间,在模块导入的时候创建,通常也会一直保留。

内置命名空间,当函数被调用时创建,当return或者异常时作废删除。

概念应该很好理解吧。干货背就是了。


四、自定义模块(困难)

注:以下程序因为作者用的手机编辑不能弄文件没来得及实验,可能会有错误的代码,如有发现请告诉作者,这对我的帮助很大!!!

建议学习时长:1小时

1、自定义模块是什么(中等)

自定义模块,就是自己编辑的模块,和自定义函数差不多。

以下内容,我们把模块称为a文件,引用模块的称为b文件、c文件……。

我们只需要import a,就能把a文件的代码给b去使用。

注意:文件的后缀必须是.py。并且几个文件必须放在一个路径。

其实a文件虽然用了模块的方法,但却不怎么像模块,倒像 …共用?

接下来,就是双线并进的烧脑时刻了。

我们可以用import去导入其他文件的模块。

a文件内代码:

print("只因你太美")

b文件内代码:

import a

b文件输出:只因你太美

当导入一个自定义模块的时候,运行之后,这个模块里面所有的语句都会被运行。

当导入这个模块的时候,会执行三件事:

1,创建以这个模块名命名的命名空间。

2,执行这个模块里面的代码。

3,引用该模块的函数、变量、类。类后面会学。

只要理清楚这三个步骤,你学的就差不多了。

诶,但是如果我不想在另一个文件运行这些语句呢?

用__name__=='__main__'。可以说,包括我们后面要学的__init__,只要名字两遍有下划线的都是高大上的很难学的(bushi。

a文件内代码:

if __name__=='__main__':
    print("小黑子卤出鸡脚了吧")
print("小黑子现实生活中一定挺紫杯吧")

b文件内代码:

import a

a文件输出:

小黑子卤出鸡脚了吧

小黑子现实生活中一定挺紫杯吧

b文件输出:

小黑子现实生活中一定挺紫杯吧

 

你干嘛~~~~哈哈~~~诶呦~


2、引用声明原模块(困难)

上一篇文章已经说过了,引用一个模块的函数必须要加所属,例如math.log,math.e,但是如果用from语句引入单独的函数就不用加所属,直接用就行。

自定义模块也是这样的,因为会有两个不同的命名空间。

多说无益,还是先举几个例子:

a文件内代码:

 

c=0

def b():

    print("只因你太美")

b文件内代码:

import a
a.c=1
print(a.c)
a.b()

c文件内代码:

from a import c,b
c=1
print(c)
b()

b文件输出:1

只因你太美

c文件输出:1

只因你太美

可以看出,如果用from导入就不用加所属了,直接用就行。

自定义模块其实挺简单的,难点就是两个命名空间的变量函数类容易搞混,建议先把作用域学好,剩下的就要靠你自己去掉坑里再填坑了。


五、类(超难)

建议学习时长:专心下来,学一天,彻底研究透。

1、基本定义——一家汉堡店(中等)

虽然我给类标的是超难,但是你只要用自己独特的方法理解,就简单一点了。

我们要用class关键字去创造一个类。

不知道大家小时候有没有玩过一个老爹汉堡店的游戏,一个4399的游戏,之前一玩就是一个下午。

游戏规则:根据客户不同的需要,做出不同的汉堡给客人。

类,就是这么一个汉堡店,但是店名不是老爹汉堡店,而是这个类的名称:

class ikunhanbaodian:
    pass

这时候,我们就开了一家ikun汉堡店。现在,你要为汉堡店写一个机器人程序来帮助你劳动,我们开始随着汉堡店一步步开张吧——

举个例子:type是用来检测一个变量的类的。

class bu_shi_ikun_lei:
    pass
xiao_hei_zi=bu_shi_ikun_lei()
print(type(xiao_hei_zi))

输出:

这时候,变量xiaoheizi就是不是ikun类的一员了。我们把小黑子添加到了不是爱坤类。

格式:变量名=类名称()

这叫做创建类的实例。注意创建实例的时候要加括号。

 

很难理解?那么换成汉堡店的思维。

class ikunhanbaodian:
    pass
keren1=ikunhanbaodian()
print(type(keren1))

首先,我们开了一家ikun汉堡店(class ikunhanbaodian:),还没有开张里面什么都没有(pass),这时候来了一个客人进了汉堡店(keren1=ikunhanbaodian()),这时候我们用type检测客人(print(type(keren1))),输出就说明客人现在在这个汉堡店。

创建实例就相当于一个客人进了汉堡店。

这是不是清晰多了?


2、__init__与self,汉堡店开张(困难)

我们可以写一个_init_方法。

每当创建一个实例的时候,都会自动执行这个类里的init方法。

这叫做构造函数。

要有init的原因是,比如汉堡店现在开张了,来了一个客人,机器人需要欢迎他进来,跟他说菜谱,之后准备好汉堡的面包和肉。

举个例子:

class ikunhanbaodian:
    def __init__(self):
        print("欢迎光临,汉堡店刚刚开张目前什么也没有")
        #准备面包和肉的代码
niganma=xiaoheizi()

输出:欢迎光临,汉堡店刚刚开张目前什么也没有

客人一踏进这家汉堡店,就欢迎他光临,之后准备好面包的肉,嗯,合理。

这里面,self的意思是自己,不知道是不是闲的慌,类里面的函数必须至少有一个参数,所以才有了self。self可以替换成其他的,不过这是程序员之间约定俗成的用self,否则你会收获其他大神的鄙视。

反正你记住:__init__一旦见到他,后面的参数的第一个永远是self,否则那你可以大声说出来:

“老师,他用__init__居然不用self!”

我们尝试获取一下self保存的是什么。

class ikunhanbaodian:
    def __init__(self):
        print(self)
keren1=ikunhanbaodian()

输出:<__main__.xiaoheizi object at 0x7fe462b404c0>

如果不知道这玩意儿是什么意思,那你就记住:

self永远保存的是实例本身。

实例本身是什么不用知道,记住就是了。


3、类的传入,客人有话说(中等)

和函数一样,类也是可以传入变量的,相当于客人进门之后会先点餐。

这里简单写个复读客人的话的程序:

class ikunhanbaodian:

    def __init__(self,yuyan):

        print(yuyan)

keren1=ikunhanbaodian("老板给我来个汉堡!")

输出:老板给我来个汉堡!

是不是很简单?

self它会自动传入,当他是个废物就行。


4、创建实例方法and访问(困难)

我们可以创建实例方法,说大白话就是在类里面定义一个函数。

为什么要有实例方法?例如汉堡店虽然有汉堡模版,但不确定客人具体要牛肉汉堡还是田园汉堡,不知道放什么料,所以要根据客户的不同的要求去放料。

格式:

def 名称(self[,其他参数])

    方法体

如果想要调用函数就这样:

类名.函数名(self[,其他参数])

我们尝试看懂这一个例子:

class ikunhanbaodian:
    def __init__(self):
        print("欢迎光临!请说出您想要什么汉堡?")
        #准备肉和面包的代码
    def zhizuo(self,a):
        print("客人想要一个",huayu,"!")
        print("您的汉堡!给个五星好评!")
keren1=ikunhanbaodian()
huayu=input()
keren1.zhizuo(huayu)

输出:欢迎光临:请说出你想要什么代码?

输入:牛肉汉堡

输出:客人想要一个 牛肉汉堡 !

输出:您的汉堡!给个五星好评!

如果你看懂了下面的思路,那么你就学会了实例方法这一大难题。

第一行:开一家ikun汉堡店。

第二行:当客人进来后运行的代码。

第三行:输出欢迎光临并询问想要什么汉堡。

第四行:准备好肉和面包。

第五行:获取到客人的需求后的代码。

第六行:跟厨房那边说客人的需求。

第七行:求好评。

第八行:来了一个客人:keren1并且执行当客人进来之后运行的代码。

第九行:input询问客人需求并用huayu变量保存

第十行:调用获取到客人的需求后的代码。


5、stop,复习!(干货)

让我们中止一下学习。

请问,类的英文是什么?创建实例方法怎么写?def __init__()叫做什么?self是干什么用的?实例方法是什么?怎么调用实例方法?

回答不上来的就是没认真看,面壁思过去(bushi。

类的学习有很多新词和语法,所以需要及时巩固,回答不上来的往上翻翻,上面都有答案。

为什么要停下来复习呢?

类是一个从入门到放弃的关键点,成功了,你就摆脱了基本语法,可以向外发展了;如果失败了……

最简单的python都学不懂,给爷学易语言去(bushi。

我们接下来会学:

私有属性、公有属性、类属性、内置属性、实例属性、静态方法,类方法,类变量,实例变量。

放在下一篇文章吧。感觉牛顿出来也学不动那么多。


六、回顾与复习

接下来,给大家一分钟时间,回顾一下这篇文章的知识点。
 
开始提问:
 

1、简答题

1,异常捕获用什么语句?(5分)
 
2,说出3种处理多个异常的方式。(10分)
 
3,说出四种作用域和三种命名空间。(10分)
 
4,说出闭包形成的三要素。(10分)
 
5,判断:无论什么时候引用另一个模块的变量和函数必须要加所属。(5分)
 
6,类里面的函数叫做什么?__init__方法叫做什么?(10分)
 

2、代码题

写一个ikun汉堡店的服务机器人程序:
 
第一步:新建一个类,命名ikunhanbaodian(10分)
 
第二步:进来一个客人及欢迎的程序(10分)
 
第三步:询问对方要吃什么的程序,目前有三种选择:普通汉堡,牛肉汉堡,田园汉堡的程序(5分)
 
第四步:询问对方有没有备注的程序(5分)
 
第五步:输出客人要点的餐和备注(10分)
 
第六步:如果客人反馈吃完之后输出“请老板结账”并且欢迎下次光临的程序(10分)
 

答案:

1,try-except语句

2,except分支,处理多异常,不加异常名

3,内置、全局、闭包、局部作用域,内置、全局、局部命名空间

4,嵌套函数,内函数引用外函数变量,外函数返回值为内函数

5,x,用from…import语句就不用加所属

6,创建实例方法,构造函数

代码题:答案不唯一(绝对不是作者懒得写),根据步骤,完成一个步骤给相应分数

分数低于60分的,重新再看一遍去!

七、尾声

别问我两篇文章为什么写这么快,问就是提前写好的想凑到下一个月攒数量。

另外就是类确实有点难讲,要有时间揣摩(bushi。

有时间可以给详解(1)前面再加一个下载python和基础语法讲解,又可以水一篇耶耶耶。

散会!

--------------------------------------------------------------end----------------------------------------------------------

 

你可能感兴趣的:(详解系列,python,开发语言)