python 装饰器的使用与要点

一、装饰器使用场景

经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

二、为什么需要装饰器

1、先来看一个简单例子:

def foo():
print('i am foo')

2、增加需求

现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加日志代码:

def foo():
    print('i am foo')
    print("foo is running")

3、又有需求

假设现在有100个函数需要增加这个需求,并且后续可能还要对这一百个函数都增加执行前打印日志的需求,怎么办?还一个个改吗?

当然不了,这样会造成大量雷同的代码,为了减少重复写代码,我们可以这样做,重新定义一个函数:专门处理日志 ,日志处理完之后再执行真正的业务代码。

def use_logging(func):
    print("%s is running" % func.__name__)
    func()

def bar():
    print('i am bar')

use_logging(bar)
运行结果:
#bar is running
#i am bar

函数use_logging就是装饰器,它把执行真正业务方法的func包裹在函数里面,看起来像bar被use_logging装饰了。在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。

通过以上use_logging函数我们增加了日志功能,不管以后有多少函数需要增加日志或者修改日志的格式我们只需要修改use_logging函数,并执行use_logging(被装饰的函数)就达到了我们想要的效果。

def use_logging(func):
    print("%s is running" % func.__name__)
    return func

@use_logging
def bar():
    print('i am bar')

bar()

三、基础装饰器入门

1、装饰器语法糖

python提供了@符号作为装饰器的语法糖,使我们更方便的应用装饰函数;但使用语法糖要求装饰函数必须return一个函数对象。因此我们将上面的func函数使用内嵌函数包裹并return。

装饰器相当于执行了装饰函数use_loggin后又返回被装饰函数bar,因此bar()被调用的时候相当于执行了两个函数。等价于use_logging(bar)()

def use_logging(func):
    def _deco():
        print("%s is running" % func.__name__)
        func()
    return _deco

@use_logging
def bar():
    print('i am bar')

bar()

2、对带参数的函数进行装饰

现在我们的参数需要传入两个参数并计算值,因此我们需要对内层函数进行改动传入我们的两个参数a和b,等价于use_logging(bar)(1,2)

def use_logging(func):
    def _deco(a,b):
        print("%s is running" % func.__name__)
        func(a,b)
    return _deco

@use_logging
def bar(a,b):
    print('i am bar:%s'%(a+b))

bar(1,2)

我们装饰的函数可能参数的个数和类型都不一样,每一次我们都需要对装饰器做修改吗?这样做当然是不科学的,因此我们使用python的变长参数*args和**kwargs来解决我们的参数问题。

3、函数参数数量不确定

不带参数装饰器版本,这个格式适用于不带参数的装饰器。

经过以下修改,我们已经适应了各种长度和类型的参数。这个版本的装饰器可以装饰任意类型的无参数函数。

def use_logging(func):
    def _deco(*args,**kwargs):
        print("%s is running" % func.__name__)
        func(*args,**kwargs)
    return _deco

@use_logging
def bar(a,b):
    print('i am bar:%s'%(a+b))
@use_logging
def foo(a,b,c):
    print('i am bar:%s'%(a+b+c))

bar(1,2)
foo(1,2,3)

4、装饰器带参数

带参数的装饰器,这个格式适用于带参数的装饰器。

某些情况我们需要让装饰器带上参数,那就需要编写一个返回一个装饰器的高阶函数,写出来会更复杂。比如:

#! /usr/bin/env python
# -*- coding:utf-8 -*-
# __author__ = "TKQ"

def use_logging(level):
    def _deco(func):
        def __deco(*args, **kwargs):
            if level == "warn":
                print "%s is running" % func.__name__
            return func(*args, **kwargs)
        return __deco
    return _deco

@use_logging(level="warn")
def bar(a,b):
    print('i am bar:%s'%(a+b))

bar(1,3)

# 等价于use_logging(level="warn")(bar)(1,3)

5、functools.wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看例子:

def use_logging(func):
    def _deco(*args,**kwargs):
        print("%s is running" % func.__name__)
        func(*args,**kwargs)
    return _deco

@use_logging
def bar():
    print('i am bar')
    print(bar.__name__)

bar()

#bar is running
#i am bar
#_deco
#函数名变为_deco而不是bar,这个情况在使用反射的特性的时候就会造成问题。因此引入了functools.wraps解决这个问题。

使用functools.wraps:

import functools
def use_logging(func):
    @functools.wraps(func)
    def _deco(*args,**kwargs):
        print("%s is running" % func.__name__)
        func(*args,**kwargs)
    return _deco

@use_logging
def bar():
    print('i am bar')
    print(bar.__name__)

bar()

#result:
#bar is running
#i am bar
#bar  ,这个结果是我们想要的。OK啦!

6、实现带参数和不带参数的装饰器自适应

import functools

def use_logging(arg):
    if callable(arg):#判断参入的参数是否是函数,不带参数的装饰器调用这个分支
        @functools.wraps(arg)
        def _deco(*args,**kwargs):
            print("%s is running" % arg.__name__)
            arg(*args,**kwargs)
        return _deco
    else:#带参数的装饰器调用这个分支
        def _deco(func):
            @functools.wraps(func)
            def __deco(*args, **kwargs):
                if arg == "warn":
                    print "warn%s is running" % func.__name__
                return func(*args, **kwargs)
            return __deco
        return _deco

@use_logging("warn")
# @use_logging
def bar():
    print('i am bar')
    print(bar.__name__)

bar()

三、类装饰器

使用类装饰器可以实现带参数装饰器的效果,但实现的更加优雅简洁,而且可以通过继承来灵活的扩展.

1、类装饰器

