python-初识装饰器

185 阅读5分钟

初识函数装饰器

先了解作用域、高阶函数、闭包的概念

作用域

全局变量(模块内定义)、局部变量(函数内定义)、嵌套作用域(内部函数的作用域)、内置作用域(python内置定义)

a=1  #全局变量,定义在模块中

def foo():
    b=2 #局部变量,定义在函数里
    print("a1:",a)
    print("b1:",b)
    def inner():
        c=3 #嵌套作用域
        print("b3:", b)
        print("c3:", c)
    print("b4:", b)
    # print("c4:", c)#无法读取局部变量
print("a2:",a)
# print("b2:", b)#无法读取局部变量

foo()
print(__name__) #内置作用域,python内置定义
    a2: 1
    a1: 1
    b1: 2
    b4: 2
    __main__

高阶函数

函数名可以赋值给其他对象、函数名可以作为返回值、函数名可以作为参数传递

#================1、函数名可以赋值给其他对象================
def foo():
    print("执行foo函数")

#方法一
a=foo() 

#方法二
a=foo #函数名可以赋值给其他对象
a()

#结果
执行foo函数
执行foo函数

#================2、函数名可以作为返回值================
def foo():
    print("执行foo函数")
    def inner():
        print("执行inner函数")
    return inner  #函数名就是变量,就可以作为返回值
    
print("----执行a=foo()------")
a=foo() #返回变量inner,执行foo函数
print("----执行a()------")
a()  #相当于inner(),执行inner函数

#结果
----执行a=foo()------
ִ执行foo函数
----执行a()------
ִ执行inner函数


#================3、函数名可以作为参数传递================
def foo(func):
    func()

def bar():
    print("执行bar函数")

foo(bar)

#结果
执行bar函数

闭包

在内部函数里调用外部作用域的变量(但是非全局变量)

x="全局变量,定义在模块中"
def outer():
    y="局部变量,定义在函数里"
    def inner():
        print(y)
    return inner

#调用inner函数
outer()() #方法一

a=outer() #方法二
a()

"""上述方法即为闭包,在内部函数里调用外部作用域的变量(但是非全局变量)"""

#结果
局部变量,定义在函数里
局部变量,定义在函数里

装饰器

允许扩展已有功能代码,禁止修改已有功能代码;比如:被装饰函数A(),装饰函数B(A),在被装饰函数A()上@装饰函数B,相当于A=B(A),调用时只需要A(),已达到上述目的扩展已有功能而不修改已有功能的代码

方法一:写一个装饰函数扩展功能,原方法作为装饰函数的参数

import time

def foo():
    print("调用很多接口")
    time.sleep(3)

def show_time(func):
    beginTime=time.time()
    func()
    endTime = time.time()
    print("总耗时:",endTime - beginTime)

show_time(foo)
"""
上述方法缺点:加了show_time后,原先调用foo函数的地方都要重新修改为调用show_time,且foo作为传参
"""

#结果
调用很多接口
总耗时:3.002992630004883

方法二:装饰函数返回函数,并用装饰器简化调用

import time

def show_time(func): #装饰函数
    def inner():
        beginTime=time.time()
        func()
        endTime = time.time()
        print("总耗时:",endTime - beginTime)
    return inner

@show_time # 等同于foo=show_time(foo) #foo=inner
def foo(): #被装饰函数
    print("调用很多接口")
    time.sleep(1)

#----调用方法一:不加装饰器----------
foo=show_time(foo) #foo=inner
foo()

#----调用方法二:被装饰函数上@装饰函数----------
foo()

"""
上述为一个简单的装饰器
"""
#结果
调用很多接口
总耗时:1.0051441192626953

带参数的函数装饰器

import time

def show_time(func):
    def inner(case_name):
        beginTime=time.time()
        func(case_name)
        endTime = time.time()
        print("总耗时:",endTime - beginTime)
    return inner

@show_time
def foo(case_name): #foo函数带参数,则inner和里面的func都要带参数
    print("调用很多接口")
    time.sleep(1)

foo('参数')
"""
foo函数带参数,则inner和里面的func都要带参数
调用foo函数也要带参数
"""
#结果
调用很多接口
总耗时:1.0126347541809082

带参数的函数装饰器-不定长参数

import time

def show_time(func):
    def inner(*args,**kwargs): #不定长参数
        beginTime=time.time()
        func(*args,**kwargs)
        endTime = time.time()
        print("总耗时:",endTime - beginTime)
    return inner

@show_time
def foo1(a,b):
    print("foo1调用很多接口")
    time.sleep(1)

@show_time
def foo2(a,b,c,d): 
    print("foo2调用很多接口")
    time.sleep(1)

foo1('参数a','参数b')
foo2('参数a','参数b','参数c','参数d')

#结果
foo1调用很多接口
总耗时:1.001772403717041
foo2调用很多接口
总耗时:1.0102813243865967

多个装饰器执行顺序

def dec1(func):
    print("1111")
    def one():
        print("2222")
        func()
        print("3333")
    return one


def dec2(func):
    print("aaaa")
    def two():
        print("bbbb")
        func()
        print("cccc")
    return two

@dec1
@dec2
def foo():
    print("调用很多接口")

foo()
"""
多个装饰器执行顺序是从最后一个装饰器开始,执行到第一个装饰器,再执行函数本身
"""

#结果
aaaa
1111
2222
bbbb
调用很多接口
cccc
3333

初识类装饰器

类装饰器本质上和函数装饰器原理、作用相同,都是为其它函数增加额外的功能。使用类装饰器可以直接依靠类内部的__call__方法来实现,当使用 @ 形式将类装饰器附加到函数上时,就会调用类装饰器的__call__方法。而不需要向函数装饰器那样,在装饰器函数中定义嵌套函数,来实现装饰功能。

__call__方法

class A:
        pass
a=A()
a()

#结果
TypeError: 'A' object is not callable   无法调用
class A:
        def __init__(self):
                self.name="AAA"
        def __call__(self):# 类中实现了__call__方法
                print("类中实现了__call__方法:"+self.name)

a=A()
a()
"""
类对象可调用,并在调用类对象的时候,自动执行__call__方法
"""

#结果
类中实现了__call__方法:AAA

类装饰器实例

import time

class show_time():
        def __init__(self, func):  # 初始化函数中传入函数对象的参数
                self._func = func

        def __call__(self):  # 定义__call__方法,直接实现装饰功能
                beginTime = time.time()
                self._func()
                endTime = time.time()
                print('总耗时:', (endTime - beginTime))

@show_time  # Foo=show_time(Foo)
def Foo():
        print('调用很多接口')
        time.sleep(1)

Foo()# Foo=show_time(Foo)(),没有嵌套关系了,直接执行Foo的 __call__方法,实现装饰功能

#结果
调用很多接口
总耗时:1.0099809169769287