Python中的装饰器

装饰器

  • 装饰器定义;
    • 本质是函数 函数的目的是完成特定的功能
  • 装饰器功能:一个装饰其他函数功能的函数(为其他函数添加特定的功能)

抛出问题:

  假如我们现在有10个函数,每个函数都有自己独特的功能,但是,现在我们需要给这10个函数添加一个记录日志的功能

# 定义日志的函数,然后将日志函数添加到test的十个函数中
def logger():
    print("...logger...")

def test1():
    pass
    logger()

def test2():
    pass
    logger()

def test3():
    pass
    logger()

test1()
test2()
test3()
使用添加函数的方法

  特定场景:假如,这10个函数已经再线上运行了,比如说,现在需要再用户已经使用的软件中,给这10个函数添加新的功能,那么该怎么做?

    • 如果我们直接修改函数的源代码,可能会导致软件崩溃,所以原则上,我们不能在已经上线的程序中修改源代码。

装饰器原则:

  • 原则1:不能修改被装饰的函数的源代码
  • 原则2:不能修改被装饰的函数的调用方式
  • 装饰器对被装饰的函数是完全透明的,函数感知不到装饰器的存在,因为函数的源代码和调用方式都没有改变

初识装饰器:

  • 计算函数运行时间的装饰器
    import time
    # 创建获取函数运行时间的装饰器
    def timmer(func):
        def warpper(*args, **kwargs):
            start_time = time.time()
            func()
            stop_time = time.time()
            print("the func run time is %s" % (stop_time-start_time))
        return warpper
    
    @timmer     #为test函数添加获取test函数运行时间的功能
    def test():
        time.sleep(3)
        print("in the test")
    
    test()
    """
    运行效果:
    in the test
    the func run time is 3.004085063934326
    """
  • 由上代码可以得知:
    • 装饰器本身就是一个函数
    • 装饰器不修改被装饰函数的源代码、同时也不修改被装饰函数的调用方式
    • 对于test函数来说,装饰器timmer就和不存在一样

实现装饰器的知识储备:

  • 函数即”变量“
  • 高阶函数
  • 嵌套函数

  高阶函数 + 嵌套函数 =》 装饰器

函数即“变量”:

# 第一种
def foo():
    print("in the foo")
    bar()
foo()

第二种
def foo():
    print("in the foo")
    bar()
def bar():
    print("in the bar")
foo()

# 第三种
def bar():
    print("in the bar")
def foo():
    print("in the foo")
    bar()
foo()

# 第四种
def foo():
    print("in the foo")
    bar()
foo()
def bar():
    print("in the bar")
分析内存地址

高阶函数:

  • 把一个函数名当作实参传递给另一个函数:

    import time
    def bar():
        time.sleep(3)
        print("in the bar")
    # print(bar) # bar记录了bar函数在内存中的地址
    
    def foo(func):
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))
    
    # 满足了装饰器的不修改被装饰器的源代码的条件,但是调用方式被修改了
    foo(bar) 
    """
    运行结果:
    in the bar
    the func run time is 3.0134708881378174
    """
    符合装饰器的条件之一
    • 在不修改被装饰函数源代码的情况下为其添加功能
  • 返回值中包含函数名
    import time
    def bar():
        time.sleep(3)
        print("in the bar")
    
    def foo(func):
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))
        return func
    
    # foo(bar())
    
    #print(foo(bar))
    
    # t = foo(bar)
    # print(t)
    
    # t = foo(bar)
    # t()
    # 不修改函数的调用方式,为其添加功能
    bar = foo(bar)
    bar()
    符合装饰器的条件之二
    • 不修改被装饰函数的调用方式

嵌套函数:

  • 在函数体内使用def关键字定义一个新的函数
    # 嵌套函数
    def foo():
        print("in the foo")
        def bar():
            print("in the bar")
        bar()
    # bar() #报错,因为bar的作用域仅在foo()函数体内,当foo函数运行结束,那么bar就会释放空间
    foo()
    嵌套函数
  • 变量作用域的访问顺序
    x = 0
    def grandfather():
        x = 1
        def father():
            x = 2
            def son():
                x = 3
                print(x)
            son()
        father()
    grandfather()
    代码演示

闭包:

  当局部变量脱离了函数体后,依然可以使用:

def foo():
    sum_1 = 100
    def deco():
        print(sum_1)
    return deco
a = foo()
a()     #100
# print(sum_1) # 报错,局部变量不能在局部以外的地方使用
演示闭包

  解释代码:

