Stefan
Stefan

Reputation: 4317

Python decorator with optional callable argument

There are many answers here on how to detect if a python decorator is used with or without arguments. They typically look like this:

class MyDecorator(object):
   def __init__(self, *args):
      if len(args) == 1 and callable(args[0]):
         # no arguments
      else:
         # arguments

But now I have the following use-case:

@MyDecorator(lambda x:2*x)
def foo():
   pass

Which is wrongly detected as a 'no-argument' case.

Is there a way to detect this situation as well?

[edit: Added missing 'self' parameter]

Upvotes: 4

Views: 1123

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1121644

The __init__ method requires a self parameter:

class MyDecorator(object):
   def __init__(self, *args):
      if len(args) == 1 and callable(args[0]):
         # no arguments
      else:
         # arguments

Without it you always have one argument at least and it will not be callable; it is the decorator instance instead. In other words, without an explicit self, *args will be two elements long when you pass in the argument and it'll be args[1] you wanted to test.

Upvotes: 1

Wolph
Wolph

Reputation: 80031

Yes, but it will remain slightly hacky. The trick is to use named arguments. Besides that there is no clean way to differentiate between the different callables.

class MyDecorator(object):
    def __init__(self, *args, **kwargs):
        if kwargs:
            # arguments
            print 'got %r as arguments'
        else:
            callable, = args

@MyDecorator(some_function=lambda x:2*x)
def foo():
    pass

Upvotes: 2

Related Questions