美文网首页
python装饰器

python装饰器

作者: agamgn | 来源:发表于2020-02-11 07:03 被阅读0次

前言

 在项目开发或者面试中,装饰器都是很重要的一个话题。装饰器的强大之处在于它能够在不修改原有业务逻辑的情况下对代码进行拓展,它能够最大程度的对代码进行复用。现在一步一步来理解装饰器。

函数

 在python中一切皆对象,那么函数也不例外,它也可以作为其他函数的返回值,函数的参数。

作为函数的返回值

def foo(num):
    return num+1
def bar():
    return foo
print(bar()(2))#3
#等价于
#print(foo(2))

 在调用函数bar()的返回值是一个函数对象,因为返回的是函数,所以我们可以继续对返回值进行调用(调用就是在函数名后面加()

函数作为参数

 函数也可以作为另一个函数的参数

def foo(num):
    return num+1
def bar(func):
    return func(2)
print(bar(foo))#3

函数 bar 接收一个参数,这个参数是一个可被调用的函数对象,把函数 foo 传递到 bar 中去时,foo 和 fun 两个变量名指向的都是同一个函数对象,所以调用 fun(3) 相当于调用 foo(3)。

函数嵌套

 函数不仅能作为参数和返回值,函数还可以定义在另一个函数中,作为嵌套函数的存在

def outer():
    x=1
    def inner():
        print(x)
    inner()
outer()#1

inner做为嵌套函数,它可以访问外部函数的变量,调用 outer 函数时,发生了3件事:
1、给 变量 x 赋值为1
2、定义嵌套函数 inner,此时并不会执行 inner 中的代码,因为该函数还没被调用,直到第3步
3、调用 inner 函数,执行 inner 中的代码逻辑。
 这里只要在改一下,就成了简单的闭包

闭包

 关于闭包,参考这一篇闭包的理解

def outer():
    x=1
    def inner():
        print(x)
    return inner
print(outer()())#1

 同样是嵌套函数,只是稍改动一下,把局部变量 x 作为参数了传递进来,嵌套函数不再直接在函数里被调用,而是作为返回值返回,这里的 closure就是一个闭包,本质上它还是函数,闭包是引用了自由变量(x)的函数(inner)。

函数装饰器

 基于上面的一些理论,现在终于到了装饰器。装饰器的本质是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象

普通装饰器

 一步一步写装饰器

def foo():
    print("foo")

 现在我们想在不改变上面的函数情况下加上日志输出,应该怎么做:

def foo():
    print("foo")

def outer(func):
    def inner():
        print("logging start")
        func()
        print("logging end")
    return inner
foo=outer(foo)
foo()
# logging start
# foo
# logging end

 在没有修改foo函数里面的任何逻辑,就重新修改了foo的执行效果,现在来分析下他的执行流程。
这里的 outer 函数其实就是一个装饰器,装饰器是一个带有函数作为参数并返回一个新函数的闭包,本质上装饰器也是函数。outer 函数的返回值是 inner 函数,在 inner 函数中,除了执行日志操作,还有业务代码,该函数重新赋值给 foo 变量后,调用 foo() 就相当于调用 inner()。
使用语法糖:python为装饰器提供了@语法糖,它用在函数的定义上:

@outer
def foo():
    print("foo2")
foo()

 这样就省去了手动给foo重新赋值的步骤。

带参数的装饰器

 如果装饰器本身是需要传入参数的,那么这时就需要一个带参数的装饰器了。

def sayHello(hello):
    def wrapper(func):
        def inner(*args,**kw):
            print("logging start")
            print(hello)
            func(*args,**kw)
            print("logging end")
        return inner
    return wrapper
@sayHello("hello")
def agam(name):
    print(name)
agam("agamgn")
# logging start
# hello
# agamgn
# logging end

被多个装饰器装饰

 一个函数是可以被多个装饰器修饰的

def outer1(func):
    def inner(*args,**kw):
        print("outer1 start")
        func(*args,**kw)
        print("outer1 end")
    return inner

def outer2(func):
    def inner(*args,**kw):
        print("outer2 start")
        func(*args,**kw)
        print("outer2 end")
    return inner
@outer1
@outer2
def foo():
    print("foo")
foo()
#outer1 start
#outer2 start
#foo
#outer2 end
#outer1 end

 上面的程序执行的顺序是里到外,所以它等效于:

foo=outer1(outer2(foo))

 类似于Koa框架中的洋葱模型。

类装饰器

普通类装饰器

 除了函数有装饰器外,类也是可以作为装饰器的,其实只要是一个“可被调用(callable)的对象则可以被当作装饰器。相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的call方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

import time
class Foo:
    def __init__(self,func):
        self._func=func
    def __call__(self):
        print("class decorator start")
        self._func()
        print("class decorator end")
@Foo
def now():
    print(time.strftime("%Y-%m-%d",time.localtime(time.time())))
now()
#class decorator start
#2020-02-03
#class decorator end

带参数的类装饰器

 同样,类装饰器也是可以带参数的。

class logger:
    def __init__(self,level='INFO'):
        self.level=level
    def __call__(self,func):
        def wrapper(*args,**kwargs):
            print("[{level}]:the function {func}() is run".format(
                level=self.level,func=func.__name__))
            func(*args,**kwargs)
        return wrapper
@logger(level="WARNING")
def say(something):
    print("say {}!".format(something))
say("hello")
# [WARNING]:the function say() is run
# say hello!

带参数和不带参数的类装饰器有很大的不同。

  • _ _ init_ _ :不再接收被装饰函数,而是接收传入参数。
  • _ _ call_ _ :接收被装饰函数,实现装饰逻辑。

装饰类的装饰器

 修饰类的装饰器不常见也不常用,但是和修饰函数的装饰器是一样的。

def typed(**kwargs):
    def deco(obj):
        for k, v in kwargs.items():
            setattr(obj, k, v)
        return obj
    return deco

@typed(x=1, y=2, z=3)
class Foo:
    pass
print(Foo.__dict__)
@typed(name='alex')
class Bar:
    pass
print(Bar.__dict__)
#{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3}
#{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Bar' objects>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': None, 'name': 'alex'}

总结

本节代码

相关文章

  • 装饰器模式

    介绍 在python装饰器学习 这篇文章中,介绍了python 中的装饰器,python内置了对装饰器的支持。面向...

  • python中的装饰器

    python装饰器详解 Python装饰器学习(九步入门) 装饰器(decorator) 就是一个包装机(wrap...

  • [译] Python装饰器Part II:装饰器参数

    这是Python装饰器讲解的第二部分,上一篇:Python装饰器Part I:装饰器简介 回顾:不带参数的装饰器 ...

  • Python中的装饰器

    Python中的装饰器 不带参数的装饰器 带参数的装饰器 类装饰器 functools.wraps 使用装饰器极大...

  • Python进阶——面向对象

    1. Python中的@property   @property是python自带的装饰器,装饰器(decorat...

  • Python 装饰器填坑指南 | 最常见的报错信息、原因和解决方

    Python 装饰器简介装饰器(Decorator)是 Python 非常实用的一个语法糖功能。装饰器本质是一种返...

  • Python装饰器

    Python装饰器 一、函数装饰器 1.无参装饰器 示例:日志记录装饰器 2.带参装饰器 示例: 二、类装饰器 示例:

  • python3基础---详解装饰器

    1、装饰器原理 2、装饰器语法 3、装饰器执行的时间 装饰器在Python解释器执行的时候,就会进行自动装饰,并不...

  • 2019-05-26python装饰器到底是什么?

    装饰器例子 参考语法 装饰器是什么?个人理解,装饰器,是python中一种写法的定义。他仍然符合python的基本...

  • 2018-07-18

    Python装饰器 装饰,顾名思义,是用来打扮什么东西的。Python装饰...

网友评论

      本文标题:python装饰器

      本文链接:https://www.haomeiwen.com/subject/ukvqxhtx.html