生成器装饰器和异常
一、 生成器
思考一:上节课协议中介绍了迭代器协议,但是需要定义类,那么只用函数可以生成吗?
- 生成器
def fibonacci(end):
n,a,b = 0,0,1
while n < end:
a,b = b,a + b
yield b
n += 1
利用yield可以将一个函数变成一个迭代器
yield具有和return一样的功能,可以返回值,同时也会暂停函数的执行,知道下一次next执行,才会继续往下运行
n | a | b | 执行 |
---|---|---|---|
0 | 0 | 1 | a,b=b,a+b;yield b |
0 | 1 | 1 | n+=1;n<end;a,b=b,a+b;yield b |
1 | 1 | 2 | n+=1;n<end;a,b=b,a+b;yield b |
2 | 2 | 3 | n+=1;n<end;a,b=b,a+b;yield b |
yield会停下来,直到下一次next调用
二、装饰器
思考一:刚才一个小小的改动就让函数变得不一样了,但终究改变了函数,能不能不改变函数,给一个函数增加功能呢?
思考二:比如别人调用了我定义的函数,但是我想知道别人传入了什么参数该怎么办呢?
- 装饰器
def variables(func):
def f(*args,**kwargs):
print(args,kwargs)
values = func(*args,**kwargs)
return values
return f
@variables
def f1(end):
return end + 1
对于函数f1,不需要做任何改变,但是每次在调用的时候,都能看到传入的变量值具体是多少
- 装饰器本质
装饰器本质也是利用了闭包将f1传入variables然后把f1重新引用为variables的返回值 - 内置装饰器
class Person:
def __init__(self,name,age,sex = '男'):
self.name = name
self.age = age
self.sex = sex
@property # 调用方法可以像调用属性一样
def play(self):
print('%s 正在玩游戏,啊哈哈---' % self.name)
@classmethod # 第一个参数自动传入类
def learn(cls):
print('%s 需要学习,emm---' % cls)
@staticmethod # 不再自动传入self或cls
def sleep():
print('人都要睡觉')
property、classmethod、staticmethod是pyhton内置的三个装饰器,在项目中会使用到
- 总结
1、装饰器
掌握:装饰器即闭包,只是传入的是一个函数
2、内置装饰器
掌握:三个内置装饰器的使用和效果
三、异常
思考一:前面的学习过程中,始终小心翼翼,尽可能避免程序出现报错,但是程序始终是由可能报错的,那该怎么办呢?
思考二:比如打开一个只读文件,这个文件可能还没有获取到,那么打开的时候就会报错,那么应该怎么办呢?
- 异常
try:
f = open('test.txt')
except:
print('文件不存在')
当文件不存在时,程序不会报错,这样报错就不会导致程序结束
- 异常的使用
语法规则:
try:
pass
except Exception:
raise Exception
else:
pass
finally:
pass
使用规则:
try:
可能出现异常的代码
except Exception:
抛出或者返回异常
else:
没有报错执行
finally:
以上执行完执行
注意事项:
try后面必须跟上except
except只有在函数中才能使用return
finally不管是否发生异常,始终都会执行
raise可以主动抛出异常
- 断言使用
assert是断言,和if判断类似,只是判断为False的时候会报错
- 总结
1、异常
掌握:异常就是报错
2、异常处理
掌握:异常处理的基本语法规则
3、断言
掌握:断言的使用
四、错误查找
思考一:错误和bug是不可避免的,那么如何快速查找和定位到错误是非常关键的,那么在Python中如何去查找错误呢?
- 错误查找
在今后解决问题的过程中,应当自己找到错误代码行,报错原因及报错行,然后再去查找问题。
在自己无法解决需要寻求帮助时,也要掌握如何描述问题,把问题描述清楚的能力,掌握如何提问的技巧,这会大大节省双方的时间和精力
五、本节课总结
1、生成器
掌握:yield的使用
2、装饰器
掌握:装饰器的定义和使用
3、异常
掌握:异常的使用
4、错误查找
掌握:错误查找的方法
六、上节课作业
1、测试列表推导和不用列表推导那一种速度更快
import time
class RunTime:
def __enter__(self):
self.start_time = time.time()
return self.start_time
def __exit__(self, exc_type, exc_val, exc_tb):
self.end_time = time.time()
self.run_time = self.end_time - self.start_time
print('Time consuming %s' % self.run_time)
with RunTime():
li = []
for i in range(10000000):
li.append(i)
with RunTime():
li = [i for i in range(10000000)]
2、range不可以使用小数做步长,实现一个可迭代对象,可以实现小数步长
class Float_range:
def __init__(self,start,end = None,step = 1):
if not isinstance(start,(int,float)):
raise TypeError
elif end != None and not isinstance(end,(int,float)):
raise TypeError
elif not isinstance(end,(int,float)):
raise TypeError
elif end and end < start:
print('开始值不能小于结束值')
elif step < 0:
print('步长不能小于0')
else:
if end == None:
self.start = 0
self.end = start
self.step = step
else:
self.start = start
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
import decimal
res = self.start
if float(self.start) < float(self.end):
self.start = decimal.Decimal(str(self.start)) + decimal.Decimal(str(self.step))
return res
else:
raise StopIteration
七、作业
1、利用装饰器,记录函数的运行次数
2、打开一个只读文件,如果文件不存在,则去创建这个文件