Python基础之 装饰器|Python 主题月

438 阅读6分钟

 装饰器

本文正在参加「Python主题月」,详情查看活动链接

装饰器是 Python 的重要组成部分。简而言之:它们是修改其他函数功能的函数。大多数初学者不知道在哪里使用它们,所以我将分享一些装饰器可以使您的代码更简洁。

首先,让我们讨论如何编写自己的装饰器。

这可能是最难掌握的概念之一。我们将一步一步来,以便您能够完全理解它。

1. Python 中的一切都是一个对象:

首先让我们了解Python中的函数:

def test(name="dm"):
    return "你好 " + name

print(test())
>>> '你好 dm'

2. 在函数中定义函数:

所以这些是功能的基础。让我们进一步了解您的知识。在 Python 中,我们可以在其他函数中定义函数:

def test(name="dm"):
    print("我是111")

    def inside1():
        return "inside11"

    def inside2():
        return "inside22"

    print(inside1())
    print(inside2())

test()
>>> 我是111
    inside11
    inside22


inside1()  # 不能在外面直接调用里面的函数
>>> NameError: name 'inside1' is not defined

所以现在我们知道我们可以在其他函数中定义函数。换句话说:我们可以创建嵌套函数。现在你需要学习一件事,函数也可以返回函数。

3. 从函数内部返回函数:

没有必要在另一个函数中执行一个函数,我们也可以将它作为输出返回:

def test(name="dm"):
    def inside1():
        return "inside11"

    def inside2():
        return "inside22"

    if name == "dm":
        return inside1
    else:
        return inside2

a = hi()
print(a)
>>> <function inside1 at 0x7f21e36594>


print(a())
>>> inside11

if/else子句中,我们返回inside1和 inside2,而不是inside1()和 inside2()。这是为什么?这是因为当你在它后面加上一对括号时,函数就会被执行;而如果你不在它后面加上括号,那么它可以被传递并且可以分配给其他变量而不执行它。

4. 将一个函数作为另一个函数的参数:

def test():
    return "hi boy!"

def inner(func):
    print("inner1")
    print(func())

inner(test)
>>> inner1
    hi boy!

现在你已经掌握了所有必要的知识来了解什么是装饰器。装饰器让你在函数前后执行代码。

5. 编写你的第一个装饰器:

在上一个例子中,我们实际上做了一个装饰器!让我们修改之前的装饰器并制作一个更有用的程序:

def outside(func):

    def inner():
        print("inner1")
        func()
    return inner

def abc():
    print("hihihihi")

abc()
>>> "hihihihi"

a = outside(abc)

a()
>>> inner1
    hihihihi

我们只是应用了之前学到的原则。这正是装饰器在 Python 中所做的!他们包装一个函数并以一种或另一种方式修改它的行为。现在您可能想知道为什么我们没有在代码中的任何地方使用 @?这只是组成装饰函数的一种简短方法。下面是我们如何使用 @ 运行前面的代码示例。

@outside
def abc():
    print("hihihihi")

a = outside(abc)

a()
>>> inner1
    hihihihi

现在好多了。让我们继续学习一些装饰器的用例。

示例

