python中的装饰器该如何使用

目录

1. 需求是怎么来的

装饰器的定义很是抽象,我们来看一个小例子。

def foo():
    print('in foo()')
foo()

这是一个很无聊的函数没错。但是突然有一个更无聊的人,我们称呼他为B君,说我想看看执行这个函数用了多长时间,好吧,那么我们可以这样做:

import time

def foo():
    start = time.time()
    print('in foo()')
    time.sleep(2)
    end = time.time()
    print(f'used:{end - start}')

foo()

很好,功能看起来无懈可击。可是蛋疼的B君此刻突然不想看这个函数了,他对另一个叫foo2的函数产生了更浓厚的兴趣。

怎么办呢?如果把以上新增加的代码复制到foo2里,这就犯了大忌了~复制什么的难道不是最讨厌了么!而且,如果B君继续看了其他的函数呢?

2. 以不变应万变,是变也

还记得吗,函数在Python中是一等公民,那么我们可以考虑重新定义一个函数timeit,将foo的引用传递给他,然后在timeit中调用foo并进行计时,这样,我们就达到了不改动foo定义的目的,而且,不论B君看了多少个函数,我们都不用去修改函数定义了!

import time

def foo():
    print('in foo()')

def timeit(func):
    start = time.time()
    func()
    time.sleep(2)
    end = time.time()
    print('used:', end - start)

timeit(foo)

看起来逻辑上并没有问题,一切都很美好并且运作正常!……等等,我们似乎修改了调用部分的代码。原本我们是这样调用的:foo(),修改以后变成了:timeit(foo)。这样的话,如果foo在N处都被调用了,你就不得不去修改这N处的代码。或者更极端的,考虑其中某处调用的代码无法修改这个情况,比如:这个函数是你交给别人使用的。

3. 最大限度地少改动

既然如此,我们就来想想办法不修改调用的代码;如果不修改调用代码,也就意味着调用foo()需要产生调用timeit(foo)的效果。我们可以想到将timeit赋值给foo,但是timeit似乎带有一个参数……想办法把参数统一吧!如果timeit(foo)不是直接产生调用效果,而是返回一个与foo参数列表一致的函数的话……就很好办了,将timeit(foo)的返回值赋值给foo,然后,调用foo()的代码完全不用修改!

# -*- coding: UTF-8 -*-
import time

def foo():
    print('in foo()')

# 定义一个计时器,传入一个,并返回另一个附加了计时功能的方法
def timeit(func):
    # 定义一个内嵌的包装函数,给传入的函数加上计时功能的包装
    def wrapper():
        start = time.time()
        func()
        time.sleep(2)
        end = time.time()
        print('used:', end - start)

    # 将包装后的函数返回
    return wrapper

foo = timeit(foo)
foo()

这样,一个简易的计时器就做好了!我们只需要在定义foo以后调用foo之前,加上foo = timeit(foo),就可以达到计时的目的,这也就是装饰器的概念,看起来像是foo被timeit装饰了。在在这个例子中,函数进入和退出时需要计时,这被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。与传统编程习惯的从上往下执行方式相比较而言,像是在函数执行的流程中横向地插入了一段逻辑。在特定的业务领域里,能减少大量重复代码。面向切面编程还有相当多的术语,这里就不多做介绍,感兴趣的话可以去找找相关的资料。

这个例子仅用于演示,并没有考虑foo带有参数和有返回值的情况,完善它的重任就交给你了 :)

上面这段代码看起来似乎已经不能再精简了,Python于是提供了一个语法糖来降低字符输入量。

import time

def timeit(func):
    def wrapper():
        start = time.time()
        func()
        time.sleep(2)
        end = time.time()
        print('used:', end - start)

    return wrapper

@timeit
def foo():
    print('in foo()')

foo()

重点关注第11行的@timeit,在定义上加上这一行与另外写foo = timeit(foo)完全等价,千万不要以为@有另外的魔力。除了字符输入少了一些,还有一个额外的好处:这样看上去更有装饰器的感觉。

看到这里其实你也明白了,python 中的装饰器本质上就是一个函数,这个函数接收其他的函数作为参数,并将其以一个全新的修改后的函数替换它。

