8

【decorator】装饰器

 3 years ago
source link: https://www.guofei.site/2019/10/27/decorator.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【decorator】装饰器

2019年10月27日

Author: Guofei

文章归类: 设计模式 ,文章编号: 1004


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2019/10/27/decorator.html

Edit

实现

普通的装饰器

定义一个装饰器

def logger(func):
    def wrapper(*args, **kwargs):
        print('我准备开始执行:{} 函数了:'.format(func.__name__))

        # 真正执行的是这行。
        res = func(*args, **kwargs)

        print('我执行完啦。')
        return res

    return wrapper

使用装饰器

@logger
def add(x, y):
    print('{} + {} = {}'.format(x, y, x + y))
    return x + y

add(1, 2)

装饰器是一种让代码简洁的方案,上面这个代码实际上等价于下面这个

def add(x, y):
    print('{} + {} = {}'.format(x, y, x + y))


logger(add)(1, 2)
import functools


def logger(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('我准备开始执行:{} 函数了:'.format(func.__name__))

        # 真正执行的是这行。
        func(*args, **kwargs)

        print('我执行完啦。')

    return wrapper


@logger
def add(x, y):
    print('{} + {} = {}'.format(x, y, x + y))


add(1, 2)
  • @functools.wraps(func)。它能把原函数的元信息拷贝到装饰器里面的 func 函数中。函数的元信息包括docstring、name、参数列表等等。
  • 如果删掉,会发现 add.__name__ 的名字变成了 wrapper

带参数的装饰器

def say_hello(contry):
    def deco(func):
        def wrapper(*args, **kwargs):
            if contry == "china":
                print("你好!")
            elif contry == "america":
                print('hello.')
            else:
                return

            # 真正执行函数的地方
            func(*args, **kwargs)

        return wrapper

    return deco


# 小明,中国人
@say_hello("china")
def xiaoming():
    pass


# jack,美国人
@say_hello("america")
def jack():
    pass

functools.wraps 同上

import functools


def say_hello(contry):
    def deco(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if contry == "china":
                print("你好!")
            elif contry == "america":
                print('hello.')
            else:
                return

            # 真正执行函数的地方
            func(*args, **kwargs)

        return wrapper

    return deco


# 小明,中国人
@say_hello("china")
def xiaoming():
    pass


# jack,美国人
@say_hello("america")
def jack():
    pass

普通的类装饰器

class logger(object):
    def __init__(self, func):
        # 这个方法用来接收被装饰的函数
        self.func = func

    def __call__(self, *args, **kwargs):
        # 用来实现装饰逻辑
        print("[INFO]: the function {func}() is running..." \
              .format(func=self.func.__name__))
        return self.func(*args, **kwargs)


@logger
def say(something):
    print("say {}!".format(something))


say("hello")

带参数的类装饰器

class logger(object):
    def __init__(self, level='INFO'):
        self.level = level

    def __call__(self, func):  # 接受函数
        def wrapper(*args, **kwargs):
            print("[{level}]: the function {func}() is running..." \
                  .format(level=self.level, func=func.__name__))
            func(*args, **kwargs)

        return wrapper


@logger(level='WARNING')
def say(something):
    print("say {}!".format(something))


say("hello")

更多内容:http://magic.iswbm.com/zh/latest/c03/c03_10.html

应用

1

我们要做一个 fibonacci 计算函数,自然想到用递归

def fibonacci(n):
    if n < 0:
        raise ValueError('n>=0')
    return n if n in (0, 1) else fibonacci(n - 1) + fibonacci(n - 2)

你会发现这里有大量重复计算,导致计算效率极低。自然想到改进,把已经计算好的函数值记录下来

known = {0: 0, 1: 1}
def fibonacci2(n):
    if n in known:
        return known[0]
    else:
        res = fibonacci2(n - 1) + fibonacci2(n - 2)
        known[n] = res
    return res

实验发现,这个速度快了很多。新问题,如果你想写一个包,里面有不同的递归算法(帕斯卡三角等),这样写会让代码一团乱麻,因为每个方法你都要定义一个known。

2

能显示运行时间的装饰器

import time


def timer(func):
    def d_func(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
        return result

    return d_func


@timer
def myfunc(a):
    time.sleep(2)
    print(a)


myfunc(3)

附:scipy的做法

from scipy import optimize
optimize.fmin

解释一下,ncalls是一个计数器,记录函数被调用的次数

def wrap_function(function, args):
    ncalls = [0]
    if function is None:
        return ncalls, None

    def function_wrapper(*wrapper_args):
        ncalls[0] += 1
        return function(*(wrapper_args + args))

    return ncalls, function_wrapper

参考资料

【印】Chetan Giridhar:《Python 设计模式》, 中国工信出版集团


您的支持将鼓励我继续创作!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK