ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

python入门精讲-27装饰器

2022-07-21 11:03:24  阅读:183  来源: 互联网

标签:__ 27 return name python 精讲 装饰 def 函数


目录

# 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有