4.对带参数的函数使用装饰器

如果要包装的函数有参数,也不麻烦,只要内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象就可以啦

import datetime,time

def out(func):
    def inner(*args):
        start = datetime.datetime.now()
        func(*args)
        end = datetime.datetime.now()
        print(end-start)
        print("out and inner")
    return inner

@out
def myfunc(*args):
    time.sleep(1)
    print("args is{}".format(args))

myfunc("lalalal")

5. 给装饰器参数

给装饰器传参也不难,和上一示例相比在外层多了一层包装而已

#coding:utf-8
def outermost(*args):
	def out(func):
		print ("装饰器参数{}".format(args))
		def inner(*args):
			print("innet start")
			func(*args)
			print ("inner end")
		return inner
	return out

@outermost(666)
def myfun(*args):
	print ("试试装饰器和函数都带参数的情况,被装饰的函数参数{}".format(args))

myfun("zhangkun")

6.带类参数的装饰器

参数是什么类型其实都不影响的,你看,参数是个类也一样的

class locker:
    def __init__(self):
        print("locker.__init__() should be not called")

    @staticmethod
    def acquire():
        print("locker.acquire() static method be called")

    @staticmethod
    def release():
        print("locker.release() static method be called")

def outermost(cls):
    def out(func):
        def inner():
            cls.acquire()
            func()
            cls.release()
        return inner
    return out

@outermost(locker)
def myfunc():
    print("myfunc called")

myfunc()

7. 对一个函数应用多个装饰器

一个函数可以拥有多个装饰器,但是要注意顺序

class mylocker:
    def __init__(self):
        print("mylocker.__init__() called.")

    @staticmethod
    def acquire():
        print("mylocker.acquire() called.")

    @staticmethod
    def unlock():
        print("  mylocker.unlock() called.")

class lockerex(mylocker):
    @staticmethod
    def acquire():
        print("lockerex.acquire() called.")

    @staticmethod
    def unlock():
        print("  lockerex.unlock() called.")

def lockhelper(cls):
    def _deco(func):
        def __deco2(*args, **kwargs):
            print("before %s called." % func.__name__)
            cls.acquire()
            try:
                return func(*args, **kwargs)
            finally:
                cls.unlock()
        return __deco2
    return _deco

class example:
    @lockhelper(mylocker)
    @lockhelper(lockerex)
    def myfunc2(self, a, b):
        print(" myfunc2() called.")
        print(a+b)

a = example()
a.myfunc2(1,2)

8. 作为一个类

虽然装饰器几乎总是可以用函数实现,但是在某些情况下,使用用户自定义的类可能会更好

import time

class DerocatorAsClass:
    def __init__(self,funcation):
        self.funcation = funcation

    def __call__(self, *args, **kwargs):
        # 调用函数之前,做点什么
        result = self.funcation(*args,**kwargs)
        print('3333333333')
        # 在调用之后做点什么并且返回结果
        return result

@DerocatorAsClass
def foo():
    print('in foo()')

foo()

如上例,用类作为装饰器也是很方便的

以上就是python中的装饰器该如何使用的详细内容,更多关于python 装饰器的使用的资料请关注我们其它相关文章!

(0)

