乐趣区

关于python:装饰器要这么去使用

装璜器是 python 中很重要的一个常识,然而对于少数老手来说,装璜器有点难以了解,所以看到装璜器就想绕道而走,然而这会给本人留下一个很大的隐患,一旦面试官要你实现一个简略的装璜器的时候,你就会因为这个坑而错失机会。

    那么咱们为什么要应用装璜器呢?因为程序编写考究一个 OCP 准则,也就是开闭准则,回绝已实现性能的代码的批改,举荐代码性能的拓展,所以这也意味着,咱们不可能再去批改曾经实现的性能,而是要去扩大他的性能,因而,咱们须要应用到装璜器来做这个扩大性能。咱们会围绕创立一般装璜器、创立通用装璜器、多个装璜器的装璜、携带参数的装璜器及装璜递归函数来发展话题。

一. 一般装璜器的创立

    首先装璜器的创立须要满足什么条件?装璜器实质上是一个闭包,所以它也合乎闭包的三个个性:

  • 函数嵌套
  • 外部函数须要用到内部函数的变量或者参数
  • 内部函数返回外部函数的函数对象
import time
def fun_out(num):
    def fun_inner():
        begin = time.time()
        print(num)
        end = time.time()
        print('程序执行工夫为 %s' % (end - begin))
    return fun_inner

那么当初这个高级函数就是一个闭包函数了。

那么既然有装璜器的应用,咱们当然也须要一个须要被装璜的指标函数了。

def fun():
    for i in range(200000):
        print(i)

当初筹备工具曾经实现,那么咱们怎么让下面的闭包函数成为一个具备计算程序运行工夫的装璜器呢?

  • 外层函数的参数必须也只能承受须要被装璜的指标函数的函数对象
  • 内层函数中要有外层函数的参数的函数调用
import time
def fun_out(fun):
    def fun_inner():
        begin = time.time()
        res = fun()
        end = time.time()
        print('程序执行工夫为 %s' % (end - begin))
    return fun_inner

当初咱们的这个函数就成为了一个装璜器函数。咱们来调用程序看看这个后果。

import time
def fun_out(fun):
    def fun_inner():
        begin = time.time()
        fun()
        end = time.time()
        print('程序执行工夫为 %s' % (end - begin))
    return fun_inner
def fun():
    for i in range(200000):
        print(i)
f = fun_out(fun)
f()
执行后果:0
.
.
.
199999
程序执行工夫为 0.6352810859680176

    那么这就是咱们的执行后果了,能够看到咱们实现了程序的增加,然而并没有对程序自身性能做更改。然而这么写不是太敌对,如果说应用人员应用这个程序,步骤的增多会升高体验度,同时,这样也会把开发人员的眼光吸引到如何调用装璜器上,而不是针对指标函数做装璜,所以咱们个别是应用语法糖的写法。

import time
def fun_out(fun):
    def fun_inner():
        begin = time.time()
        fun()
        end = time.time()
        print('程序执行工夫为 %s' % (end - begin))
    return fun_inner
@fun_out
def fun():
    for i in range(200000):
        print(i)
fun()
执行后果:0
.
.
.
199999
程序执行工夫为 0.6352810859680176

    这样的应用是更加的便捷的。

二. 通用装璜器的创立

    通用装璜器,即被装璜的函数是否携带参数都能够应用,则是通用装璜器。

import time
def fun_out(fun):
    def fun_inner(*args, **kwargs):
        begin = time.time()
        res = fun(*args, **kwargs)
        end = time.time()
        print('程序执行工夫为 %s' % (end - begin))
        return res
    return fun_inner
@fun_out
def fun1(a, b):
    time.sleep(1)
    return a + b
r = fun1(1, 2)
print(r)
执行后果:程序执行工夫为 1.0004768371582031
3

    咱们给外部函数增加不定长参数,不论指标函数是否携带参数都是能够间接应用此装璜器。

三. 多个装璜器的应用

    大家都晓得了应用一个装璜器,咱们的指标函数是没有问题的,那么如果咱们应用多个装璜器是怎么样的呢?

import time
def fun_out(fun):
    def fun_inner(*args, **kwargs):
        begin = time.time()
        res = fun(*args, **kwargs)
        end = time.time()
        print('程序执行工夫为 %s' % (end - begin))
        return res
    return fun_inner
def login(fun):
    def wrapper(*args, **kwargs):
        print('请先登录')
        res = fun(*args, **kwargs)
        return res
    return wrapper
@fun_out
@login
def fun1(a, b):
    time.sleep(1)
    return a + b
r = fun1(1, 2)
print(r)
运行后果:请先登录
程序执行工夫为 1.0000872611999512
3

    那么大家有没有留神到程序的执行程序问题,装璜器的执行程序是从下往上执行的,并不是咱们设想中的从上往下执行,这个是多个装璜器同时装璜一个指标函数的非凡性质,哪个装璜器离指标函数近,那么那个装璜器就先执行。

四. 携带参数的装璜器的应用

    携带参数的装璜器是一个比拟非凡的装璜器了,当咱们应用的装璜器是携带了参数的时候,咱们必须在装璜器的外层在包裹一层函数才能够。

def logging(sign):
    def fun_out(fun):
        def fun_inner(num1, num2):
            if sign == "+":
                print("正在调用加法运算")
            elif sign == "-":
                print("正在调用减法运算")
            res = fun(num1, num2)
            return res
        return fun_inner
    return fun_out
@logging("+")
def add(a, b):
    result = a + b
    return result
@logging("-")
def sub(a, b):
    result = a - b
    return result
res = add(1, 2)
print(res)
res = sub(1, 2)
print(res)

    其实也是同样的情理,既然装璜器的外层函数只能承受指标函数对象这一个参数,那么咱们就必要要用另外一个函数去承受这个参数才能够,所以间接用一层函数来包裹装璜器就能够了,当然咱们在最初要返回装璜器的外层函数对象。

五. 装璜器装璜递归函数

    递归函数的装璜是一个比拟非凡的景象,装璜器有个机制,当它发现此函数曾经被装璜了之后,那么它就不会在装璜这个函数,而是会间接进行装璜程序,归其起因还是因为装璜器的内存原理。

    装璜器装璜一个函数,会把指标函数的对象保留到开拓的内存空间中,所以当内存空间曾经存在了这个被装璜的指标函数的对象的时候,那么程序会间接报错。所以咱们不可能间接应用装璜器装璜递归函数,而是要应用一个外层函数先包裹住递归函数,让闭包函数在此函数中运行完了之后再完结运行,这个时候,装璜器中就不会呈现反复的函数对象了。

import time
def fun_out(fun):
    def fun_inner(*args, **kwargs):
        begin = time.time()
        res = fun(*args, **kwargs)
        end = time.time()
        print('程序执行工夫为 %s' % (end - begin))
        return res
    return fun_inner
@fun_out
# 求 n 的 m 次幂
def decorator(n, m):
    def fun(n, m):
        time.sleep(1)
        if m == 1:
            return n
        return n * fun(n, m - 1)
    return fun(n, m)
res = decorator(10, 5)
print(res)
执行后果:程序执行工夫为 5.0015575885772705
100000

以这样的形式,咱们就能够实现对递归函数的装璜了。

你学废了吗?

退出移动版