40

I was creating a simple command line utility and using a dictionary as a sort of case statement with key words linking to their appropriate function. The functions all have different amount of arguments required so currently to check if the user entered the correct amount of arguments needed for each function I placed the required amount inside the dictionary case statement in the form {Keyword:(FunctionName, AmountofArguments)}.

This current setup works perfectly fine however I was just wondering in the interest of self improval if there was a way to determine the required number of arguments in a function and my google attempts have returned so far nothing of value but I see how args and kwargs could screw such a command up because of the limitless amount of arguments they allow.

1
  • I posted another solution here that works even when the function has keyword-only parameters without defaults or positional-only parameters with defaults. stackoverflow.com/a/74328772/9318372 Commented Nov 5, 2022 at 14:56

6 Answers 6

49

inspect.getargspec():

Get the names and default values of a function’s arguments. A tuple of four things is returned: (args, varargs, varkw, defaults). args is a list of the argument names (it may contain nested lists). varargs and varkw are the names of the * and ** arguments or None. defaults is a tuple of default argument values or None if there are no default arguments; if this tuple has n elements, they correspond to the last n elements listed in args.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks I just discovered that very function as you posted that answer. Thats exactly what I need.
Also since I don't have the rep to edit your answer you have typo in your answer it's getargspec() not getargspect()
Should use inspect.getfullargspec(func) (docs.python.org/3.1/library/inspect.html#inspect.getfullargspec) for Python 3.x
Better yet: use Signature objects, introduced in Python 3.3. See docs.python.org/3/library/inspect.html#inspect-signature-object for documentation. They would correctly avoid counting the self parameter when working on bounded method objects.
19

What you want is in general not possible, because of the use of varargs and kwargs, but inspect.getargspec (Python 2.x) and inspect.getfullargspec (Python 3.x) come close.

  • Python 2.x:

    >>> import inspect
    >>> def add(a, b=0):
    ...     return a + b
    ...
    >>> inspect.getargspec(add)
    (['a', 'b'], None, None, (0,))
    >>> len(inspect.getargspec(add)[0])
    2
    
  • Python 3.x:

    >>> import inspect
    >>> def add(a, b=0):
    ...     return a + b
    ...
    >>> inspect.getfullargspec(add)
    FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(0,), kwonlyargs=[], kwonlydefaults=None, annotations={})
    >>> len(inspect.getfullargspec(add).args)
    2
    

4 Comments

inspect.getargspec(function) is a named tuple, so instead of len(inspect.getargspec(function)[0]) you can use the more readable len(inspect.getargspec(function).args).
getargspec has been deprecated since Python 3, use signature or getfullargspec instead.
@1'': That is true as of version 2.6, it wasn't in the older version I used back then.
@akshay: Thanks for the heads up. I added a Python 3.x example (using 1"'s named tuple suggestion).
6

In Python 3, use someMethod.__code__.co_argcount

(since someMethod.func_code.co_argcount doesn't work anymore)

5 Comments

this has already been answered 3 months ago. Check the other answers before answering (and of course don't copy other answers)
I was looking for an answer to this question, and none of the listed ones were satisfactory to me. I found this answer elsewhere and placed it here in case any one comes searching for this issue through Google. I very much did check the other answers. I did not copy any answer.
look at the answer Josep Valls did the 4th of august: "This has already been answered but without the inspect module you can also use someMethod.func_code.co_argcount". Pretty similar ain't it? maybe that's why it was flagged for moderator attention.
It is similar, but it is NOT the same. It does NOT work for Python 3. func_code is not an attribute in Python 3 on functions. Instead, you use __code__. A more advanced user of Python would reasonably understand that the two versions have some major compatibility changes that can be frustrating at times to deal with. This is one such change.
that is correct. I'm editing your answer so I can remove the downvote.
2

This has already been answered but without the inspect module you can also use someMethod.func_code.co_argcount

Comments

1

Make each command a class, derived from an abstract base defining the general structure of a command. As much as possible, the definition of command properties should be put into class variables with methods defined in the base class handling that data.

Register each of these subclasses with a factory class. This factory class get the argument list an decides which command to execute by instantiating the appropriate command sub class.

Argument checking is handled by the command sub classes themselves, using properly defined general methods form the command base class.

This way, you never need to repeatedly code the same stuff, and there is really no need to emulate the switch statement. It also makes extending and adding commands very easy, as you can simply add and register a new class. Nothing else to change.

1 Comment

+1; You, sir, are funny. Given that you wrote this after the accepted answer had been posted, I'm assuming you are intentionally expressing very cogent views on defensive programming rather than attempting to describe the easiest way to count how many args the function takes.
0

Excellent question. I just had the problem that I wanted to write a function that takes a callback argument. Depending on the number of arguments of that callback, it needs to be called differently.

I started with gimel's answer, then expanded to be able to deal with builtins which don't react well with the inspect module (raise TypeError).

So here's code to check if a function expects exactly one argument:

def func_has_one_arg_only(func, typical_argument=None, ignore_varargs=False):
    """True if given func expects only one argument

    Example (testbench):
    assert not func_has_one_arg_only(dict.__getitem__), 'builtin 2 args'
    assert func_has_one_arg_only(lambda k: k), 'lambda 1 arg'
    assert not func_has_one_arg_only(lambda k,x: k), 'lambda 2 args'
    assert not func_has_one_arg_only(lambda *a: k), 'lambda *a'
    assert not func_has_one_arg_only(lambda **a: k), 'lambda **a'
    assert not func_has_one_arg_only(lambda k,**a: k), 'lambda k,**a'
    assert not func_has_one_arg_only(lambda k,*a: k), 'lambda k,*a'

    assert func_has_one_arg_only(lambda k: k, ignore_varargs=True), 'lambda 1 arg'
    assert not func_has_one_arg_only(lambda k,x: k, ignore_varargs=True), 'lambda 2 args'
    assert not func_has_one_arg_only(lambda *a: k, ignore_varargs=True), 'lambda *a'
    assert not func_has_one_arg_only(lambda **a: k, ignore_varargs=True), 'lambda **a'
    assert func_has_one_arg_only(lambda k,**a: k, ignore_varargs=True), 'lambda k,**a'
    assert func_has_one_arg_only(lambda k,*a: k, ignore_varargs=True), 'lambda k,*a'
    """

    try:
        import inspect
        argspec = inspect.getargspec(func)
    except TypeError:                   # built-in c-code (e.g. dict.__getitem__)
        try:
            func(typical_argument)
        except TypeError:
            return False
        else:
            return True
    else:
        if not ignore_varargs:
            if argspec.varargs or argspec.keywords:
                return False
        if 1 == len(argspec.args):
            return True
        return False
    raise RuntimeError('This line should not be reached')

You can control the behaviour related to varargs arguments *args and **kwargs with the ignore_varargs parameter.

The typical_argument parameter is a kludge: If inspect fails to work, e.g. on the aforementioned builtins, then we just try to call the function with one argument and see what happens.

The problem with this approach is that there are multiple reasons to raise TypeError: Either the wrong number of arguments is used, or the wrong type of arguments is used. By allowing the user to provide a typical_argument I'm trying to circumvent this issue.

This is not nice. But it may help folks having the same question and also running into the fact that inspect cannot inspect C-coded function implementations. Maybe folks have a better suggestion?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.