相关推荐

  • python 装饰器重要在哪

    1.什么是装饰器? 要理解什么是装饰器,您首先需要熟悉Python处理函数的方式.从它的观点来看,函数和对象没有什么不同.它们有属性,可以重新分配: def func(): print('hello from func') func() > hello from func new_func = func new_func() > hello from func print(new_func.__name__) > func 此外,你还可以将它们作为参数传递给其他函数: def func(

  • Python必备基础之闭包和装饰器知识总结

    一.闭包 1.1 三要素 必须有一个内嵌函数 内嵌函数必须引用外部函数中变量 外部函数返回值必须是内嵌函数 1.2 语法 # 语法 def 外部函数名(参数): 外部变量 def 内部函数名(参数): 使用外部变量 return 内部函数名 # 调用 变量 = 外部函数名(参数) 变量(参数) 举个例子 def func01(): # 外部函数 a = 1 # 外部变量 print('外部变量:',a) def func02(num): #内部函数 print("调用内部函数后:",n

  • 如何正确理解python装饰器

    一.闭包 要想了解装饰器,首先要了解一个概念,闭包.什么是闭包,一句话说就是,在函数中再嵌套一个函数,并且引用外部函数的变量,这就是一个闭包了.光说没有概念,直接上一个例子. def outer(x): def inner(y): return x + y return inner print(outer(6)(5)) ----------------------------- >>>11 如代码所示,在outer函数内,又定义了一个inner函数,并且inner函数又引用了外部函数ou

  • Python高阶函数与装饰器函数的深入讲解

    本文主要介绍的是Python高阶函数与装饰器函数的相关内容,分享给大家,下面话不多说了,来一起看看详细的介绍吧 高阶函数 1.可以使用函数对象作为参数的函数 2.或可以将函数作为返回值的函数 3.函数对象:定义好的函数,使用函数名调用(不要加括号) #将函数作为参数的高阶函数,通过传入不同的函数,可以使执行的结果不同 4.内置高阶函数 (1)map数据映射函数 map函数接收的是两个参数,一个函数,一个序列,其功能是将序列中的值处理再依次返回至列表内.其返回值为一个迭代器对象 (2)reduce

  • python 装饰器的使用与要点

    一.装饰器使用场景 经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用. 概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. 二.为什么需要装饰器 1.先来看一个简单例子: def foo(): print('i am foo') 2.增加需求 现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加日志代码: def foo(): print

  • 详解Python模块化编程与装饰器

    我们首先以一个例子来介绍模块化编程的应用场景,有这样一个名为requirements.py的python3文件,其中两个函数的作用是分别以不同的顺序来打印一个字符串: # requirements.py def example1(): a = 'hello world!' print (a) print (a[::-1]) def example2(): b = 'hello again!' print (b) print (b[::-1]) if __name__ == '__main__':

  • Python pytest装饰器总结(实例详解)

    几个常用装饰器 pytest.ini 配置文件 例子: [pytest] addopts = -v -s --html=py_test/scripts/report/report.html -p no:warnings --reruns=10 testpaths = ./py_test/scripts python_files= test_rerun.py python_classes = Test* python_function = test* xfail_strict = true add

  • Python 中的函数装饰器和闭包详解

    函数装饰器可以被用于增强方法的某些行为,如果想自己实现装饰器,则必须了解闭包的概念. 装饰器的基本概念 装饰器是一个可调用对象,它的参数是另一个函数,称为被装饰函数.装饰器可以修改这个函数再将其返回,也可以将其替换为另一个函数或者可调用对象. 例如:有个名为 decorate 的装饰器: @decorate def target(): print('running target()') 上述代码的写法和以下写法的效果是一样的: def target(): print('running targe

  • Python 的lru_cache装饰器使用简介

    Python 的 lru_cache 装饰器是一个为自定义函数提供缓存功能的装饰器.其内部会在下次以相同参数调用该自定义函数时直接返回计算好的结果.通过缓存计算结果可以很好地提升性能. 1 从示例说起 假设我们有一个计算斐波那契数列的求和函数,其内部采用递归方式实现. from xxx.clock_decorator import clock @clock def fibonacci(n): if n<2: return n return fibonacci(n-2)+fibonacci(n-1

  • python装饰器代码深入讲解

    python装饰器就是用于扩展原函数功能的一种函数,这个函数特殊的地方就是它的返回值也是一个函数,使用Python装饰器的一个好处就是:在不需要修改原函数代码的情况下,给函数增加新的功能. 先来看个例子: def say(): print('Nice day') say() # 这个函数的输出为: Nice day 现在,我想在输出Nice day的前面再打印一行****************,类似下面的效果: **************** Nice day 一般情况下,我可以修改上面的代

  • python 装饰器的基本使用

    知识点 简单的装饰器 带有参数的装饰器 带有自定义参数的装饰器 类装饰器 装饰器嵌套 @functools.wrap装饰器使用 基础使用 简单的装饰器 def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper() def test(): print('test done.') test = my_decorator(test) test 输出: wrapper of dec

随机推荐