zoukankan      html  css  js  c++  java
  • python 装饰器的缺点以及解决方法

    1.python装饰器的缺点

    装饰器可以允许我们在不改变函数或犯方法的调用方式的情况下,添加额外的功能;

    如下所示,我们要在中的方法之前增加装饰器check_is_admin,用来判断执行类的方法的用户是否为admin用户;

    def check_is_admin(f):
        def wrapper(*args,**kwargs):
            if kwargs.get('username') != 'admin':
                raise Exception("This user is not allowed to get food")
            return f(*args,**kwargs)
        return wrapper
    
    class store(object):
        @check_is_admin
        def get_food(self,username,food):
            return self.storage.get(food)
    
        @check_is_admin
        def put_food(self,username,food):
            self.storage.put(food)
    

    但是,经过装饰器修饰的函数,其func_name和func_doc的属性都会丢失;

    如下:

    def foobar(username="someone"):
        """Do crzay staff."""
        pass
    print foobar.func_doc
    print foobar.func_name
    
    执行结果为:
    Do crzay staff.
    foobar
    

    如果给上述函数增加装饰器呢?

    def is_admin(f):
        def wrapper(*args,**kwargs):
            if kwargs.get(*args,**kwargs):
                raise Exception("This user is not allowed to get food")
            return f(*args,**kwargs)
        return wrapper
    
    
    @is_admin
    def foobar(username="someone"):
        """Do crazy staff"""
        pass
    print foobar.__doc__
    print foobar.__name__
    
    执行结果为:
    None
    wrapper
    

    update_wapper的源码如下:

    WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
    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:
            setattr(wrapper, attr, getattr(wrapped, attr))
        for attr in updated:
            getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
        # Return the wrapper so this can be used as a decorator via partial()
        return wrapper
    

      

    2.解决方案

    使用functools的update_wrapper函数可以解决该问题

    如下:

    def is_admin(f):
        def wrapper(*args,**kwargs):
            if kwargs.get(*args,**kwargs):
                raise Exception("This user is not allowed to get food")
            return f(*args,**kwargs)
        return wrapper
    
    def foobar(username="someone"):
        """Do crazy staff"""
        pass
    
    import functools
    foobar = functools.update_wrapper(is_admin,foobar)
    print foobar.__name__
    print foobar.__doc__
    
    执行结果如下:
    foobar
    Do crazy staff
    

    但是上述方式手工调用装饰器不太方便,我们在这里使用functools.warps的装饰器

    import functools
    
    def is_admin(f):
        @functools.wraps(f)
        def wrapper(*args,**kwargs):
            if kwargs.get(*args,**kwargs):
                raise Exception("This user is not allowed to get food")
            return f(*args,**kwargs)
        return wrapper
    
    @is_admin
    def foobar(username="someone"):
        """Do crazy staff"""
        pass
    print foobar.__doc__
    print foobar.__name__
    
    执行结果为:
    Do crazy staff #####
    foobar
    

    结论:python的装饰器会导致被修饰函数的__doc__,__name__等属性丢掉,如果要保留函数的这些属性,需要在装饰器函数中添加functools.wrap装饰器;

  • 相关阅读:
    node.js之npm命令安装扩展模块
    jquery选择器(转)
    node.js入门
    node.js之模块
    redhat 下装redis
    html 5之websocket(转)
    node.js安装和环境搭建
    javascript 动态加载脚本库
    HTML5 LocalStorage 本地存储
    【ecmascript】 ECMAScript 6概览【转】
  • 原文地址:https://www.cnblogs.com/cqq-20151202/p/6606544.html
Copyright © 2011-2022 走看看