It is well known that the following two pieces of code are nearly equivalent:    
@dec
def foo():
    pass    foo = dec(foo)
############################################
foo = dec(foo)
A common mistake is to think that @ simply hides the leftmost argument.
@dec(1, 2, 3)
def foo():
    pass    
###########################################
foo = dec(foo, 1, 2, 3)
It would be much easier to write decorators if the above is how @ worked. Unfortunately, that’s not the way things are done.
Consider a decorator Waitwhich haults 
program execution for a few seconds.
If you don't pass in a Wait-time
then the default value is 1 seconds.
Use-cases are shown below.
##################################################
@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)
When Wait has an argument, such as @Wait(3), then the call Wait(3)
is executed before anything else happens.
That is, the following two pieces of code are equivalent
@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)
This is a problem. 
if `Wait` has no arguments:
    `Wait` is the decorator.
else: # `Wait` receives arguments
    `Wait` is not the decorator itself.
    Instead, `Wait` ***returns*** the decorator
One solution is shown below:         
Let us begin by creating the following class, DelayedDecorator:    
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)
Now we can write things like:
 dec = DelayedDecorator(Wait, delay=4)
 @dec
 def delayed_print(something):
    print(something)
Note that:       
- decdoes not not accept multiple arguments.
- deconly accepts the function to be wrapped.
 - 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 
 
The following two pieces of code are equivalent:
@Wait
def print_something(something):
     print (something)
##################################################
def print_something(something):
    print(something)
print_something = Wait(print_something)
We can print "something" to the console very slowly, as follows:
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")
Final Notes
It may look like a lot of code, but you don't have to write the classes DelayedDecorator and PolyArgDecoratorMeta every-time. The only code you have to personally write something like as follows, which is fairly short:
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