6_Python进阶_装饰器

装饰器

装饰器(Decorators)是Python的⼀个重要部分。
装饰器是修改其他函数的功能的函数。

  • 有助于让我们的代码更简短;
  • 有助于让我们的代码更Pythonic

一切皆对象

# 定义函数
def hi(name="鲁班7号"):
    string = "你好," + name + "!"
    return string


print(hi())

返回的结果是如下。

你好,鲁班7号!

Process finished with exit code 0

在Python中可以将一个函数赋给一个变量,如下所示。

hello = hi
print(hello())

你好,鲁班7号!

Process finished with exit code 0
  1. 变量=函数名,可以用这个变量加上括号,使它具有函数功能;
  2. 变量=函数名,相当于“复制”原来的函数的内容给现在的变量,形成新的“函数”。当删除旧函数时,变量无影响。
  3. 变量=函数名+括号,变量实际就是函数的返回值;
# 把原来的函数删去
# 当删除旧函数时,变量无影响。
del hi
print(hello())

# 已删去的函数无法再调用
print(hi())

你好,鲁班7号!
Traceback (most recent call last):
  File "C:/Users/aw/PycharmProjects/AdvancedPython/Decoration.py", line 17, in <module>
    print(hi())
NameError: name 'hi' is not defined

Process finished with exit code 1

在函数中定义函数

我们可以创建嵌套的函数。

def Function1():
    print("This is Function1's scope of execution")

    def Function1_1():
        print("This is Function1_1's scope of execution")

        def Function1_1_1():
            print("This is Function1_1_1's scope of execution")

        def Function1_1_2():
            print("This is Function1_1_2's scope of execution")

        Function1_1_1()
        Function1_1_2()

    def Function1_2():
        print("This is Function1_2's scope of execution")

    print("This is Function1's scope of execution")
    Function1_1()
    Function1_2()

Function1()

返回的结果是如下。

This is Function1's scope of execution
This is Function1's scope of execution
This is Function1_1's scope of execution
This is Function1_1_1's scope of execution
This is Function1_1_2's scope of execution
This is Function1_2's scope of execution

但是无法调用函数里面的函数

Function1_1_1()

Traceback (most recent call last):
  File "C:/Users/aw/PycharmProjects/AdvancedPython/Decoration.py", line 45, in <module>
    Function1_1_1()
NameError: name 'Function1_1_1' is not defined

在函数中返回函数

def Hi(name="鲁班7号"):
    def FuncA():
        return "出电刀。"

    def FuncB():
        return "出影刃。"

    if name == "鲁班7号":
        res = FuncA
    else:
        res = FuncB

    return res

# 把“主函数”赋给变量,print的是赋给变量的函数在内存中的地址
a = Hi
print(a)
# 把“主函数”的返回值赋给变量,print的确实是返回值
# 但是这个返回值还是一个函数,因此返回的是这个函数的地址
a = Hi()
print(a)
# 把“主函数”的返回值函数的结果赋给变量,print的确实是返回值
# 这个返回值是一个文本
a = Hi()()
print(a)
# 修改“主函数”里面的传入参数值,可以得到不一样的效果
a = Hi(name="伽罗")()
print(a)

返回的结果如下。

<function Hi at 0x000001DC020FEDC0>
<function Hi.<locals>.FuncA at 0x000001DC020FED30>
出电刀。
出影刃。

Process finished with exit code 0

将函数作为参数传给另⼀个函数

# 将函数作为参数传递给另一个函数
def Func1():
    return "攻速鞋"


def Func2(Func1):
    print("我在玩鲁班七号")
    print(Func1())


Func2(Func1)

返回的结果如下。

我在玩鲁班七号
攻速鞋

Process finished with exit code 0

第一个装饰器

# 第一个装饰器
def DecoratingFunction():
    print("可爱的鲁班七号!")


def Decorator(DecoratingFunction):
    def WrapTheFunction():
        print("正在包装函数!")
        DecoratingFunction()
        print("包装函数结束!")

    return WrapTheFunction


DecoratingFunction = Decorator(DecoratingFunction)

DecoratingFunction()

输出的结果是:

正在包装函数!
可爱的鲁班七号!
包装函数结束!

Process finished with exit code 0

最后是采用@的方式进行书写。

@Decorator
# 第一个装饰器
def DecoratingFunction():
    print("可爱的鲁班七号!")

DecoratingFunction()

