簡體   English   中英

可選的 args,裝飾器的 kwargs

[英]Optional args, kwargs to decorator

我正在使用以下方法將可選參數傳遞給裝飾器:

def wait(func=None, delay=1.0):
    def decorator_wait(func):
        def wrapper_wait(*args, **kwargs):
            time.sleep(delay)
            return func(*args, **kwargs)
        return wrapper_wait
    return decorator_wait(func) if func is not None else decorator_wait


@wait
def print_something(something):
    print (something)

@wait(delay=0.2)
def print_something_else(something):
    print (something)

盡管所有的嵌套,上面的代碼看起來都很難理解。 是否有另一種方法來執行上述操作,或者這是唯一可用於此類事情的方法?

您可以編寫具有__call__方法的類,而不是編寫一堆嵌套的def

聽起來你想要一個裝飾器Wait來拖拽程序執行幾秒鍾。 如果您不傳遞等待時間,則默認值為 1 秒。 用例如下所示。

##################################################
@Wait
def print_something(something):
    print(something)

##################################################
@Wait(3)
def print_something_else(something_else):
    print(something_else)

##################################################
@Wait(delay=3)
def print_something_else(something_else):
    print(something_else)

Wait有一個參數時,例如@Wait(3) ,則調用Wait(3)會在其他任何事情發生之前執行。

也就是下面兩段代碼是等價的

@Wait(3)
def print_something_else(something_else):
    print(something_else)
    
###############################################
return_value = Wait(3)
@return_value
def print_something_else(something_else):
    print(something_else)
    

這是個問題。

if `Wait` has no arguments:
    `Wait` is the decorator.
else: # `Wait` receives arguments
    `Wait` is not the decorator itself.
    Instead, `Wait` ***returns*** the decorator
    

一種解決方案如下所示:

讓我們從創建以下 class, DelayedDecorator

import io
class DelayedDecorator:
    def __init__(i, cls, *args, **kwargs):
        print("Delayed Decorator __init__", cls, args, kwargs)
        i._cls = cls
        i._args = args
        i._kwargs = kwargs
    def __call__(i, func):
        print("Delayed Decorator __call__", func)
        if not (callable(func)):
            import io
            with io.StringIO() as ss:
                print(
                    "If only one input, input must be callable",
                    "Instead, received:",
                    repr(func),
                    sep="\n",
                    file=ss
                )
                msg = ss.getvalue()
            raise TypeError(msg)
        return i._cls(func, *i._args, **i._kwargs)
    

現在我們可以編寫如下內容:

 dec = DelayedDecorator(Wait, delay=4)
 @dec
 def delayed_print(something):
    print(something)

注意:

  • dec不接受多個 arguments。
  • dec僅接受要包裝的 function。
import inspect
class PolyArgDecoratorMeta(type):
    def __call__(Wait, *args, **kwargs):
        try:
            arg_count = len(args)
            if (arg_count == 1):
                if callable(args[0]):
                    SuperClass = inspect.getmro(PolyArgDecoratorMeta)[1]
                    r = SuperClass.__call__(Wait, args[0])
                else:
                    r = DelayedDecorator(Wait, *args, **kwargs)
            else:
                r = DelayedDecorator(Wait, *args, **kwargs)
        finally:
            pass
        return r
    
import time
class Wait(metaclass=PolyArgDecoratorMeta):
    def __init__(i, func, delay = 2):
        i._func = func
        i._delay = delay
        
    def __call__(i, *args, **kwargs):
        time.sleep(i._delay)
        r = i._func(*args, **kwargs)
        return r 

以下兩段代碼是等價的:

@Wait
def print_something(something):
     print (something)

##################################################

def print_something(something):
    print(something)
print_something = Wait(print_something)

我們可以非常緩慢地將"something"打印到控制台,如下:

print_something("something")

#################################################
@Wait(delay=1)
def print_something_else(something_else):
    print(something_else)

##################################################
def print_something_else(something_else):
    print(something_else)

dd = DelayedDecorator(Wait, delay=1)
print_something_else = dd(print_something_else)

##################################################

print_something_else("something") 
     

最后的筆記

它可能看起來有很多代碼,但您不必每次都編寫DelayedDecoratorPolyArgDecoratorMeta類。 您必須親自編寫的唯一代碼如下,相當短:

from PolyArgDecoratorMeta import PolyArgDecoratorMeta
import time
class Wait(metaclass=PolyArgDecoratorMeta):
 def __init__(i, func, delay = 2):
     i._func = func
     i._delay = delay

 def __call__(i, *args, **kwargs):
     time.sleep(i._delay)
     r = i._func(*args, **kwargs)
     return r

您可以避免記住“我需要打電話給這個嗎?” 通過從wait function 中刪除func參數,並記住始終調用您的裝飾器返回器。

它看起來像這樣:

def wait(delay=1.0):
    def decorator_wait(func):
        def wrapper_wait(*args, **kwargs):
            time.sleep(delay)
            return func(*args, **kwargs)
        return wrapper_wait
    return decorator_wait


@wait()
def print_something(something):
    print (something)

@wait(delay=0.2)
def print_something_else(something):
    print (something)

print_something("hello")
# 1 second delay, then 'hello'
print_something_else("there")
# 0.2 second delay, then 'there'

你只需要記住wait總是返回裝飾器,所以你必須在裝飾你的函數時使用()

我認為它好一點:

import functools
import time

def wait(func=None, delay=1.0):
    if func is None:
        return lambda func: wait(func=func, delay=delay)

    @functools.wraps(func) # this is good practice to use it see: https://stackoverflow.com/questions/308999/what-does-functools-wraps-do
    def _wrapper(*args, **kwargs):
        time.sleep(delay)
        return func(*args, **kwargs)
    return _wrapper


@wait
def test():
    return

@wait(delay=3)
def test2():
    return

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM