Python学习之装饰器

装饰器就是修改其他函数功能的函数。

学习装饰器,需要一步一步来解析(涉及前面的函数知识)

函数对象

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

函数对象有一个__name__属性,可以拿到函数的名字

# 函数
def hello(name="python"):
    return "hello " + name


# 直接调用函数
print(hello())  # hello python

# 将函数赋值给一个变量
greet = hello
print(greet)    #<function hello at 0x0000011141F361F0>
print(greet())  # hello python
print(greet.__name__)   # hello

返回函数

在函数中定义另外的函数

# 在函数中返回函数
def subject(name="python"):
    print("you are inside in the subject() function")

    def pyth():
        return "you are inside in pyth() function"

    def java():
        return "you are inside in java() function"

    print(pyth())
    print(java())
 
subject()

 pyth()函数和java()函数只能在subject()函数里面被调用。

上述例子我们不需要在一个函数里去执行另一个函数,可以将其作为输出返回出来。

def subject(name="python"):
   def pyth():
        return "you are inside in pyth() function"

    def java():
        return "you are inside in java() function"

    if name == "python":
        return pyth
    elif name == "java":
        return java
    else:
        print("没有调用函数里面的函数")


f = subject()   # 返回的是一个函数
print(f)    # <function subject.<locals>.pyth at 0x000001F080F56280>
print(f())  # you are inside in pyth() function

函数作为参数传递

将函数作为另外函数的参数传递

def hello(name="python"):
    return "hello " + name


def param(func):
    print("before hello() function")
    print(func())


param(hello)

装饰器

如果我们要增加其他功能,在函数前打印日志,又不改变函数的定义,就需要用到装饰器。

所以我们定义一个打印日志的decorator

def log(func):
    def wrapper(*args, **kwargs):
        print("call %s()" % func.__name__)
        # func()
        return func(*args, **kwargs)

    return wrapper

使用该decorator

@log
def hi(name="python"):
    print("hi", name)


hi()

我们在调用hi()函数时,不仅会运行hi()函数,也会在前面打印日志。

把@log放到了函数hi()的定义处,相当于执行了 hi = log(hi),把hi()函数作为参数传入log()函数中。

如果装饰器本身需要传入参数,又应该怎么做呢?

def log(text):
    def deco(func):
        def wrapper(*args, **kwargs):
            print("call %s %s()" % (text, func.__name__))
            # func()
            return func(*args, **kwargs)

        return wrapper

    return deco


@log("ex")
def hi(name="python"):
    print("hi", name)


hi()
print(hi.__name__)

经过decorator装饰之后的函数,__name__属性已经发生改变,已经不是hi,而是wrapper,返回的是wrapper()函数的名字。

所以,需要把原始的__name__属性复制到wrapper()函数中。使用Python内置的functools.wraps。

import functools
def log(text):
    def deco(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print("call %s %s()" % (text, func.__name__))
            # func()
            return func(*args, **kwargs)

        return wrapper

    return deco


@log("ex")
def hi(name="python"):
    print("hi", name)


hi()
print(hi.__name__)

@functools.wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

posted @ 2021-08-05 11:20  rissa  阅读(42)  评论(0)    收藏  举报

记录学习笔记,会有很多是参考重复,如有侵权,联系删除