编程语言
首页 > 编程语言> > python入门精讲-27装饰器

python入门精讲-27装饰器

作者:互联网

目录

# 27 装饰器

01. 装饰器的作用

用于管理和增强 函数 行为的代码,提供一种在函数或者类定义中插入自动运行代码的机制。
特点:提供了更明确的语法,更高的代码可维护性和更好的一致性。
可以通过函数或者类来定义装饰器,可以实现装饰器函数、装饰器类,装饰器函数可以用于修饰函数+类方法,类装饰器也可以用于修饰函数+类方法,但是类作为装饰器,来装饰类会遇到问题,建议使用函数来定义装饰器。

02.装饰器的函数基础

02-1 函数赋值给变量

直接输入函数的名称,返回的是一个地址。可以将函数名赋值给变量,通过变量来执行函数。
img

02-2函数作为参数传递

函数同样也可以作为参数进行传递,下面的例子表明:可以通过传递函数名来确认要执行的函数,对于没有定义的函数动作,还可以通过lambda进行函数参数的返回。
img

02-3函数的嵌套

# 返回的是被嵌套函数的执行结果
def func1():
    def func2():
        return "Hello"
    return func2()
r = func1()
print(r)
# 返回的是被嵌套函数的 函数对象
def func3():
    def func4():
        return "Hello"
    return func4
r = func3() 
print(r)

运行的结果:

Hello
<function func3.<locals>.func4 at 0x7f97d9c045e0>

返回的是函数对象,如果要返回函数的执行结果,可以直接运行得到。
在上面的基础之上,加上代码:

r = func3()()
print(r)

运行结果:Hello
函数嵌套,返回值,和函数嵌套中定义返回的内容有关。

02-4函数的作用域

LEGB 从内到外,不使用本地的x,打算使用封装的x,使用nonlocal来定义x,意思就是这个变量的值不是本地的变量值,使用和封装的x的变量,如下是从内到外的一个作用域。

L : local
E : enclosure 
G: global
B: builtin

同样如果声明变量的值为global 的话,就会使用全局的变量的值。

def func():
    x = 10
    def func2():
        x = 20
        return x+10
    return func2()

def func3():
    x = 10
    def func4():
        nonlocal x
        return x+10
    return func4()

print(func())
print(func3())
输出:
30
20

例子2:

    x = 500

def f1():
    x = 100
    def f2():
        # 使用封装的x,会改变f1()中的x,global的x不会改变
        nonlocal x
        print(f'Nonlocal的x:{x}')
        x += 20
        print(f'函数f2的x:{x}')

    def f3():
        # 使用全局的x,改变global的x的值
        global x
        x += 20
        print(f'函数f3的global的x的值是{x}')

    f2()
    print(f'函数f1外侧的x:{x}')
    f3()

f1()
print(f'Global x is:{x}')

输出:
Nonlocal的x:100
函数f2的x:120
函数f1外侧的x:120
函数f3的global的x的值是520
Global x is:520

03.函数定义装饰器

03-1 函数装饰器的作用

装饰器用于接收一个函数名的变量作为参数,可以对函数进行装饰:对结果进行处理、添加额外的功能等等操作,装饰器可以批量丰富函数的功能。

03-2函数作为装饰器-装饰函数

带参数的函数的装饰器,装饰器的参数是函数名称,装饰器函数是如何获取到函数的参数呢。
是通过;*args, **kwargs 来获取到的函数的参数。
以下的例子为:带参数 + 不带参数的装饰器的用法,可以看到带参数的函数say_hello('Lucy')可以正常传递。

import time

def wrapperfun(func):
    def warpper(*args, **kwargs):
        print(func(*args, **kwargs) + '@' + str(time.time()))
    return warpper

@wrapperfun
def say_hello(name):
    return f'Hello {name}'

@wrapperfun
def say_hi():
    return 'Hi'

if __name__ == "__main__":
    say_hello("Lucy")
    say_hi()
输出:
Hello Lucy@1658300817.562695
Hi@1658300817.562695
  • 定义一个装饰器,可以装饰不同的函数,让每个函数执行一些相同的操作
  • 带参数的函数,可以通过装饰器内部的 (*args,**kwargs)来传递,表示传递所有的参数
  • 装饰器函数本身,返回的是一个函数对象

03-3函数作为装饰器-装饰类方法

可以使用定义的函数装饰器来修饰类方法,使用的方法和函数基本一致

import time

def wrapperfun(func):
    def warpper(*args, **kwargs):
        print(func(*args, **kwargs) + '@' + str(time.time()))
    return warpper

class Student:
    def __init__(self,name):
        self.name = name
    # 定义的函数装饰器来修饰类方法
    @wrapperfun
    def get_name(self):
        return self.name

if __name__ == "__main__":
    s = Student('Lily')
    s.get_name()
输出结果:
Lily@1658363063.2143705

例子02:

def wrapper_fun(func):
    def wapperf(*args,**kwargs):
        return func(*args,**kwargs).upper()
    return wapperf

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age  = age

# 用于修饰类方法
    @wrapper_fun
    def get_info(self):
        return f'Person info: name-{self.name},age-{self.age}'

if __name__ == '__main__':
    p = Person('Mike',33)
    print(p.get_info())

输出结果:
PERSON INFO: NAME-MIKE,AGE-33

04.定义类作为装饰器

04-1 类作为装饰器-装饰函数

定义一个类,做为装饰器,用来装饰一个函数。
使用了__call__方法,该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用。