from functools import wraps
def decorator_name(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if not can_run:
            return "Function will not run"
        return f(*args, **kwargs)
    return decorated

@decorator_name
def func():
    return("Function is running")

can_run = True
print(func())
>>> Output: Function is running

can_run = False
print(func())
>>> Output: Function will not run

注意:@wraps接受要装饰的函数并添加复制函数名称、文档字符串、参数列表等的功能。这允许我们在装饰器中访问预装饰函数的属性。

5.1. 用例:

现在让我们来看看装饰器真正发挥作用的领域,它们的使用使得一些东西非常容易管理。

5.2. 授权

装饰器可以帮助检查某人是否被授权使用 Web 应用程序中的端点。它们广泛用于 Flask Web 框架和 Django。这是使用基于装饰器的身份验证的示例:

例子 :

from functools import wraps

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            authenticate()
        return f(*args, **kwargs)
    return decorated

5.3. 日志记录

日志是装饰器发光的另一个领域。下面是一个例子:

from functools import wraps

def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging

@logit
def addition_func(x):
   """Do some math."""
   return x + x


result = addition_func(4)
# Output: addition_func was called

我相信你已经在考虑装饰器的一些巧妙用途。

装饰器条件:

1.外部函数中定义了内部函数
2.外部函数一定要有返回值,返回的值是:内部函数名
3.内部函数引用了外部函数的变量
4.函数作为外层函数参数

使用装饰器

  @装饰器名字
   def 函数名():
     pass
  1. 简单的装饰器

    在闭包的前提下引用函数作为外层函数的参数

    import time
    ​
    ​
    # 装饰器带参数
    def zhuang1(func):  # 接收f1函数作为参数
        def wrapper(*args, **kwargs):  # 这里就要加*args,**kwargs 1.外部函数中定义了内部函数
            print("正在校验中.....")
            time.sleep(2)
            print("校验完毕.....")
    ​
            func(*args, **kwargs)  # 调用原函数f1    4.函数作为外层函数参数
    ​
        return wrapper  # 把装饰器在返回给f1 2.外部函数一定要有返回值,返回的值是:内部函数名
    ​
    ​
    @zhuang1
    def f1():
        print("我是户主one------one")
    ​
    f1()
    
  2. 带有参数的函数

    import time
    ​
    ​
    # 装饰器带参数
    def zhuang1(func):  # 接收f1
        def wrapper(*args, **kwargs):  # 这里就要加*args,**kwargs
            print("正在校验中.....")
            time.sleep(2)
            print("校验完毕.....")
            # 调用原函数
            func(*args, **kwargs)
    ​
        return wrapper  # 把装饰器在返回给f1
    ​
    ​
    sum = 10000
    ​
    ​
    @zhuang1
    def f1(sum):
        print("我是户主one------one", sum)
    ​
    ​
    f1(sum)
    ​
    ​
    @zhuang1
    def f2(sum, name="abc"):
        print("我是户主{}-----two".format(name), sum)
    ​
    ​
    f2(sum)
    ​
    ​
    @zhuang1
    def f2(sum, name="aaaaaa"):
        print("我是户主{}-----two".format(name), sum)
    ​
    ​
    f2(sum, name="adfafasdasd")  # 覆盖了name的值
    
  3. 装饰的函数有返回值

    一定要返回原函数

    # 如果装饰的函数有返回值需要在装饰器的内部函数中用return返回 装饰函数的运行结果
    def say(func):
        def inner():
            print("-----2-----")
            return func()   # 这里一定要返回原函数
    ​
        return inner
    ​
    ​
    @say
    def show():
        return "-----1-----"
    ​
    ​
    print(show())
    
  4. 使用多个装饰器装饰同一个函数

    '''
    如果装饰器是多层的,谁距离函数最近就优先使用哪个装饰器
    '''
    def zhuangfeng1(func):  # 定义装饰器1
        print("-----one---start")
        def warper():
            print("刷漆..")
            return func()  # 返回原函数
    ​
        print("end")
        return warper
    ​
    def zhuangfeng2(func):  # 定义装饰器2
        print("-----two---start")
        def warper():
            print("打麻椒..")
            return func()  # 返回原函数
    ​
        print("end")
        return warper
    ​
    ​
    # 使用装饰器
    @zhuangfeng2
    @zhuangfeng1
    def fz1():
        return "户主一"
    ​
    ​
    print(fz1())
    ​
    """
    -----one---start
    end
    -----two---start
    end
    打麻椒..
    刷漆..
    户主一
    """
    #总结:多个装饰器作用于同一个函数的时候,从上往下依次执行,但是,原函数只被调用一次
    
  5. 装饰器带有参数

    有三层

    '''
        装饰器带参数
      带参数的装饰器是三层的
        第一层: 负责接收装饰器的参数
        第二层: 负责接收函数
        第三层: 去接收函数里面的参数
    '''
    def outer(a):  # 第一层: 负责接收装饰器的参数
        def decorate(func):  # 第二层: 负责接收函数
            def wraper(*args, **kwargs):  # 第三层: 去买个接收函数的参数
                func(*args, **kwargs)
                print("--->铺地砖{}块".format(a))
    ​
            return wraper
    ​
        return decorate
    ​
    ​
    @outer(100)  # 装饰器带有参数
    def house(time):
        print("我{}日期拿到了毛坯房".format(time))
    ​
    ​
    house("2020-9-9")