zoukankan      html  css  js  c++  java
  • Python带参数的装饰器

    在装饰器函数里传入参数

    # -*- coding: utf-8 -*-
    # 2017/12/2 21:38
    # 这不是什么黑魔法,你只需要让包装器传递参数:
    def a_decorator_passing_arguments(function_to_decorate):
        def a_wrapper_accepting_arguments(arg1, arg2):
            print("I got args! Look:", arg1, arg2)
            function_to_decorate(arg1, arg2)
        return a_wrapper_accepting_arguments
    
    # 当你调用装饰器返回的函数时,也就调用了包装器,把参数传入包装器里,
    # 它将把参数传递给被装饰的函数里.
    
    @a_decorator_passing_arguments
    def print_full_name(first_name, last_name):
        print("My name is", first_name, last_name)
    
    print_full_name("Peter", "Venkman")
    # 输出:
    #I got args! Look: Peter Venkman
    #My name is Peter Venkman

    在Python里方法和函数几乎一样.唯一的区别就是方法的第一个参数是一个当前对象的(self)

    也就是说你可以用同样的方式来装饰方法!只要记得把self加进去:

    def method_friendly_decorator(method_to_decorate):
        def wrapper(self, lie):
            lie = lie - 3 # 女性福音 :-)
            return method_to_decorate(self, lie)
        return wrapper
    
    class Lucy(object):
        def __init__(self):
            self.age = 32
    
        @method_friendly_decorator
        def sayYourAge(self, lie):
            print("I am %s, what did you think?" % (self.age + lie))
    
    l = Lucy()
    l.sayYourAge(-3)
    #输出: I am 26, what did you think?

    如果你想造一个更通用的可以同时满足方法和函数的装饰器,用*args,**kwargs就可以了

    def a_decorator_passing_arbitrary_arguments(function_to_decorate):
        # 包装器接受所有参数
        def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
            print("Do I have args?:")
            print(args)
            print(kwargs)
            # 现在把*args,**kwargs解包
            # 如果你不明白什么是解包的话,请查阅:
            # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
            function_to_decorate(*args, **kwargs)
        return a_wrapper_accepting_arbitrary_arguments
    
    @a_decorator_passing_arbitrary_arguments
    def function_with_no_argument():
        print("Python is cool, no argument here.")
    
    function_with_no_argument()
    #输出
    #Do I have args?:
    #()
    #{}
    #Python is cool, no argument here.
    
    @a_decorator_passing_arbitrary_arguments
    def function_with_arguments(a, b, c):
        print(a, b, c)
    
    function_with_arguments(1,2,3)
    #输出
    #Do I have args?:
    #(1, 2, 3)
    #{}
    #1 2 3
    
    @a_decorator_passing_arbitrary_arguments
    def function_with_named_arguments(a, b, c, platypus="Why not ?"):
        print("Do %s, %s and %s like platypus? %s" %(a, b, c, platypus))
    
    function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")
    #输出
    #Do I have args ? :
    #('Bill', 'Linus', 'Steve')
    #{'platypus': 'Indeed!'}
    #Do Bill, Linus and Steve like platypus? Indeed!
    
    class Mary(object):
        def __init__(self):
            self.age = 31
    
        @a_decorator_passing_arbitrary_arguments
        def sayYourAge(self, lie=-3): # 可以加入一个默认值
            print("I am %s, what did you think ?" % (self.age + lie))
    
    m = Mary()
    m.sayYourAge()
    #输出
    # Do I have args?:
    #(<__main__.Mary object at 0xb7d303ac>,)
    #{}
    #I am 28, what did you think?

    把参数传递给装饰器

    好了,如何把参数传递给装饰器自己?

    因为装饰器必须接收一个函数当做参数,所以有点麻烦.好吧,你不可以直接把被装饰函数的参数传递给装饰器.

    在我们考虑这个问题时,让我们重新回顾下:

    # 装饰器就是一个'平常不过'的函数
    def my_decorator(func):
        print "I am an ordinary function"
        def wrapper():
            print "I am function returned by the decorator"
            func()
        return wrapper
    
    # 因此你可以不用"@"也可以调用他
    
    def lazy_function():
        print "zzzzzzzz"
    
    decorated_function = my_decorator(lazy_function)
    #输出: I am an ordinary function
    
    # 之所以输出 "I am an ordinary function"是因为你调用了函数,
    # 并非什么魔法.
    
    @my_decorator
    def lazy_function():
        print "zzzzzzzz"
    
    #输出: I am an ordinary function

    看见了吗,和"my_decorator"一样只是被调用.所以当你用@my_decorator你只是告诉Python去掉用被变量my_decorator标记的函数.

    这非常重要!你的标记能直接指向装饰器.

    def decorator_maker():
    
        print "I make decorators! I am executed only once: "+
              "when you make me create a decorator."
    
        def my_decorator(func):
    
            print "I am a decorator! I am executed only when you decorate a function."
    
            def wrapped():
                print ("I am the wrapper around the decorated function. "
                      "I am called when you call the decorated function. "
                      "As the wrapper, I return the RESULT of the decorated function.")
                return func()
    
            print "As the decorator, I return the wrapped function."
    
            return wrapped
    
        print "As a decorator maker, I return a decorator"
        return my_decorator
    
    # 让我们建一个装饰器.它只是一个新函数.
    new_decorator = decorator_maker()
    #输出:
    #I make decorators! I am executed only once: when you make me create a decorator.
    #As a decorator maker, I return a decorator
    
    # 下面来装饰一个函数
    
    def decorated_function():
        print "I am the decorated function."
    
    decorated_function = new_decorator(decorated_function)
    #输出:
    #I am a decorator! I am executed only when you decorate a function.
    #As the decorator, I return the wrapped function
    
    # Let’s call the function:
    decorated_function()
    #输出:
    #I am the wrapper around the decorated function. I am called when you call the decorated function.
    #As the wrapper, I return the RESULT of the decorated function.
    #I am the decorated function.

    下面让我们去掉所有可恶的中间变量:

    def decorated_function():
        print "I am the decorated function."
    decorated_function = decorator_maker()(decorated_function)
    #输出:
    #I make decorators! I am executed only once: when you make me create a decorator.
    #As a decorator maker, I return a decorator
    #I am a decorator! I am executed only when you decorate a function.
    #As the decorator, I return the wrapped function.
    
    # 最后:
    decorated_function()
    #输出:
    #I am the wrapper around the decorated function. I am called when you call the decorated function.
    #As the wrapper, I return the RESULT of the decorated function.
    #I am the decorated function.

    让我们简化一下:

    @decorator_maker()
    def decorated_function():
        print "I am the decorated function."
    #输出:
    #I make decorators! I am executed only once: when you make me create a decorator.
    #As a decorator maker, I return a decorator
    #I am a decorator! I am executed only when you decorate a function.
    #As the decorator, I return the wrapped function.
    
    #最终:
    decorated_function()
    #输出:
    #I am the wrapper around the decorated function. I am called when you call the decorated function.
    #As the wrapper, I return the RESULT of the decorated function.
    #I am the decorated function.

    看到了吗?我们用一个函数调用"@"语法!:-)

    所以让我们回到装饰器的.如果我们在函数运行过程中动态生成装饰器,我们是不是可以把参数传递给函数?

    def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
        print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2
        def my_decorator(func):
            # 这里传递参数的能力是借鉴了 closures.
            # 如果对closures感到困惑可以看看下面这个:
            # http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
            print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2
            # 不要忘了装饰器参数和函数参数!
            def wrapped(function_arg1, function_arg2) :
                print ("I am the wrapper around the decorated function.
    "
                      "I can access all the variables
    "
                      "	- from the decorator: {0} {1}
    "
                      "	- from the function call: {2} {3}
    "
                      "Then I can pass them to the decorated function"
                      .format(decorator_arg1, decorator_arg2,
                              function_arg1, function_arg2))
                return func(function_arg1, function_arg2)
            return wrapped
        return my_decorator
    
    @decorator_maker_with_arguments("Leonard", "Sheldon")
    def decorated_function_with_arguments(function_arg1, function_arg2):
        print ("I am the decorated function and only knows about my arguments: {0}"
               " {1}".format(function_arg1, function_arg2))
    
    decorated_function_with_arguments("Rajesh", "Howard")
    #输出:
    #I make decorators! And I accept arguments: Leonard Sheldon
    #I am the decorator. Somehow you passed me arguments: Leonard Sheldon
    #I am the wrapper around the decorated function.
    #I can access all the variables
    #   - from the decorator: Leonard Sheldon
    #   - from the function call: Rajesh Howard
    #Then I can pass them to the decorated function
    #I am the decorated function and only knows about my arguments: Rajesh Howard

    上面就是带参数的装饰器.参数可以设置成变量:

    c1 = "Penny"
    c2 = "Leslie"
    
    @decorator_maker_with_arguments("Leonard", c1)
    def decorated_function_with_arguments(function_arg1, function_arg2):
        print ("I am the decorated function and only knows about my arguments:"
               " {0} {1}".format(function_arg1, function_arg2))
    
    decorated_function_with_arguments(c2, "Howard")
    #输出:
    #I make decorators! And I accept arguments: Leonard Penny
    #I am the decorator. Somehow you passed me arguments: Leonard Penny
    #I am the wrapper around the decorated function.
    #I can access all the variables
    #   - from the decorator: Leonard Penny
    #   - from the function call: Leslie Howard
    #Then I can pass them to the decorated function
    #I am the decorated function and only knows about my arguments: Leslie Howard

    你可以用这个小技巧把任何函数的参数传递给装饰器.如果你愿意还可以用*args,**kwargs.但是一定要记住了装饰器只能被调用一次.当Python载入脚本后,你不可以动态的设置参数了.当你运行import x,函数已经被装饰,所以你什么都不能动了.

    functools模块在2.5被引进.它包含了一个functools.wraps()函数,可以复制装饰器函数的名字,模块和文档给它的包装器.

    如何为被装饰的函数保存元数据
    解决方案:
    使用标准库functools中的装饰器wraps 装饰内部包裹函数,
    可以 制定将原函数的某些属性,更新到包裹函数的上面
    其实也可以通过
    wrapper.name = func.name
    update_wrapper(wrapper, func, (‘name‘,’doc‘), (‘dict‘,))
    f.__name__ 函数的名字
    f.__doc__ 函数文档字符串
    f.__module__ 函数所属模块名称
    f.__dict__ 函数的属性字典
    f.__defaults__ 默认参数元组
    f.__closure__ 函数闭包
    >>> def f():
    ...     a=2
    ...     return lambda k:a**k
    ...
    >>> g=f()
    >>> g.__closure__
    (<cell at 0x000001888D17F2E8: int object at 0x0000000055F4C6D0>,)
    >>> c=g.__closure__[0]
    >>> c.cell_contents
    2
    from functools import wraps,update_wrapper
    def log(level="low"):
        def deco(func):
            @wraps(func)
            def wrapper(*args,**kwargs):
                ''' I am wrapper function'''
                print("log was in...")
                if level == "low":
                    print("detailes was needed")
                return func(*args,**kwargs)
            #wrapper.__name__ = func.__name__
            #update_wrapper(wrapper, func, ('__name__','__doc__'), ('__dict__',))
            return wrapper
        return deco
    
    @log()
    def myFunc():
        '''I am myFunc...'''
        print("myFunc was called")
    
    print(myFunc.__name__)
    print(myFunc.__doc__)
    myFunc()
    
    
    """
    myFunc
    I am myFunc...
    log was in...
    detailes was needed
    myFunc was called
    """

    如何定义带参数的装饰器
    实现一个装饰器,它用来检查被装饰函数的参数类型,装饰器可以通过参数指明函数参数的类型,
    调用时如果检测出类型不匹配则抛出异常。
    提取函数签名python3 inspect.signature()
    带参数的装饰器,也就是根据参数定制化一个装饰器可以看生成器的工厂
    每次调用typeassert,返回一个特定的装饰器,然后用它去装饰其他函数

    >>> from inspect import signature
    >>> def f(a,b,c=1):pass
    >>> sig=signature(f)
    >>> sig.parameters
    mappingproxy(OrderedDict([('a', <Parameter "a">), ('b', <Parameter "b">), ('c', <Parameter "c=1">)]))
    >>> a=sig.parameters['a']
    >>> a.name
    'a'
    >>> a
    <Parameter "a">
    >>> dir(a)
    ['KEYWORD_ONLY', 'POSITIONAL_ONLY', 'POSITIONAL_OR_KEYWORD', 'VAR_KEYWORD', 'VAR_POSITIONAL', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_annotation', '_default', '_kind', '_name', 'annotation', 'default', 'empty', 'kind', 'name', 'replace']
    >>> a.kind
    <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>
    >>> a.default
    <class 'inspect._empty'>
    >>> c=sig.parameters['c']
    >>> c.default
    1
    >>> sig.bind(str,int,int)
    <BoundArguments (a=<class 'str'>, b=<class 'int'>, c=<class 'int'>)>
    >>> bargs=sig.bind(str,int,int)
    >>> bargs.arguments
    OrderedDict([('a', <class 'str'>), ('b', <class 'int'>), ('c', <class 'int'>)])
    >>> bargs.arguments['a']
    <class 'str'>
    >>> bargs.arguments['b']
    <class 'int'>
    from inspect import signature
    def typeassert(*ty_args,**ty_kargs):
        def decorator(func):
            #func ->a,b
            #d = {'a':int,'b':str}
            sig = signature(func)
            btypes = sig.bind_partial(*ty_args,**ty_kargs).arguments
            def wrapper(*args,**kargs):
                #arg in d,instance(arg,d[arg])
                for name, obj in sig.bind(*args,**kargs).arguments.items():
                    if name in btypes:
                        if not isinstance(obj,btypes[name]):
                            raise TypeError('"%s" must be "%s"' %(name,btypes[name]))
                return func(*args,**kargs)
            return wrapper
        return decorator
    
    @typeassert(int,str,list)
    def f(a,b,c):
        print(a,b,c)
    
    f(1,'abc',[1,2,3])
    # f(1,2,[1,2,3])

    如何实现属性可修改的函数装饰器
    为分析程序内哪些函数执行时间开销较大,我们定义一个带timeout参数的函数装饰器,装饰功能如下:
    1.统计被装饰函数单词调用运行时间
    2.时间大于参数timeout的,将此次函数调用记录到log日志中
    3.运行时可修改timeout的值。
    解决方案:
    python3 nolocal
    为包裹函数添加一个函数,用来修改闭包中使用的自由变量.
    python中,使用nonlocal访问嵌套作用域中的变量引用,或者在python2中列表方式,这样就不会在函数本地新建一个局部变量

    from functools import wraps
    import time
    import logging
    def warn(timeout):
        # timeout = [timeout]
        def deco(func):
            def wrapper(*args,**kwargs):
                start = time.time()
                res = func(*args,**kwargs)
                used = time.time() -start
                if used > timeout:
                    msg = '"%s" : %s > %s'%(func.__name__,used,timeout)
                    logging.warn(msg)
                return res
    
            def setTimeout(k):
                nonlocal timeout
                # timeout[0] = k
                timeout=k
            print("timeout was given....")
            wrapper.setTimeout = setTimeout
            return wrapper
        return deco
    
    from random import randint
    @warn(1.5)
    def test():
        print("in test...")
        while randint(0,1):
            time.sleep(0.5)
    
    for _ in range(30):
        test()
    
    test.setTimeout(1)
    print("after set to 1....")
    for _ in range(30):
        test()

    小练习:

    #为了debug,堆栈跟踪将会返回函数的 __name__
    def foo():
        print("foo")
    
    print(foo.__name__)
    #输出: foo
    ########################################
    # 如果加上装饰器,将变得有点复杂
    def bar(func):
        def wrapper():
            print("bar")
            return func()
        return wrapper
    
    @bar
    def foo():
        print("foo")
    
    print(foo.__name__)
    #输出: wrapper
    #######################################
    # "functools" 将有所帮助
    import functools
    
    def bar(func):
        # 我们所说的"wrapper",正在包装 "func",
        # 好戏开始了
        @functools.wraps(func)
        def wrapper():
            print("bar")
            return func()
        return wrapper
    
    @bar
    def foo():
        print("foo")
    
    print(foo.__name__)
    #输出: foo
  • 相关阅读:
    UVA1599 理想路径 Ideal Path(最短路径)
    换根DP
    小w的魔术扑克(树状数组+并查集)
    NOIP 2016蚯蚓(优先队列)
    ZR 动物园
    T105017 seq(DP)
    noip2017酱油记
    noip2017酱油记前篇
    P1985 翻转棋
    luogu P2512 [HAOI2008]糖果传递
  • 原文地址:https://www.cnblogs.com/ningxin18/p/7955897.html
Copyright © 2011-2022 走看看