0%

生成器装饰器和异常

python的第10节课笔记(基础高级)

生成器

什么是生成器———–>生成器本质上是迭代器
生成器作用———–>当我们的数据特别大的时候建立一个列表的储存数据就会很占内存的。这时生成器就派上用场了。它是占计算机资源相对较小的一种方法。
应用场景———–>处理数学中有很多算法是无限穷举的问题(比如自然数)

1
2
3
4
5
6
7
8
9
10
11
12
共同:
都可以使用for进行遍历
迭代器:
可以一次性直接打印查看
生成器:
直接打印返回出来的是内存地址
需要使用__next__方法进行一一取值
如果已经取完将报错

生成器的本质就是一个迭代器

dir() 函数,查看这个对象类里面拥有的方法
1
2
3
4
5
# 列表推导式
list1 = [i for i in range(5)]
for i in list1:
print(i)
print(list1)
1
2
3
4
5
6
7
8
# 元组生成器
tu1 = (n for n in range(5))
for n in tu1:
print(n)
print(tu1)
print(tu1.__next__())
print(tu1.__next__())
print(tu1.__next__())
1
2
3
可迭代对象: 相对于普通方法多了一个__iter__()
iter(): 将可迭代对象转化为迭代器
迭代器: 同时拥有__iter__()和 __next__()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 区别dir()
n = 1 # 数值
sn = set(dir(n))
print(sn)

li = [1, 2, 3]
sli = set(dir(li))
print(sli)

tu = (n for n in range(5))
stu = set(dir(tu))
print(stu)

print(sli - sn)
print("******")
print(stu - sn)

li = [1, 2, 3]
print(li)
li_iter = iter(li)
print(li_iter) # 可迭代器
print(next(li_iter))
print(next(li_iter))
print(next(li_iter))
print(next(li_iter))
1
2
3
生成器
return: 具有结束函数的功能
yield: 具有返回函数值的功能, 但是不是结束函数,只是暂停
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ef func():
yield 1
yield 2
yield 3
yield 4


res = func()
print("*************")
print(res) # 生成器对象
print(next(res)) # 一一取值
print(next(res))
print(next(res))
print(next(res))
print(next(res)) # 超出了迭代范围,报错
1
2
3
场景: 兔子数列
1 1 2 3 5 8 13 21 34
项数: 第几个数,数的顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 将a 和 b 的值进行互换
a = 1
b = 2
# 新变量名 = 前面保存有值的变量
a, b = b, a # a, b = 2, 1
#原理: 变量名重复,新值覆盖旧值
print(a, b)


def rabbit(end):
"""
:param end: 总项数
:return: 两数之和
"""
a = 0
b = 1
n = 0 # 初始化项数
while n < end:
# 第一位是原来的第二位, 第二位是前面两个数的和
a, b = b, a + b
yield b
n += 1 # 项数自增条件


res = rabbit(5)
print(next(res))
print(next(res))
print(next(res))
print(next(res))

装饰器

刚才一个小小的改动就让函数变得不一样了,但终究改变了函数,能不能不改变函数,给一个函数增加功能呢?

装饰器介绍

什么是装饰器———–>装饰器本质上是一个python函数
装饰器作用———–>可以在不改动原函数的前提下,对函数的功能进行扩充。
应用场景———–>引入日志;函数执行时间统计;执行函数前预备处理;执行函数后清理功能;权限校验;缓存。

1
装饰器的场景:在不改变原有代码的情况下又想改变功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def login():
print("欢迎**登录………………")


def add_user():
print("add……ing…………")


def del_user():
print("del……ing…………")


test = login
print(test)
test() # login()
1
2
3
4
闭包
作用:保护内部函数,让内部函数不会被外部直接修改
1、嵌套函数里面
2、外部函数将内部函数名的函数名返回
1
2
3
4
5
6
7
8
9
10
def out_fun():
print("out function")

def in_fun():
print("in function")
return in_fun


res = out_fun() # in_func
res() # in_func()

装饰器的定义

1
2
3
4
装饰器的定义
1、给外部函数加上一个必备参数
2、将这个必备参数加上括号写在内部函数里面
3、调用外部函数的时候,将要执行的函数名作为实参传入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 需要装饰的函数
def add_user():
print("add……ing…………")


# 装饰器
def out_fun(par):
"""
:param par: add_user
:return:
"""
print("out function")