class loging(object):
    def __init__(self,level="warn"):
        self.level = level

    def __call__(self,func):
        @functools.wraps(func)
        def _deco(*args, **kwargs):
            if self.level == "warn":
                self.notify(func)
            return func(*args, **kwargs)
        return _deco

    def notify(self,func):
        # logit只打日志,不做别的
        print "%s is running" % func.__name__

@loging(level="warn")#执行__call__方法
def bar(a,b):
    print('i am bar:%s'%(a+b))

bar(1,3)

2、继承扩展类装饰器

class email_loging(Loging):
    '''
    一个loging的实现版本,可以在函数调用时发送email给管理员
    '''
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(email_loging, self).__init__(*args, **kwargs)

    def notify(self,func):
        # 发送一封email到self.email
        print "%s is running" % func.__name__
        print "sending email to %s" %self.email

@email_loging(level="warn")
def bar(a,b):
    print('i am bar:%s'%(a+b))

bar(1,3)

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

(0)

相关推荐

  • python装饰器代码深入讲解

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

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

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

  • Python classmethod装饰器原理及用法解析

    英文文档: classmethod(function) Return a class method for function. A class method receives the class as implicit first argument, just like an instance method receives the instance. To declare a class method, use this idiom: class C: @classmethod def f(c

  • 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高级语法之闭包和装饰器详解

    一.闭包 闭包的形成条件: 1.函数嵌套. 2.内部函数使用了外部函数的变量或者参数. 3.外部函数返回了使用外 部变量的内部函数. 二.一个简单的例子 def func_out(num1): def inner(num2): res = num1 + num2 print(res) return inner # a = func_out(10)(10) a = func_out(10) a(10) 闭包修改外部函数的变量: 在闭包内修改外部函数的变量需要使用nonlocal关键字 def fu

  • 详解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装饰器原理

    作用 装饰器可以用于用于装饰一个函数或方法,使得在不修改原函数.方法代码的前提下,为方法添加前置或后置操作: 例如突然想要计算一下各个函数的执行时间,又不希望在每一个函数中添加tim.time()来计算执行时间 用法 装饰器的写法网上很多,但是我觉得还是尽量先理解,再知道怎么写会比较好,所以会先说如何理解,在后面重写用法 实现 了解装饰器是如何实现的,远比会写装饰器更重要,简单的说装饰器就是接收一个函数对象,然后先执行前置操作,再执行函数,再执行后置操作: 这么说可能有些抽象,或者举一个不那么恰

  • 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.先来看一个简单例子: def foo(): print('i am foo') 2.增加需求 现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加日志代码: def foo(): print

  • python装饰器实例大详解

    一.作用域 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我们要理解两点: a.在全局不能访问到局部定义的变量 b.在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改) 下面我们来看看下面实例: x = 1 def funx(): x = 10 print(x) # 打印出10 funx() print(x) # 打印出1 如果局部没有定义变量x,那么函数内部会从内往

  • Python 装饰器深入理解

    讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它变得更厚更长,这样一来,它不仅有遮羞功能,还能提供保暖,不过有个问题,这个内裤被我们改造成了长裤后,虽然还有遮羞功能,但本质上它不再是一条真正的内裤了.于是聪明的人们发明长裤,在不影响内裤的前提下,直接把长裤套在了内裤外面,这样内裤还是内裤,有了长裤后宝宝再也不冷了.装饰器就像我们这里说的长裤,在不

  • 使用Python装饰器在Django框架下去除冗余代码的教程

    Python装饰器是一个消除冗余的强大工具.随着将功能模块化为大小合适的方法,即使是最复杂的工作流,装饰器也能使它变成简洁的功能. 例如让我们看看Django web框架,该框架处理请求的方法接收一个方法对象,返回一个响应对象: def handle_request(request): return HttpResponse("Hello, World") 我最近遇到一个案例,需要编写几个满足下述条件的api方法: 返回json响应 如果是GET请求,那么返回错误码 做为一个注册api

  • 深入理解Python装饰器

    装饰器简介: 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 装饰器最早在Python 2.5中出现,它最初被用于加工函数和方法这样的可调用对象(callable object,这样的对象定义有__call__方法).在Python 2

  • python装饰器初探(推荐)

    一.含有一个装饰器 #encoding: utf-8 ############含有一个装饰器######### def outer(func): def inner(*args, **kwargs):#要装饰f1(),这里用这俩形式参数,可以接受任意个参数,不管f1定义几个参数 print "1" r = func(*args, **kwargs)#这里要用func,不要用f1 print "2" return r return inner @outer #这里ou

  • 深入浅出分析Python装饰器用法

    本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 用类作为装饰器 示例一 最初代码: class bol(object): def __init__(self, func): self.func = func def __call__(self): return "<b>{}</b>".format(self.func()) class ita(object): def __init__(self, func): self.func = f

  • Python装饰器基础详解

    装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 前面快速介绍了装饰器的语法,在这里,我们将深入装饰器内部工作机制,更详细更系统地介绍装饰器的内容,并学习自己编写新的装饰器的更多高级语法. 什么是装饰器 装饰是为函数和类指定管理代码的一种

  • python装饰器与递归算法详解

    1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说明一下: 小P闲来无事,随便翻看自己以前写的一些函数,忽然对一个最最最基础的函数起了兴趣: def sum1(): sum = 1 + 2 print(sum) sum1() 此时小P想看看这个函数执行用了多长时间,所以写了几句代码插进去了: import time def sum1(): star

  • Python装饰器实现几类验证功能做法实例

    最近新需求来了,要给系统增加几个资源权限.尽量减少代码的改动和程序的复杂程度.所以还是使用装饰器比较科学 之前用了一些登录验证的现成装饰器模块.然后仿写一些用户管理部分的权限装饰器. 比如下面这种 def permission_required(permission): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.can(permission): abort(40

随机推荐