美文网首页
python装饰器

python装饰器

作者: XYZeroing | 来源:发表于2017-09-11 10:07 被阅读0次

装饰器简述

要理解装饰器需要知道Python高阶函数python闭包,Python高阶函数可以接受函数作为参数,也可以返回函数,闭包的内部函数可以访问外部函数的局部变量。Python的装饰器正式基于高阶函数和闭包。

来看一个简单的装饰器:

def dec_func(fun):
    def wrapper():
        print('In Wapper!')
        fun()
        print('After fun')
    return wrapper
@dec_func
def foo():
    print('foo')
foo()
In Wapper!
foo
After fun

可以看到装饰器不影响foo()函数的正常功能,它接受一个函数作为参数,然后对这个函数进行装饰后返回给原来的函数名。

装饰器的原理类似下面的函数:

def bar():
    print('Bar')
bar = dec_func(bar)
bar()
In Wapper!
Bar
After fun

装饰器的运行首先是将函数带入装饰器,原本的函数名接受装饰器“装饰”后的返回函数,再调用这个返回函数。

装饰器就是对函数进行装饰,“装饰”以为这并不会对函数的正常运行造成影响,仅仅是对函数的一些功能进行额外的补充。

装饰器是一个函数,接受一个函数(或者类)作为参数,返回值也是也是一个函数(或者类)

无参装饰器

无参装饰器就是不接受参数的装饰器,无参装饰器嵌套了两层函数,一个是外部函数接受参数,一个是内部的返回函数。

import time
def timeit(func):
    def wrapper():  # wrapper是返回函数,被装饰的函数的函数名接受这个函数,相当于f = wrapper()
        start = time.clock()
        func()  # func就是被传入装饰器的函数,func()在wrapper()内,在调用wrapper时,会调用func()
        end = time.clock()
        print('Used {}'.format(end - start))
    return wrapper
@timeit
def f():
    print('In f()')
f()  # 此时f = wrapper
In f()
Used 0.00031399999999992545

1.首先,把foo函数当做参数传入timeit
2.foo接受返回的函数wrapper,此时的foo指向wrapper,执行foo其实执行wrapper
3.调用foo其实调用了wrapper,在调用wrapper时又重新调用了原本的foo函数,在调用wrapper其实调用foo

多装饰器

def deco1(func):
    def wrapper():
        print('In Deco1')
        func()
    return wrapper
def deco2(func):
    def wrapper():
        print('In Deco2')
        func()
    return wrapper
@deco2
@deco1
def foo():
    print('In foo()')
foo()
In Deco2
In Deco1
In foo()

等价与:

foo = deco2(deco1(foo))

在多装饰器中,紧挨着被装饰函数的装饰器属于最内层,最先调用,最外层的装饰器最后调用。

有参装饰器

def make_header(level):  #接受参数
    print('Create decorator')
    def decorator(func):  #接受函数
        print('Initialize...')
        def wrapper():
            print('Call')
            return '<h{0}>{1}</h{0}>'.format(level, func())
        return wrapper
    return decorator
@make_header(2)
def get_content():
    return 'hello world'
Create decorator
Initialize...
get_content()
Call





'<h2>hello world</h2>'

等价于:

get_content = make_header(2)
get_content = decorator(get_content)

def log(prefix):  # 接受装饰器的参数
    def log_decorator(f):  # 内部定义的wapper负责输出log的参数+被装饰函数名称, 接受被装饰函数
        def wrapper(*args, **kw):   # 接受被装饰函数的参数
                # 将log函数的参数引用
            print('[%s] in decorate wrapper s%s()...' % (prefix, f.__name__))
            f(*args, **kw)
        return wrapper  # 返回内部函数给装饰器
    return log_decorator  # 返回装饰器给log函数
@log('DEBUG')
def test():
    print('out decorate run test()')


print(test())
[DEBUG] in decorate wrapper stest()...
out decorate run test()
None

有参装饰器嵌套了三层函数,最外层的函数接受装饰器的参数,中间的函数接受被装饰函数,最内层的函数接受被装饰函数的参数。

import time
from functools import reduce

def performance(unit):
    def perf_decorator(f):
        def wrapper(*args, **kargs):
            start_time = time.time()
            run_func = f(*args, **kargs)
            end_time = time.time()
            run_time = (end_time - start_time) * 1000 if unit == 'ms' else (end_time - start_time)
            print('call %s() in %f %s' % (f.__name__, run_time, unit))
            return run_func
        return wrapper
    return perf_decorator
@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print(factorial(10))
call factorial() in 0.010252 ms
3628800

装饰器的影响

def log(f):
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    return wrapper
@log
def f2(x):
    pass
print(f2.__name__)
wrapper

由于decorator返回的新函数函数名已经不是'f2',而是@log内部定义的'wrapper'。 这对于那些依赖函数名的代码就会失效。decorator还改变了函数的__doc__等其它属性。

改善:

import functools
def log(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    return wrapper

最后需要指出,由于我们把原函数签名改成了(*args, **kw),因此,无法获得原函数的原始参数信息。 即便我们采用固定参数来装饰只有一个参数的函数

def log(f):
    @functools.wraps(f)
    def wrapper(x):
        print('call...')
        return f(x)
    return wrapper

也可能改变原函数的参数名,因为新函数的参数名始终是 'x',原函数定义的参数名不一定叫 'x'

import time
import functools

def performance(unit):
    def perf_decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kw):
            t1 = time.time()
            r = f(*args, **kw)
            t2 = time.time()
            t = (t2 - t1) * 1000 if unit == 'ms' else (t2 - t1)
            print('call %s() in %f %s' % (f.__name__, t, unit))
            return r
        return wrapper
    return perf_decorator
@performance('ms')
def factorial(n):
    return reduce(lambda x, y: x * y, range(1, n + 1))
print(factorial(10))
call factorial() in 0.008821 ms
3628800
print(factorial.__name__)
factorial

基于类的装饰器

可以定义基于类的装饰器

class Bold(object):
    def __init__(self, func):
        self.func = func
 
    def __call__(self, *args, **kwargs):
        return '<b>' + self.func(*args, **kwargs) + '</b>'
@Bold
def hello(name):
    return 'hello %s' % name
hello('world')
'<b>hello world</b>'
  • __init__():它接收一个函数作为参数,也就是被装饰的函数
  • __call__():让类对象可调用,就像函数调用一样,在调用被装饰函数时被调用

阅读

Python Decorator 基础
Python: 会打扮的装饰器

相关文章

  • 装饰器模式

    介绍 在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/mkvxsxtx.html