威尼斯wns.9778官网 > 计算机教程 > [python 基础]python装饰器(一)添加functools获取原函数

原标题:[python 基础]python装饰器(一)添加functools获取原函数

浏览次数:200 时间:2019-05-10

python装饰器学习的时候有两点需要注意一下

1 模块简介

functools,用于高阶函数:指那些作用于函数或者返回其它函数的函数,通常只要是可以被当做函数调用的对象就是这个模块的目标。

在Python 2.7 中具备如下方法,

cmp_to_key,将一个比较函数转换关键字函数;

partial,针对函数起作用,并且是部分的;

reduce,与python内置的reduce函数功能一样;

total_ordering,在类装饰器中按照缺失顺序,填充方法;

update_wrapper,更新一个包裹(wrapper)函数,使其看起来更像被包裹(wrapped)的函数;

wraps,可用作一个装饰器,简化调用update_wrapper的过程;

1,被装饰器装饰的函数取其func.__name__和func.func_doc的时候得到的不是被修饰函数的相关信息而是装饰器wrapper函数的docstring和名字

2 模块使用

对此我们使用functools这个模块添加一行函数即可

2.1 cmp_to_key

将老式的比较函数(comparison function)转换为关键字函数(key function),与接受key function的工具一同使用(例如sorted,min,max,heapq.nlargest,itertools.groupby),该函数主要用于将程序转换成Python 3格式的,因为Python 3中不支持比较函数。

比较函数是可调用的,接受两个参数,比较这两个参数并根据他们的大小关系返回负值、零或者正值中的一个。关键字函数也是可调用的,接受一个参数,同时返回一个可以用作排序关键字的值。

from functools import cmp_to_key

def compare(ele1,ele2):
    return ele2 - ele1

a = [2,3,1]
print sorted(a, key = cmp_to_key(compare))

控制台输出,

[3, 2, 1]
@functools.wraps(f)
def check_id_admin(f):

'''检查是否为admin'''
    # 使得__name__和func_doc能够获得函数原有的docstring和函数名而不是装饰器相关的
    @functools.wraps(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

@check_id_admin
def get_food(username,password,food="chocolate"):
    '''get food'''
    return "%s get food: %s"%(username,food)

def main():
    print(get_food.__name__)
    print(get_food.func_doc)

#输出结果:

2.2 partial

functools.partial(func, *args, **keywords),函数装饰器,返回一个新的partial对象。调用partial对象和调用被修饰的函数func相同,只不过调用partial对象时传入的参数个数通常要少于调用func时传入的参数个数。当一个函数func可以接收很多参数,而某一次使用只需要更改其中的一部分参数,其他的参数都保持不变时,partial对象就可以将这些不变的对象冻结起来,这样调用partial对象时传入未冻结的参数,partial对象调用func时连同已经被冻结的参数一同传给func函数,从而可以简化调用过程。

如果调用partial对象时提供了更多的参数,那么他们会被添加到args的后面,如果提供了更多的关键字参数,那么它们将扩展或者覆盖已经冻结的关键字参数。

partial对象的调用如下,

import functools

def add(a,b):
    return a   b

add3 = functools.partial(add,3)
add5 = functools.partial(add,5)

print add3(4)

print add5(10)

控制台输出,

7
15

  get_food
  get food

2.3 reduce

与Python内置的reduce函数一样,为了向Python3过渡;

import functools

a = range(1,6)
print functools.reduce(lambda x,y:x y,a)

控制台输出,

15

 对比不使用@functools.wrap(f)

2.4 total_ordering

这是一个类装饰器,给定一个类,这个类定义了一个或者多个比较排序方法,这个类装饰器将会补充其余的比较方法,减少了自己定义所有比较方法时的工作量;

被修饰的类必须至少定义 __lt__(), __le__(),__gt__(),__ge__()中的一个,同时,被修饰的类还应该提供 __eq__()方法。

from functools import total_ordering

class Person:
    # 定义相等的比较函数
    def __eq__(self,other):
        return ((self.lastname.lower(),self.firstname.lower()) == 
                (other.lastname.lower(),other.firstname.lower()))

    # 定义小于的比较函数
    def __lt__(self,other):
        return ((self.lastname.lower(),self.firstname.lower()) < 
                (other.lastname.lower(),other.firstname.lower()))

p1 = Person()
p2 = Person()

p1.lastname = "123"
p1.firstname = "000"

p2.lastname = "1231"
p2.firstname = "000"

print p1 < p2
print p1 <= p2
print p1 == p2
print p1 > p2
print p1 >= p2

控制台输出,

True
True
False
False
False

 wrapper

2.5 update_wrapper

更新一个包裹(wrapper)函数,使其看起来更像被包裹(wrapped)的函数。

可选的参数指定了被包裹函数的哪些属性直接赋值给包裹函数的对应属性,同时包裹函数的哪些属性要更新而不是直接接受被包裹函数的对应属性,参数assigned的默认值对应于模块级常量WRAPPER_ASSIGNMENTS(默认地将被包裹函数的 __name__, __module__,和 __doc__ 属性赋值给包裹函数),参数updated的默认值对应于模块级常量WRAPPER_UPDATES(默认更新wrapper函数的 __dict__ 属性)。

这个函数的主要用途是在一个装饰器中,原函数会被装饰(包裹),装饰器函数会返回一个wrapper函数,如果装饰器返回的这个wrapper函数没有被更新,那么它的一些元数据更多的是反映wrapper函数定义的特征,无法反映wrapped函数的特性。

 none 

2.6 wraps

这个函数可用作一个装饰器,简化调用update_wrapper的过程,调用这个函数等价于调用partial(update_wrapper, wrapped = wrapped, assigned = assigned,updated = updated)。

from functools import wraps

def my_decorator(f):
    @wraps(f)
    def wrapper(*args,**kwds):
        print "Calling decorated function"
        return f(*args,**kwds)
    return wrapper

@my_decorator
def example():
    """DocString"""
    print "Called example function"

example()
print example.__name__
print example.__doc__

控制台输出,

Calling decorated function
Called example function
example
DocString

可以看到,最终调用函数example时,是经过 @my_decorator装饰的,装饰器的作用是接受一个被包裹的函数作为参数,对其进行加工,返回一个包裹函数,代码使用 @functools.wraps装饰将要返回的包裹函数wrapper,使得它的 __name__, __module__,和 __doc__ 属性与被装饰函数example完全相同,这样虽然最终调用的是经过装饰的example函数,但是某些属性还是得到维护。

如果在 @my_decorator的定义中不使用 @function.wraps装饰包裹函数,那么最终example.__name__ 将会变成'wrapper',而example.__doc__ 也会丢失。

将 @wraps(f)注释掉,然后运行程序,控制台输出,

Calling decorated function
Called example function
wrapper
None

2.查看functools.wraps()源码

3 Reference

PYTHON-进阶-FUNCTOOLS模块小结

Python——functools

python中functools宝库下的partial

functools

RAPPER_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:
     #在这里获取原函数wrapped的attr赋值给value
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            setattr(wrapper, attr, value)
    for attr in updated:
     #用wrapped获取的值更新wrapper中的attr
        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)

 

本文由威尼斯wns.9778官网发布于计算机教程,转载请注明出处:[python 基础]python装饰器(一)添加functools获取原函数

关键词:

上一篇:WebRtc编译好的vs2015源码

下一篇:没有了