python @warps装饰器及源码剖析

标签: python 装饰器 @warps
3人阅读 评论(0) 收藏 举报
分类:

python装饰器的原理是将装饰对象传入,将装饰对象加强后再返回,但是我们此时调用装饰对象的时候,其实是调用装饰器对象,如下:

@decorator
def fn():
    pass

@语法糖其实相当于decorator(fn)
python这种动态语言很多功能是以鸭子方式来实现的,即看着像鸭子,游着像鸭子我们就认为它是鸭子
这个也是一样,我们虽然调用的是装饰器,但是实现的效果一样,所以我们认为它就是一样的
但是这样会有一些坑,比如之前的装饰对象会有一些属性丢失,如下列例子:

def decorator_single_obj(cls, *args, **kwargs):
    instance = {}  # 创建字典来保存实例

    def get_instance(*args, **kwargs):
        if cls not in instance:  # 若实例不存在则新建
            instance[cls] = cls(*args, **kwargs)
        return instance[cls]
    return get_instance


@decorator_single_obj
class Foo(object):
    age = 24

    def __init__(self, name):
        self.name = name

    @classmethod
    def test(cls):
        pass

我们用装饰器实现了单例模式,打印下面

obj1 = Foo("123")
print(Foo)
Foo.age

结果如下:

Traceback (most recent call last):
<function decorator_single_obj.<locals>.get_instance at 0x0000025AEDAF9158>
  File "E:/githubproject/Source-code/plus/decorator/decorator_single_object.py", line 28, in <module>
    Foo.age
AttributeError: 'function' object has no attribute 'age'

我们再访问Foo变成了函数对象,且再访问age属性没了,虽然我们通过实例对象obj1能正常访问,但是正常类属性和类方法还是应该由类来调用.
其他呢比如说原来对象的doc即文档说明等属性也丢失了,那么如何解决这个问题呢,这时就需要@warps装饰器了
@warps装饰器也是python的内置装饰器,它用来保存之前装饰对象的属性,保证属性不丢失,包括doc
这里需要在头部导入装饰器,并在函数类添加上

from functools import wraps


def decorator_single_obj(cls, *args, **kwargs):
    instance = {}  # 创建字典来保存实例

    @wraps(cls)   ##看这里!看这里
    def get_instance(*args, **kwargs):
        if cls not in instance:  # 若实例不存在则新建
            instance[cls] = cls(*args, **kwargs)
        return instance[cls]
    return get_instance


@decorator_single_obj
class Foo(object):
    age = 24

    def __init__(self, name):
        self.name = name

    @classmethod
    def test(cls)

上面我们只是在get_instance上加上了@warps装饰器

这时我们再调用

obj1 = Foo("123")
print(Foo)
Foo.age
Foo.test

就不会出错了
这就是@warps的作用,保存装饰对象的属性
它的原理其实就是将原对象的属性取出来,再一一赋值给装饰器

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):
    """Update a wrapper function to look like the wrapped function

       wrapper is the function to be updated
       wrapped is the original function
       assigned is a tuple naming the attributes assigned directly
       from the wrapped function to the wrapper function (defaults to
       functools.WRAPPER_ASSIGNMENTS)
       updated is a tuple naming the attributes of the wrapper that
       are updated with the corresponding attribute from the wrapped
       function (defaults to functools.WRAPPER_UPDATES)
    """
    for attr in assigned:
        try:
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            setattr(wrapper, attr, value)
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
    # from the wrapped function when updating __dict__
    wrapper.__wrapped__ = wrapped
    # Return the wrapper so this can be used as a decorator via partial()
    return wrapper

def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    """Decorator factory to apply update_wrapper() to a wrapper function

       Returns a decorator that invokes update_wrapper() with the decorated
       function as the wrapper argument and the arguments to wraps() as the
       remaining arguments. Default arguments are as for update_wrapper().
       This is a convenience function to simplify applying partial() to
       update_wrapper().
    """
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

这一部分就是@warps的源码,难度也不是很大,

这里写图片描述
这里写图片描述
其中WRAPPER_ASSIGNMENTS为新添加的属性,不必一一深究,感兴趣的可以一一研究下,大部分都是python3的新特性

更正一下,qualname才会更新对象的名字

WRAPPER_UPDATES为原对象的dict属性并且会一一赋值给装饰器
这里返回的装饰器对象就包含原来对象的所有属性了
并且我们打印

print(Foo.__wrapped__)

结果:

<class '__main__.Foo'>

可以看到原来的对象保存在了装饰器的warpped属性中,且原对象的所有属性都也添加到了装饰器中
所以对象的属性在这里得到了保存

查看评论

通俗易懂的分析——python装饰器之@functools.warps

首先,宝宝觉着网上没有比我这还透彻的@functools.warps分析了~~~害羞自恋ing……栗子详情:http://stackoverflow.com/questions/308999/what...
  • su92chen
  • su92chen
  • 2017-04-07 16:21:43
  • 384

python装饰器的wraps作用

Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的de...
  • hqzxsc2006
  • hqzxsc2006
  • 2015-12-17 10:08:04
  • 5796

Python 入门:装饰器(decorator)、@functools.wraps、@staticmethod、@classmethod

Python 装饰器(decorator) 1、要了解装饰器,就需要知道什么是高阶函数,高阶函数就是将函数作为参数赋值给另一个函数 2、Python的 decorator 本质上就是一个高阶函数,它...
  • foryouslgme
  • foryouslgme
  • 2016-05-26 16:28:50
  • 843

python源码剖析

  • 2011年05月11日 16:18
  • 408KB
  • 下载

Python源码剖析

  • 2018年04月08日 08:31
  • 29.04MB
  • 下载

Python源码剖析_f117f.pdf

  • 2018年03月17日 01:05
  • 29.03MB
  • 下载

Python源码剖析.rar

  • 2010年03月26日 08:25
  • 606KB
  • 下载

Python实战小程序——装饰器

第四题:简述对Python装饰器的理解,写一个简单的装饰器。 要理解装饰器,我们先介绍一下几点python的基础知识。 1、作用域(命名空间)及变量生存周期 有过一点编程基础的都知道namesp...
  • misayaaaaa
  • misayaaaaa
  • 2016-11-04 16:15:29
  • 977

浅谈Python装饰器

浅谈Python装饰器 By 马冬亮(凝霜  Loki) 一个人的战争(http://blog.csdn.net/MDL13412) 前置知识 一级对象 Python将一切视为 objec t的...
  • MDL13412
  • MDL13412
  • 2014-03-30 22:07:27
  • 42182

深入理解Python 装饰器(decorator)

返璞归真, 看山还是山 刚看到Python装饰器时, 觉得很神奇。简单实验下,发现也就那么回事。但是慢慢的看到越来越多的装饰器。很多时候又不了解到底是怎么回事了。最后还是决定好好研究下。先看看一些实例...
  • TangHuanan
  • TangHuanan
  • 2015-04-17 11:12:38
  • 9578
    个人资料
    持之以恒
    等级:
    访问量: 4436
    积分: 207
    排名: 36万+