Python之路第四天,基础(4)-装饰器,迭代器,生成器

时间:2021-06-12 23:05:21

装饰器

装饰器(decorator)是一种高级Python语法。装饰器可以对一个函数、方法或者类进行加工。在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果。相对于其它方式,装饰器语法简单,代码可读性高。因此,装饰器在Python项目中有广泛的应用。

为了说明装饰器的使用方法,我们先写几个简单的例子,然后逐步深入。

简单的写了两个函数,他们的功能很简单就是打印一句话:

def f1():
print('hello,f1()') def f2():
print('hello,f2()') f1()
f2()

接着,我想让这两个函数都添加一个功能,就是在打印这一句话之前先打印'before'

def f1():
print('before')
print('hello,f1()') def f2():
print('before')
print('hello,f2()') f1()
f2()

试想,如果我们有100个这样的函数,那我们就需要改100次,改动的比较多,那我们有什么办法可以在不改变函数的情况下为每个函数添加这些功能,这时就用到了装饰器

def outer(func):
def inner():
print('before')
func()
return inner #返回函数名inner,这时 f1 = inner,也就是说f1被重新赋值成inner,
#调用f1实际上是执行的inner函数。inner函数又调用func 实际是调用
#的老的f1函数。
@outer
def f1():
print('hello,f1()') @outer
def f2():
print('hello,f2()') f1()
f2()

装饰器可以用def的形式定义,如上面代码中的outer。装饰器接收一个可调用对象作为输入参数,并返回一个新的可调用对象。装饰器新建了一个可调用对象,也就是上面的inner。inner中,我们增加了打印before功能,并通过调用func()来实现原有函数的功能。

定义好装饰器后,我们就可以通过 @ 语法使用了。在函数f1和f2定义之前调用 @outer,我们实际上将f1或f2传递给outer,并将outer返回的新的可调用对象赋给原来的函数名(f1或f2)。 所以,当我们调用f1()的时候,就相当于:

f1 = outer(f1)
f1()

上面例子函数都不带参数,下面我们讲一下带参数的函数装饰器的使用:

def outer(func):
def inner(s):
print('before')
func(s)
return inner @outer
def f1(s):
print(s) @outer
def f2(s):
print(s) s1 = 'hello,f1()'
s2 = 'hello,f2()'
f1(s1)
f2(s2)

和上面的不带参数的其实没多大的变化,只是你函数带几个参数,inner也得带几个参数,然后func也就是老的f1或f2也得带上参数。

函数还有一个特性就是有返回值,那我们下面讲一下带有返回值的函数该怎么用:

def outer(func):
def inner(s):
print('before')
return func(s)
return inner @outer
def f1(s):
print(s)
return 'Successful call f1' @outer
def f2(s):
print(s)
return 'Successful call f2' s1 = 'hello,f1()'
s2 = 'hello,f2()'
res1 = f1(s1)
res2 = f2(s2)
print(res1)
print(res2)

上面例子函数只带了一个参数,下面我将函数改成不定参数:


def outer(func):
def inner(*args, **kwargs):
print('before')
return func(*args, **kwargs)
return inner @outer
def f1(s):
print(s)
return 'Successful call f1' @outer
def f2(s):
print(s)
return 'Successful call f2' s1 = 'hello,f1()'
s2 = 'hello,f2()'
res1 = f1(s1)
res2 = f2(s2)
print(res1)
print(res2)

双层装饰器,下面用用户管理系统举例


# 装饰器,判断是否登录
def is_login(func):
"""
装饰器,在使用功能之前判断用户是否登录,没登录是不能使用某些功能的
:param func: 使用装饰器的函数的函数名
:return: 将新的函数名传回去赋值给使用装饰器的函数
"""
def inner(*args, **kwargs):
if USER_INFO['is_login']:
return func(*args, **kwargs)
else:
print('\033[31m您还未登录,请登录!\033[0m')
return inner # 装饰器,判断用户是否是管理员
def is_manager(func):
"""
装饰器,在使用某些功能之前判断用户是否为管理员,非管理员不能使用
:param func: 使用装饰器的函数的函数名
:return: 将新的函数名传回去赋值给使用装饰器的函数
"""
def inner(*args, **kwargs):
if USER_INFO['role'] == '1':
return func(*args, **kwargs)
else:
print('\033[31m您不是管理员,没有权限使用这些功能,抱歉!\033[0m')
return inner @is_login
@is_manager
def show_info_all():
"""
显示所有用户信息,此功能只有登录后,并且是管理员才能使用。
:return: None
"""
x = PrettyTable(["姓名", "密码", "邮箱", "手机号码", "角色"])
x.align["姓名"] = "l"
x.padding_width = 1
user_dict = get_user_info()
users = []
for user in user_dict:
users.append(user)
users.sort()
for user in users:
# 将用户名插入到列表的第一个元素
user_dict[user].insert(0, user)
# 密码用6个*代替
user_dict[user][1] = '*'*6
if user_dict[user][4] == '1':
user_dict[user][4] = '管理员'
else:
user_dict[user][4] = '普通用户'
x.add_row(user_dict[user])
print(x)

双层装饰器的执行顺序是从上往下,如上,先判断用户是否登录,再判断用户是否为管理员;然后代码解释的顺序是从下往上,例如先解释是否为管理员,然后再解释是否登录。

迭代器

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件

特点:

  1. 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
  2. 不能随机访问集合中的某个值 ,只能从头到尾依次访问
  3. 访问到一半时不能往回退
  4. 便于循环比较大的数据集合,节省内存

例子:

>>> num = iter([1,2,3,4,5])
>>> num
<list_iterator object at 0x101402630>
>>> num.__next__()
1
>>> num.__next__()
2
>>> num.__next__()
3
>>> num.__next__()
4
>>> num.__next__()
5
>>> num.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

生成器

一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator);如果函数中包含yield语法,那这个函数就会变成生成器;

def func():
yield 1
yield 2
yield 3
yield 4

上述代码中:func是函数称为生成器,当执行此函数func()时会得到一个迭代器。

>>> temp = func()
>>> temp.__next__()
1
>>> temp.__next__()
2
>>> temp.__next__()
3
>>> temp.__next__()
4
>>> temp.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

例子:

利用生成器自定义range

def myrange(num):
start = -1
while True:
start = start + 1
if start >= num:
return
else:
yield start

利用迭代器访问range

x = myrange(4)
for i in x:
print(i)