def in_fun():
print("in function")
par() # add_user()
return in_fun


res = out_fun(add_user) # in_func
res() # in_func()
add_user()

简便调用

1
2
3
4
5
6
7
8
简便调用
def 装饰器():
pass

@装饰器名字
def 需要装饰的函数():
pass
如何修饰带有参数的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 装饰器
def out_fun(par):
"""
:param par: add_user
:return:
"""
print("out function")

def in_fun():
print("in function")
par() # add_user()
return in_fun


# 需要装饰的函数
@out_fun
def add_user():
print("add……ing…………")


add_user()

带有参数的装饰器

1
2
3
带有参数的装饰器:
装饰带有参数的函数时,装饰器内部函数需要定义不定长参数
即内部函数以及内部函数里面的参数()都需要加上 *args 以及 **kwargs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 装饰器
def out_fun(par):
"""
:param par: add_user
:return:
"""
print("out function")

def in_fun(*args, **kwargs):
print("in function")
par(*args, **kwargs) # add_user()
return in_fun


@out_fun
def my_sum(a, b):
print("两数之和:",a + b)


my_sum(1, 2)

*带有返回值的装饰器

1
带有返回值的装饰器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#装饰器
def out_fun(par):
"""
:param par: add_user
:return:
"""
print("out function")

def in_fun(*args, **kwargs):
print("in function")
return par(*args, **kwargs) # 将需要装饰的函数的返回值,返回到外部函数
return in_fun


@out_fun
def my_sum(a, b):
return f"两数之和:{a + b}"


print(my_sum(1, 2))

内置装饰器

1
2
3
4
内置装饰器
@property 让方法的调用就像属性一样,不需要加括号(加了则报错)
@classmethod 定义方法的时候,自动补全类
@staticmethod 不再自动补全self或者cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person():
def __init__(self, name, age):
self.name = name
self.age = age

@property
def play(self):
print("play")

@staticmethod
def study():
print("study")

@classmethod
def func(cls):
print(cls)


lm = Person('lm', 18)
# lm.play

异常

python中一般使用try-except语句处理异常
try语句用于检测异常
except语句用于捕获异常

try里面的代码如果出现异常
就会执行except里面的代码

1
2
3
4
try:
可能会遇到报错的代码
except:
报错的情况之下执行
1
2
3
4
5
try:
a = int(input("请输入一个数:"))
print(type(a), a)
except:
print("输入有误!!")
1
报告一下具体异常类型
1
2
3
4
5
try:
a = int(input("请输入一个数:"))
print(type(a), a)
except Exception as e: # 接收异常并取别名为e
print(e)
1
2
3
4
5
断言
assert 是断言, 和 if 判断类似,只是判断为False 的时候会报错,触发
AssertionError异常

assert 表达式
1
2
3
4
print("断言前")
assert 1 == 1.0 # 条件True,执行后面
assert 1 == 2.0 # 条件False, 报错
print("断言后")
1
2
自定义异常
raise 主动触发异常类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class TelNumError(Exception):  # 直接继承所有异常类的父类
def __init__(self, msg):
self.massage = msg


e = TelNumError("电话号码格式错误!!")
print(e)


def is_tel():
number = input("请输入一个电话号码:")
if number[0] == "1" and len(number) == 11 and number.isdigit():
print("是一个手机号!!")
else:
print("不是")
# 报错--->电话号码错误!!
raise TelNumError(f"{number}")


is_tel()


def is_tel():
number = input("请输入一个电话号码:")
if number[0] == "1" and len(number) == 11 and number.isdigit():
print("是一个手机号!!")
else:
try:
# 报错--->电话号码错误!!
raise TelNumError(f"{number}")
except Exception as e:
print(e)


is_tel()

异常处理

注意事项:

  1. try 后面必须跟上 except
  2. except 只有在函数中才能使用 return
  3. finally 不管是否发生异常,始终都会执行
  4. raise 可以主动抛出异常
1
2
3
4
5
6
7
8
try:
可能会报错的代码
except:
报错执行
else:
不报错执行
finally:
不管怎样都会执行
1
2
3
4
5
6
7
8
try:
a = int(input("请输入一个数:"))
except Exception as e: # 接收异常并取别名为e
print(e)
else:
print(type(a), a)
finally:
print("最终执行的代码!!!")