here is another way i have just discovered: check whether the first (and only) argument to your decorator is callable; if so, you are done and can return your behavior-modifying wrapper method (itself decorated with functools.wraps
to preserve name and documentation string).
in the other case, one or more named or positional arguments should be present; you can collect those arguments and return a callable that accepts a callable as first argument and returns a wrapper method—and since that description fits the description of the decorator method, return that very decorator method! i’ve used functools.partial
here to get a version of my decorator, is_global_method
(which i’m working on right now—its implementation is of course nonsense as shown below, this is only to demonstrate the decoration works).
this solution appears to work but sure needs more testing. if you quint our eyes, you can see that the trick is only three or four lines as a pattern to remember. now i wonder whether i can wrap that kind of functionality into another decorator? ah, the metaness of it!
from functools import wraps
from functools import partial
_ = print
is_instance_of = isinstance
is_callable = lambda x: hasattr( x, '__call__' )
def is_global_method( x, *, name = None ):
if is_callable( x ):
@wraps( x )
def wrapper( *P, **Q ):
return { 'name': name, 'result': x( *P, **Q ), }
return wrapper
# assert is_instance_of( x, str ) # could do some sanity checks here
return partial( is_global_method, name = x )
@is_global_method
def f( x ):
"""This is method f."""
return x ** 2
@is_global_method( 'foobar' )
def g( x ):
"""This is method g."""
return x ** 2
_( f.__name__ )
_( f.__doc__ )
_( f( 42 ) )
_( g.__name__ )
_( g.__doc__ )
_( g( 42 ) )