class AddTag:
    def __init__(self,func):
        self.func = func

    def __call__(self,*args,**kwargs):
        return '<p>'+self.func(*args,**kwargs)+'</p>'

@AddTag
def greeting(name):
    return f'Hi {name}'

if __name__ == "__main__":
    print(greeting('Amanda'))
    
输出:
<p>Hi Amanda</p>

例子2:

class Decorator:
    def __init__(self,func):
        self.func = func

    def __call__(self,*args,**kwargs):
        return self.func(*args,**kwargs).upper()
@Decorator
def greeting(name):
    return f'Hello,{name}'

if __name__ == '__main__':
    print(greeting('Jim'))
    
输出:
HELLO,JIM

04-2 类装饰器来装饰-类方法

1、没有使用装饰器

class AddTag:
    def __init__(self,func):
        self.func = func

    def __call__(self,*args,**kwargs):
        return '<p>'+self.func(*args,**kwargs)+'</p>'

class Student:
    def __init__(self,name):
        self.name = name

    def get_name(self):
        print(self.name)

if __name__ == "__main__":
    s = Student('Lucy')
    s.get_name()

返回的结果:Lucy

2、使用了类装饰器
类装饰中使用了__call__方法调用传递过来的函数对象

class AddTag:
    def __init__(self,func):
        self.func = func

    def __call__(self,*args,**kwargs):
        return '<p>'+self.func(*args,**kwargs)+'</p>'

class Student:
    def __init__(self,name):
        self.name = name

    @AddTag
    def get_name(self):
        print(self.name)

if __name__ == "__main__":
    s = Student('Lucy')
    s.get_name()

运行的结果:
Alt text
抛出异常,提示参数的个数不正确,AddTag和get_name中的self字段冲突了
出现该问题的原因:AddTag装饰器类中有self这个参数,同样被修改的函数也有self这个参数,两个相同的参数导致无法区分。

例子:打印函数的耗时

import time
def clock(func):
    def wrapper(*args, **kwargs):
        print(f'Start time:{time.ctime()}')
        func(*args, **kwargs)
        print(f'End time:{time.ctime()}')

    return wrapper
@clock
def intersect(sq1, sq2):
    res = []
    for sq in sq1:
        if sq in sq2 and sq not in res:
            res.append(sq)
    else:
        print('Task done')
    print(f'res is:{res}')
if __name__ == "__main__":
    sq1 = [1, 2, 4, 5, 421, 3, 14, 546, 4, 1, 1, 4, 1, 3, 444, 4, 5, 4]
    sq2 = [3.1, 3, 11, 32, 54, 1, 3, 544, 4, 4, 1, 55, 1, 1, 4145546, 14]
    intersect(sq1, sq2)
# seq1 = set(sq1)
# seq2 = set(sq2)
# sq = seq1&seq2
# print(f'sq is:{sq}')

运行的结果:
img

05.函数做为装饰器是通用的

定义一个装饰器,尽量使用函数的方式进行定义,函数的方式是通用的。
而通过类定义的装饰器,只能用来装饰函数,不经过其他处理无法装饰类方法。
结论:定义装饰器,尽量使用函数的方式来定义

06.参数化装饰器

在没有使用参数装饰器之前,添加装饰器后,起到修饰作用的参数是固定的,如函数中的

字符,如果想要修饰的参数可变,需要参数化装饰器。
没有使用参数化的装饰器

def add_tag(func):
    def wrapper(*args,**kwargs):
        return f'<p>{func(*args,**kwargs)}</p>'
    return wrapper


@add_tag
def say_hi(name):
    return f'Hi {name}'

if __name__ == "__main__":
    print(say_hi('Lucy'))

使用参数化的装饰器
可以在之前没有参数化的装饰器上再定义一层函数,然后再加上返回值。

def tag(tag):
    def add_tag(func):
        def wrapper(*args,**kwargs):
            return f'<{tag}>{func(*args,**kwargs)}<{tag}>'
        return wrapper
    return add_tag

@tag('body')
@tag('p')
@tag('div')
def say_hi(name):
    return f'Hi {name}'

if __name__ == "__main__":
    print(say_hi('Lucy'))

运行的结果:

<body><p><div>Hi Lucy<div><p><body>

例子02 带参数的装饰器
01 - 不带参数

def decorate(func):
    def wrapper(*args,**kwargs):
        return '<p>' + func(*args,**kwargs) + '</p>'
    return wrapper

@decorate
def greeting(name):
    return f'Hello ,{name}'

if __name__ == '__main__':
    print(greeting('Tom'))

输出:
<p>Hello ,Tom</p>

02 - 带参数
如果装饰器带参数,就要在之前的装饰器上再封装一层即可,并逐层进行return
先写好一个不带参数的装饰器decorate,然后再加上一层函数tags并传递一个参数,tags函数返回decorate
并且一个函数,还可以被多个装饰器装饰,执行的顺序是从内到外的顺序,如下代码所示:先使用tags('p')进行装饰,然后再使用tags('div')进行装饰。

def tags(tag):
    def decorate(func):
        def wrapper(*args,**kwargs):
            return f'<{tag}>{func(*args,**kwargs)}</{tag}>'
        return wrapper
    return decorate

@tags('div')
@tags('p')
def greeting(name):
    return f'Hello ,{name}'

if __name__ == '__main__':
    print(greeting('Tom'))

标签:__,27,return,name,python,精讲,装饰,def,函数
来源: https://www.cnblogs.com/spring2022/p/16501167.html