Programmatically determining amount of parameters a function requires – Python
Question:
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.
Answers:
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.
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
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.
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?
This has already been answered but without the inspect module you can also use someMethod.func_code.co_argcount
In Python 3, use someMethod.__code__.co_argcount
(since someMethod.func_code.co_argcount
doesn’t work anymore)
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.
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.
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
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.
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?
This has already been answered but without the inspect module you can also use someMethod.func_code.co_argcount
In Python 3, use someMethod.__code__.co_argcount
(since someMethod.func_code.co_argcount
doesn’t work anymore)