这几天比较懒,没有动力了。最近几天又封家里了,所以也没啥其他玩的,还是逃不过屑代码。
封面中等…感觉还不如不加。
本文紧接着详解(3),对作用域,错误异常,自定义模块,类进行详细的讲解。写作不易,给个支持吧,详解(3)又没流量了。
建议学习时长:1小时。
错误异常,大家应该都很熟悉。不过我们要讲的是异常捕捉。
举个例子,如果你做了一个游戏,想让用户输入整数,结果用户有反骨输入其他类型,就会报错,显得特别不美观,还会直接终止程序。于是,用户来到了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 "
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 "
ValueError: invalid literal for int() with base 10: '只因'
我们可以处理多个异常,把报错类型都逗号分隔,括号括起来形成一个元组。
元组这玩意儿不知名的用法其实很多,在详解(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能做除数?我干死你
输入:只因你太美
输出:你再给我填错一个字试试?
我们可以不填报错类型,就会只要报错就执行。属于有骨就正,这算是比较实用的了。
while 1:
try:
a,b=map(int,input().split(" "))
print(a/b)
except:
print("错误了md给爷重新填")
输入:只因
输出:错误了md给爷重新填
输入:1 0
输出:错误了md给爷重新填
我们还可以填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
太感动了居然没报错
我们还可以用try-finally语句,放在所有语句后面。
作用是:有没有异常都会执行,比如正骨店无论有没有要正的骨都会和客人说“欢迎下次光临”。
咱就是说,异常执行,没有异常执行,有没有异常都执行全给安排明白了。
while 1:
try:
a,b=map(int,input().split(" "))
print(a/b)
except:
print("错误了!!!")
finally:
print("这句话有没有异常都执行")
输入:1 0
输出:错误了!!!
这句话有没有异常都执行
注意一下,如果try用下文的raise抛出了异常而没有任何except语句接收他,会先运行finally的语句,报错。
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 "
ZeroDivisionError: 写0写上瘾了是吧?有反骨去正骨去!
嗯,十分的文明。
raise还是要搭配try使用,在没有异常的情况下生造异常让except接收。
就这样,我们学习了异常的处理方法,
纠正了用户的反骨啊呸,能够正确处理异常了。
建议学习市场:一坤时(两小时半)。
作用域,就是变量可以使用的范围。
比如我们上一篇文章:详解(3)里面讲过全局变量和局部变量,局部变量的作用域就是这个函数,全局变量的作用域就是这个文件。
作用域一共有四种:
内置作用域对应内置变量,英文Built-in,简写为B。
全局作用域对应全局变量,英文Global,简写为G。
闭包作用域对应非局部非全局变量,英文Enclosing,简写为E。
注:在写程序时用nonlocal。
局部作用域对应局部变量,英文Local,简写为L。
他们的关系如下图:
当我们要调用一个变量时,会按照L-E-G-B的顺序查找又没有这个变量,没有则放弃查找,报错nameError。这就叫做LEGB原则。
局部作用域和全局作用域我们已经在上一篇文章讲过了,重点讲一下闭包作用域和内置作用域。
闭包作用域,有人叫他为作用于闭包函数外的函数中的作用域,还有人叫嵌套作用域…还是闭包作用域好叫。
比如我现在定义了两个函数,他们互相嵌套:
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不会一直生效。
作用三:避免了使用全局变量,占内存并且不容易寻找。
内置作用域包含程序提前写好的变量。
我们输入:
import builtins
print(dir(builtins))
就会蹦出来一个包含很多东西的列表,这就是所有的内置变量。
就是一些内置的函数名,没什么讲的。
命名空间,大部分语言的难点,学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__':
{'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':
可以看出,locals只能侦测当前函数的局部变量,不能侦测其他函数的变量、全局变量、嵌套函数内变量。
print(built-ins())可以吗?
输出:Traceback (most recent call last):
File "
NameError: name 'built' is not defined
别想了!
其实,模块相当于是另一个全局命名空间,import导入时要声明他的所属命名空间所以是moudle.xxx,from…import导入时,就相当于把那个全局命名空间的函数直接拉过来,所以不用加,直接饮用。
不同的命名空间有不同的生命周期。
内置命名空间,在解释器启动时创建,会一直保留,不会删除。
全局命名空间,在模块导入的时候创建,通常也会一直保留。
内置命名空间,当函数被调用时创建,当return或者异常时作废删除。
概念应该很好理解吧。干货背就是了。
注:以下程序因为作者用的手机编辑不能弄文件没来得及实验,可能会有错误的代码,如有发现请告诉作者,这对我的帮助很大!!!
建议学习时长: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文件输出:
小黑子现实生活中一定挺紫杯吧
你干嘛~~~~哈哈~~~诶呦~
上一篇文章已经说过了,引用一个模块的函数必须要加所属,例如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导入就不用加所属了,直接用就行。
自定义模块其实挺简单的,难点就是两个命名空间的变量函数类容易搞混,建议先把作用域学好,剩下的就要靠你自己去掉坑里再填坑了。
建议学习时长:专心下来,学一天,彻底研究透。
虽然我给类标的是超难,但是你只要用自己独特的方法理解,就简单一点了。
我们要用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))),输出
创建实例就相当于一个客人进了汉堡店。
这是不是清晰多了?
我们可以写一个_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永远保存的是实例本身。
实例本身是什么不用知道,记住就是了。
和函数一样,类也是可以传入变量的,相当于客人进门之后会先点餐。
这里简单写个复读客人的话的程序:
class ikunhanbaodian:
def __init__(self,yuyan):
print(yuyan)
keren1=ikunhanbaodian("老板给我来个汉堡!")
输出:老板给我来个汉堡!
是不是很简单?
self它会自动传入,当他是个废物就行。
我们可以创建实例方法,说大白话就是在类里面定义一个函数。
为什么要有实例方法?例如汉堡店虽然有汉堡模版,但不确定客人具体要牛肉汉堡还是田园汉堡,不知道放什么料,所以要根据客户的不同的要求去放料。
格式:
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变量保存
第十行:调用获取到客人的需求后的代码。
让我们中止一下学习。
请问,类的英文是什么?创建实例方法怎么写?def __init__()叫做什么?self是干什么用的?实例方法是什么?怎么调用实例方法?
回答不上来的就是没认真看,面壁思过去(bushi。
类的学习有很多新词和语法,所以需要及时巩固,回答不上来的往上翻翻,上面都有答案。
为什么要停下来复习呢?
类是一个从入门到放弃的关键点,成功了,你就摆脱了基本语法,可以向外发展了;如果失败了……
最简单的python都学不懂,给爷学易语言去(bushi。
我们接下来会学:
私有属性、公有属性、类属性、内置属性、实例属性、静态方法,类方法,类变量,实例变量。
放在下一篇文章吧。感觉牛顿出来也学不动那么多。
1,try-except语句
2,except分支,处理多异常,不加异常名
3,内置、全局、闭包、局部作用域,内置、全局、局部命名空间
4,嵌套函数,内函数引用外函数变量,外函数返回值为内函数
5,x,用from…import语句就不用加所属
6,创建实例方法,构造函数
代码题:答案不唯一(绝对不是作者懒得写),根据步骤,完成一个步骤给相应分数
分数低于60分的,重新再看一遍去!
别问我两篇文章为什么写这么快,问就是提前写好的想凑到下一个月攒数量。
另外就是类确实有点难讲,要有时间揣摩(bushi。
有时间可以给详解(1)前面再加一个下载python和基础语法讲解,又可以水一篇耶耶耶。
散会!
--------------------------------------------------------------end----------------------------------------------------------