输出的结果也是一样的。

正在包装函数!
可爱的鲁班七号!
包装函数结束!

Process finished with exit code 0

注意

print(DecoratingFunction.__name__)

WrapTheFunction

Process finished with exit code 0

这是因为装饰器重写了我们函数的名字和注释⽂档(docstring)。
解决方法,使用functools包引进wraps
@wraps(函数名)接受⼀个函数来进⾏装饰,并加⼊了复制函数名称、注释⽂档、参数列表等等的功能。这可以让我们在装饰器⾥⾯访问在装饰之前的函数的属性。

from functools import wraps


def Decorator(DecoratingFunction):
    @wraps(DecoratingFunction)
    def WrapTheFunction():
        print("正在包装函数!")
        DecoratingFunction()
        print("包装函数结束!")

    return WrapTheFunction


@Decorator
# 第一个装饰器
def DecoratingFunction():
    print("可爱的鲁班七号!")


DecoratingFunction()

print(DecoratingFunction.__name__)

输出的结果是一样的了。

正在包装函数!
可爱的鲁班七号!
包装函数结束!
DecoratingFunction

装饰器的规范模板

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,因此can_run==False的时候,第二个return不会返回
	return decorated
	
@decorator_name
def func():
	return("Function is running")

装饰器的使用场景

授权(Authorization)

装饰器能有助于检查某个⼈是否被授权去使⽤⼀个web应⽤的端点(endpoint)。
它们被⼤量使⽤于FlaskDjango web框架中。

from functools import wraps


def RequiresAuthorization(Endpoint):
    @wraps(Endpoint)
    def decorating(*args, **kwargs):
        auth_state = request.authorization
        if not auth_state or not check_auth(auth_state.name, auth_state.password):
            authenticate()
        return Endpoint(*args, **kwargs)

    return decorating

日志(Logging)

from functools import wraps


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

    return with_logging


@logging
def add_math_function(x):
    return x + x

print(add_math_function(2))

输出的结果如下。

add_math_function was called.
4

Process finished with exit code 0

日志延申——在函数中嵌入装饰器

from functools import wraps
import time


def logging(func, logfile='FunctionOutput.log'):
	# FunctionOutput.log是待会要写进日志数据的文件
    @wraps(func)
    def with_logging(*args, **kwargs):
        logfile_string = func.__name__ + " was called." + str(time.time()) + '\n'
        print(logfile_string)
        # 打开日志文件,生成句柄,进行日志书写
        with open(logfile, 'a') as opened_file:
            opened_file.write(logfile_string)
        # 写完后进行被装饰函数的功能执行
        return func(*args, **kwargs)

    return with_logging


@logging
def add_math_function(x):
    return x + x


print(add_math_function(2))

返回的结果是:

add_math_function was called.1656740346.1487005

4

Process finished with exit code 0

在PyCharm编译器下面看到生成了日志文件FunctionOutput.log
重复运行三次程序,可以看到,程序执行的函数名和执行时间均被记录了。

add_math_function was called.1656740285.4093149
add_math_function was called.1656740332.3984892
add_math_function was called.1656740346.1487005

装饰器类

类也可以⽤来构建装饰器。
以类的形式重新构建上述日志生成程序。
场景:你想把引起你注意的问题发送到⼀个email,同时也保留⽇志,留个记录。

from functools import wraps
import time


class logging(object):
    def __init__(self, logfile="FunctionOutput.log"):
        """
        初始化日志文件名。
        :param logfile: 日志文件名。
        """
        self.logfile = logfile

    def __call__(self, func, *args, **kwargs):
        @wraps(func)
        def with_logging(*args, **kwargs):
            logfile_string = func.__name__ + " was called. " + str(time.time()) + '\n'
            print(logfile_string)
            with open(self.logfile, 'a') as opened_file:
                opened_file.write(logfile_string)
            self.send_email()
            return func(*args, **kwargs)

        return with_logging

    def send_email(self):
        print("Sending Email ing...")
        pass


# 对装饰器类进行实例化操作
logging = logging()


@logging
def add_math_function(x):
    return x + x


print(add_math_function(2))

输出的结果是:

add_math_function was called. 1656741691.0767632

Sending Email ing...
4

Process finished with exit code 0

打开日志文件,程序运行了两次,可看到。

add_math_function was called. 1656741617.9853013
add_math_function was called. 1656741691.0767632
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ctrl+Alt+L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值