Python装饰器
发表于:2022-08-26 | 分类: python
字数统计: 1.4k | 阅读时长: 6分钟 | 阅读量:

Python 装饰器

装饰器

在Python中,函数也是一个对象,可以被当做参数传递,可以在另一个函数中调用,也可以实现函数的嵌套。

所以,利用这个特性,我们可以写出一段测试功能运行时长的代码。

import time


def function():
    print("I am function!")
    time.sleep(3)
    print("I have finished my work!")


def index(func):
    startTime = time.time()
    func()
    enfTime = time.time()
    print("script spend time is", enfTime - startTime)


index(function)

这样的想法乍一看很好,但其实这从本质上看,不是改变了function函数的功能,而是创建了一个新的函数,而后将function融合进了这个函数里,最终实际上对function函数的一个包装,使它成为了一个新的函数,来实现功能。

那么,如果我们想要直接调用function函数就能实现像程序计时这样的功能呐?(要注意,我们做的一切都要建立在不更改function函数任何代码的前提之下。)

Python中有装饰器可以帮助我们更好的实现这样的功能。

装饰器的本质还是函数,他的作用是去装饰另外一个函数,而在程序计时这样的场景里,我们就需要根据我们要计时的程序,去编写一个计时的函数,这个计时的函数就是装饰器。

import time


def index(func):
    def test_time():
        print("I doing some work")
        func()
        print("I am end!")

    return test_time


@index
def function():
    print("I am function!")
    time.sleep(3)
    print("I have finished my work!")


function()

我们使用了装饰器的语法糖,@加函数名的形式,这其实就相当于一个index(function),但是因为他的本质是装饰,因此在function不执行的情况下,程序也不会执行。

最后我们直接调用function函数,就能顺利的完成程序计时的功能。

这看起来似乎还有一点问题,但其实是因为此处应用的场景有一点点问题,作为程序计时来说,并不需要把这个功能焊死在函数里,但是这样的使用,似乎把计时焊死在了原来的函数内部。

其实换一个应用场景就好了,例如在flask等web框架里,我们针对用户授权的时候,一个小小的装饰器就能解决。

装饰器带参数

首先是,装饰的对象带参数。

image-20220826120832286

如图,尽管我已经在调用的时候加入了参数,但是最终依然报错了。

image-20220826120904793

因为在装饰器中,我运行了这个函数,但是此时我是没有带参数的。

image-20220826120944616

并且值得注意的是,函数的参数可以很长,因为如果我们直接写参数的话,好像有点不对劲。

于是我们简单的使用一样python中的动态参数。

image-20220826121202910

这其实很简单,不用动态参数,你手动加入参数也可以。

import time


def index(func):
    def test_time(*args, **kwargs):
        print("I doing some work")
        func(*args, **kwargs)
        print("I am end!")

    return test_time


@index
def function(x, y):
    print("I am function!")
    time.sleep(3)
    print("I have finished my work!")
    print(x + y)


function(2, 3)

装饰器带参数

这是另外一个问题,我们希望给装饰器带上参数,使得它使用起来更加的灵活,如果你见过flask的代码,那么就应该见过装饰器带参数的情况。、

import time


def index(url):
    def test_time(func):
        def Timer(*args, **kwargs):
            print("I doing some work")
            func(*args, **kwargs)
            print("I request the ", url)
            print("I am end!")

        return Timer
    return test_time


@index("/home")
def function(x, y):
    print("I am function!")
    time.sleep(3)
    print("I have finished my work!")
    print(x + y)


function(2, 4)

如上,这就是一个带参数的装饰器。

你可能发现了装饰器上三层的函数嵌套,并且写出了内两层的返回值,这是不可缺少的。

我们将它写成最开始的形式;

import time


def function(x, y):
    print("I am function!")
    time.sleep(3)
    print("I have finished my work!")
    print(x + y)


def index(url, func, *args, **kwargs):
    startTime = time.time()
    func(*args, **kwargs)
    enfTime = time.time()
    print("script spend time is", enfTime - startTime)
    print("I request the ", url)
    return index


index("home", function, 2, 5)

可以发现此时再使用这种形式,整个函数的调用就变得异常的繁琐和麻烦了起来。

类装饰器

或许叫他装饰器类更加合适,比起一般的函数形式的装饰器,

import time


class Foo(object):
    def __init__(self, url):
        self._url = url

    def __call__(self, func):
        def wrap(*args,**kwargs):
            print("func is starting!")
            func(*args, **kwargs)
            print("I request the " + self._url)
            print("func is ending!")
        return wrap


@Foo("home")
def function(x, y):
    print("I am function!")
    time.sleep(3)
    print("I have finished my work!")
    print(x+y)


function(4, 6)

装饰器嵌套

class Foo01(object):
    def __init__(self, url):
        self._url = url

    def __call__(self, func):
        def wrap(*args, **kwargs):

            func(*args, **kwargs)
            print("I request the " + self._url)


        return wrap


class Foo02(object):
    def __init__(self, url):
        self._url = url

    def __call__(self, func):
        def wrap(*args, **kwargs):

            func(*args, **kwargs)
            print("The IP" + self._url)


        return wrap


class Foo03(object):
    def __init__(self, url):
        self._url = url

    def __call__(self, func):
        def wrap(*args, **kwargs):

            func(*args, **kwargs)
            print("The UA" + self._url)


        return wrap


@Foo03("{'User_Agent':'*****'}")
@Foo02("192.168.121.130")
@Foo01("home")
def function(x, y):
    print(x + y)


function(4, 6)

image-20220826125058086

由此可以发现,装饰器的运行是按照和函数的距离来决定的,越近就越先执行。

上一篇:
springboot+vue(2)
下一篇:
springboot+vue(1)