def foo(x):
    def deco(y):
        print(x+y)
    return deco

a = foo(10)
print(a)    #<function foo.<locals>.deco at 0x00000275D6421040>
a(5)        # 15
b = foo(20)
print(b)    #<function foo.<locals>.deco at 0x0000017D992D10D0>
b(5)        #25

"""
所有函数都有一个__closure__属性,如果这个函数是一个闭包的话,那么它返回的是一个由cell对象组成
的元组对象。cell对象的cell_contents属性就是闭包中的自由变量
"""
print(foo.__closure__)  #None
print(a.__closure__)    #(<cell at 0x000002B1F02C1370: int object at 0x000002B1F0116A50>,)
print(b.__closure__)    #(<cell at 0x000002B1F018A0D0: int object at 0x000002B1F0116B90>,)
print(a.__closure__[0].cell_contents)   #10
print(b.__closure__[0].cell_contents)   #20
"""
这解释了为什么局部变量脱离函数之后,还可以在函数之外被访问的原因,因为它存储在了闭包的cell_contens中了
"""
演示闭包原理

实现装饰器:

import time
def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(3)
    print("in the test1")
@timer
def test2():
    time.sleep(3)
    print("in the test2")

# test1 = timer(test1)
# test2 = timer(test2)
test1()
test2()

改进后的装饰器:

import time
def timer(func):
    def deco(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(3)
    print("in the test1")
@timer
def test2(name):
    time.sleep(3)
    print("in the test2 %s"% name)

test1()
test2("python")

 优化后的装饰器:

import time
def timer(func):
    def deco(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))
        return res
    return deco

@timer
def test1():
    time.sleep(1)
    print("in the test1")
@timer
def test2(name):
    time.sleep(2)
    print("in the test2 %s"% name)

@timer
def test3(name, age):
    time.sleep(3)
    print("in the test3 %s"% name)
    return age+1

test1()
test2("python")
#test3("某人飞",999)
print(test3("某人飞",999))

加强版装饰器:

# 加强版装饰器
usern = "fjf"
passwd = "123456"
def dl(func,*args, **kwargs):
    username = input("请输入您的账号")
    password = input("请输入您的密码")
    if username == usern and password == passwd:
        print("登录成功")
        res = func(*args, **kwargs)
        return res
    else:
        print("您的账号或者密码错误")

def login(auth_type):
    def wrapper_inout(func):
        def wrapper(*args, **kwargs):
            if auth_type == "loca":
                print("通过loca验证")
                dl(func, *args, **kwargs)

            elif auth_type == "loca1":
                print("通过loca1验证")
                dl(func, *args, **kwargs)

            elif auth_type == "ip":
                print("通过ip验证")
                dl(func, *args, **kwargs)
            else:
                print("暂不支持其他登录方式")
        return wrapper
    return wrapper_inout

@login(auth_type="loca")
def qq_user():
    print("欢迎使用qq")

@login(auth_type="loca1")
def wx_user(name):
    print("欢迎%s使用微信"%name)

@login(auth_type="ip")
def wb_user(name, age):
    print("欢迎%s使用微博"%name)
    return age >= 18

@login(auth_type="smdx")
def ys():
    pass

ys()
qq_user()
wx_user("某人飞")
print(wb_user("某人飞", 17))
"""
闭包中的拓展知识点:
导入functools模块:
1.@functools.wraps(func):在闭包中将原函数func的文件名、注释文档等,传递给inner函数。
2.func.__name__:获取函数的名字(字符串), 比如:func 这是一个字符串,并不是一个func函数的地址
3.func.__doc__:获取函数的注释文档
"""
import functools


def auth(func):
    @functools.wraps(func)  # inner.__name=func.__name   inner.__doc__=func.__doc__
    def inner(*args, **kwargs): 
        """这个是inner函数的注释文档"""
        # 在原函数func执行之前添加的功能
        res = func(*args, **kwargs)  # 对接收到的参数进行拆包,传递给原函数func()
        # 在原函数func执行之之后添加的功能
        return res  # 将原函数func的返回值返回给调用者
    return inner


@auth
def func():
    """这个是func函数的注释文档"""
    print("this is func")
    return "func"


print(func.__name__)    # 原结果:inner  加上代码 @functools.wraps(func) 后结果为:func
print(func.__doc__)     # 原结果:这个是inner函数的注释文档   加上代码 @functools.wraps(func) 后结果为:这个是func函数的注释文档

 

原文地址:https://www.cnblogs.com/fjfsu/p/15651695.html