349

Given that a function a_method has been defined like

def a_method(arg1, arg2):
    pass

Starting from a_method itself, how can I get the argument names - for example, as a tuple of strings, like ("arg1", "arg2")?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Staale
  • 27,254
  • 23
  • 66
  • 85
  • 1
    For a different list of answers to a nearly identical question, see [this other stackoverflow post](http://stackoverflow.com/questions/582056/getting-list-of-parameters-inside-python-function) – dan mackinlay Feb 04 '11 at 00:57
  • 6
    Your title is misleading: when one say 'method' w.r.t the word 'function', one usually think of a class method. For function, your selected answer (from Jouni K. Seppanen) is good. But for (class) method, it is not working and the inspect solution (from Brian) should be used. – Juh_ Aug 17 '12 at 12:46

20 Answers20

487

Take a look at the inspect module - this will do the inspection of the various code object properties for you.

>>> inspect.getfullargspec(a_method)
(['arg1', 'arg2'], None, None, None)

The other results are the name of the *args and **kwargs variables, and the defaults provided. ie.

>>> def foo(a, b, c=4, *arglist, **keywords): pass
>>> inspect.getfullargspec(foo)
(['a', 'b', 'c'], 'arglist', 'keywords', (4,))

Note that some callables may not be introspectable in certain implementations of Python. For Example, in CPython, some built-in functions defined in C provide no metadata about their arguments. As a result, you will get a ValueError if you use inspect.getfullargspec() on a built-in function.

Since Python 3.3, you can use inspect.signature() to see the call signature of a callable object:

>>> inspect.signature(foo)
<Signature (a, b, c=4, *arglist, **keywords)>
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Brian
  • 116,865
  • 28
  • 107
  • 112
  • 1
    see also http://stackoverflow.com/a/3999574/288875 on examining callables in general – Andre Holzner Dec 20 '12 at 10:30
  • 44
    How could the code possibly know that the default parameter `(4,)` corresponds to the keyword parameter `c` specifically? – fatuhoku Sep 24 '13 at 20:56
  • 86
    @fatuhoku I was wondering the same thing. Turns out it's not ambiguous since you can only add default arguments at the end in a contiguous block. From the docs: "if this tuple has n elements, they correspond to the last n elements listed in args" – Soverman Oct 03 '13 at 22:31
  • 9
    I think since Python 3.x getargspec(...) is replaced by inspector.signature(func) – Diego Andrés Díaz Espinoza Aug 03 '16 at 14:35
  • 1
    Just emphasize the fact that this doesn't work with built in since I recently discovered that if the function is in C it seems it won't work for some reason, see: http://stackoverflow.com/questions/39194325/why-cant-i-see-the-arguments-to-the-print-function-imported-from-future. – Charlie Parker Aug 28 '16 at 18:36
  • 2
    Changed in version 2.6: Returns a named tuple ArgSpec(args, varargs, keywords, defaults). – theannouncer Jan 19 '17 at 01:26
  • 5
    That is right, @DiegoAndrésDíazEspinoza - in Python 3, `inspect.getargspec` is [deprecated](https://docs.python.org/3/library/inspect.html#inspect.getargspec), but the replacement is [`inspect.getfullargspec`](https://docs.python.org/3/library/inspect.html#inspect.getfullargspec). – j08lue Jan 12 '18 at 07:48
  • 2
    It's probably frowned upon here but as a German I cannot resist, sorry about that, but I somehow like the parameter name 'arglist'. – Hendrik Wiese Jul 13 '18 at 11:43
  • 1
    `>>> inspect.getargspec(api_function)` `./manage.py:1: DeprecationWarning: inspect.getargspec() is deprecated, use inspect.signature() or inspect.getfullargspec()` `#!/usr/bin/env python3` – Blairg23 Jan 25 '19 at 22:20
  • 1
    You can now do `inspect.signature(api_function).bind(*args).parameters` as mentioned in this answer: https://stackoverflow.com/a/45781963/2132312 – Matthew D. Scholefield Mar 08 '19 at 14:46
  • 1
    what if we would like to get the list of possible values for each argument? Is there a way? – Kiann Oct 01 '19 at 22:17
105

In CPython, the number of arguments is

a_method.func_code.co_argcount

and their names are in the beginning of

a_method.func_code.co_varnames

These are implementation details of CPython, so this probably does not work in other implementations of Python, such as IronPython and Jython.

One portable way to admit "pass-through" arguments is to define your function with the signature func(*args, **kwargs). This is used a lot in e.g. matplotlib, where the outer API layer passes lots of keyword arguments to the lower-level API.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Jouni K. Seppänen
  • 43,139
  • 5
  • 71
  • 100
  • 2
    co_varnames does work with standard Python, but this method is not preferable since it will also display the internal arguments. – MattK Sep 28 '10 at 16:39
  • 13
    Why not use aMethod.func_code.co_varnames[:aMethod.func_code.co_argcount]? – hochl Mar 02 '11 at 14:13
  • 1
    Not working with arguments after `*args`, e.g.: `def foo(x, *args, y, **kwargs): # foo.__code__.co_argcount == 1` – Nikolay Makhalin Aug 21 '19 at 18:14
  • @Nikolay see https://stackoverflow.com/questions/147816/preserving-signatures-of-decorated-functions – Brian McCutchon Oct 09 '19 at 20:24
  • 3
    Please use inspect instead. Otherwise, your code doesn't work well with functools.wraps in 3.4+. See https://stackoverflow.com/questions/147816/preserving-signatures-of-decorated-functions – Brian McCutchon Oct 09 '19 at 20:25
40

The Python 3 version is:

def _get_args_dict(fn, args, kwargs):
    args_names = fn.__code__.co_varnames[:fn.__code__.co_argcount]
    return {**dict(zip(args_names, args)), **kwargs}

The method returns a dictionary containing both args and kwargs.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
argaen
  • 4,145
  • 24
  • 28
25

In a decorator method, you can list arguments of the original method in this way:

import inspect, itertools 

def my_decorator():   
        def decorator(f):
            def wrapper(*args, **kwargs):
                # if you want arguments names as a list:
                args_name = inspect.getargspec(f)[0]
                print(args_name)

                # if you want names and values as a dictionary:
                args_dict = dict(itertools.izip(args_name, args))
                print(args_dict)

                # if you want values as a list:
                args_values = args_dict.values()
                print(args_values)

If the **kwargs are important for you, then it will be a bit complicated:

def wrapper(*args, **kwargs):
    args_name = list(OrderedDict.fromkeys(inspect.getargspec(f)[0] + kwargs.keys()))
    args_dict = OrderedDict(list(itertools.izip(args_name, args)) + list(kwargs.iteritems()))
    args_values = args_dict.values()

Example:

@my_decorator()
def my_function(x, y, z=3):
    pass


my_function(1, y=2, z=3, w=0)
# prints:
# ['x', 'y', 'z', 'w']
# {'y': 2, 'x': 1, 'z': 3, 'w': 0}
# [1, 2, 3, 0]
Michael M.
  • 10,486
  • 9
  • 18
  • 34
Mehdi Behrooz
  • 2,904
  • 1
  • 17
  • 6
21

I think what you're looking for is the locals method -


In [6]: def test(a, b):print locals()
   ...: 

In [7]: test(1,2)              
{'a': 1, 'b': 2}
Damian
  • 2,588
  • 23
  • 19
18

Python 3.5+:

DeprecationWarning: inspect.getargspec() is deprecated since Python 3.0, use inspect.signature() or inspect.getfullargspec()

So previously:

func_args = inspect.getargspec(function).args

Now:

func_args = list(inspect.signature(function).parameters.keys())

To test:

'arg' in list(inspect.signature(function).parameters.keys())

Given that we have function 'function' which takes argument 'arg', this will evaluate as True, otherwise as False.

Example from the Python console:

Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (Intel)] on win32
>>> import inspect
>>> 'iterable' in list(inspect.signature(sum).parameters.keys())
True
Dave Liu
  • 906
  • 1
  • 11
  • 31
Peter Majko
  • 1,241
  • 11
  • 22
  • 1
    If you only want a param list, then `list(inspect.signature(function).parameters)` is enough, you don't need to call the `.keys()` method. Anyway, this is a great answer. – Michał Jabłoński Jan 17 '22 at 09:49
15

Here is something I think will work for what you want, using a decorator.

class LogWrappedFunction(object):
    def __init__(self, function):
        self.function = function

    def logAndCall(self, *arguments, **namedArguments):
        print "Calling %s with arguments %s and named arguments %s" %\
                      (self.function.func_name, arguments, namedArguments)
        self.function.__call__(*arguments, **namedArguments)

def logwrap(function):
    return LogWrappedFunction(function).logAndCall

@logwrap
def doSomething(spam, eggs, foo, bar):
    print "Doing something totally awesome with %s and %s." % (spam, eggs)


doSomething("beans","rice", foo="wiggity", bar="wack")

Run it, it will yield the following output:

C:\scripts>python decoratorExample.py
Calling doSomething with arguments ('beans', 'rice') and named arguments {'foo':
 'wiggity', 'bar': 'wack'}
Doing something totally awesome with beans and rice.
hlzr
  • 378
  • 1
  • 7
13

In Python 3.+ with the Signature object at hand, an easy way to get a mapping between argument names to values, is using the Signature's bind() method!

For example, here is a decorator for printing a map like that:

import inspect

def decorator(f):
    def wrapper(*args, **kwargs):
        bound_args = inspect.signature(f).bind(*args, **kwargs)
        bound_args.apply_defaults()
        print(dict(bound_args.arguments))

        return f(*args, **kwargs)

    return wrapper

@decorator
def foo(x, y, param_with_default="bars", **kwargs):
    pass

foo(1, 2, extra="baz")
# This will print: {'kwargs': {'extra': 'baz'}, 'param_with_default': 'bars', 'y': 2, 'x': 1}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kfir Eisner
  • 493
  • 5
  • 3
  • This decorates the function so that it reports the arguments that were passed and then does whatever it was going to do. That's related and useful, but OP wanted parameters, not arguments, and this interface is also a bit awkward (really just a proof of concept). – Karl Knechtel Jan 14 '23 at 06:18
11

Here is another way to get the function parameters without using any module.

def get_parameters(func):
    keys = func.__code__.co_varnames[:func.__code__.co_argcount][::-1]
    sorter = {j: i for i, j in enumerate(keys[::-1])} 
    values = func.__defaults__[::-1]
    kwargs = {i: j for i, j in zip(keys, values)}
    sorted_args = tuple(
        sorted([i for i in keys if i not in kwargs], key=sorter.get)
    )
    sorted_kwargs = {
        i: kwargs[i] for i in sorted(kwargs.keys(), key=sorter.get)
    }   
    return sorted_args, sorted_kwargs


def f(a, b, c="hello", d="world"): var = a
    

print(get_parameters(f))

Output:

(('a', 'b'), {'c': 'hello', 'd': 'world'})
dildeolupbiten
  • 1,314
  • 1
  • 15
  • 27
5

inspect.signature is very slow. Fastest way is

def f(a, b=1, *args, c, d=1, **kwargs):
   pass

f_code = f.__code__
f_code.co_varnames[:f_code.co_argcount + f_code.co_kwonlyargcount]  # ('a', 'b', 'c', 'd')
2

Update for Brian's answer:

If a function in Python 3 has keyword-only arguments, then you need to use inspect.getfullargspec:

def yay(a, b=10, *, c=20, d=30):
    pass
inspect.getfullargspec(yay)

yields this:

FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(10,), kwonlyargs=['c', 'd'], kwonlydefaults={'c': 20, 'd': 30}, annotations={})
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ASMik09
  • 33
  • 5
2

Returns a list of argument names, takes care of partials and regular functions:

def get_func_args(f):
    if hasattr(f, 'args'):
        return f.args
    else:
        return list(inspect.signature(f).parameters)
lovesh
  • 5,235
  • 9
  • 62
  • 93
2

In python 3, below is to make *args and **kwargs into a dict (use OrderedDict for python < 3.6 to maintain dict orders):

from functools import wraps

def display_param(func):
    @wraps(func)
    def wrapper(*args, **kwargs):

        param = inspect.signature(func).parameters
        all_param = {
            k: args[n] if n < len(args) else v.default
            for n, (k, v) in enumerate(param.items()) if k != 'kwargs'
        }
        all_param .update(kwargs)
        print(all_param)

        return func(**all_param)
    return wrapper
Alpha
  • 2,372
  • 3
  • 21
  • 23
1

Simple easy to read answer as of python 3.0 onwards:

import inspect


args_names = inspect.signature(function).parameters.keys()
args_dict = {
    **dict(zip(args_names, args)),
    **kwargs,
}


Jose Enrique
  • 361
  • 1
  • 3
  • 8
1

Easiest way to manipulate parameters names of some function:

parameters_list = list(inspect.signature(self.YOUR_FUNCTION).parameters))

Result:

['YOUR_FUNCTION_parameter_name_0', 'YOUR_FUNCTION_parameter_name_1', ...]

Making this way will be even easier since you get the specific one:

parameters_list = list(inspect.signature(self.YOUR_FUNCTION).parameters)[0]

Result:

'YOUR_FUNCTION_parameter_name_0'
1

I have another suggestion for those who, like me, are looking for a solution that puts inside a decorator all parameters and their values (default or not) into a dictonary.

import inspect

def get_arguments(func, args, kwargs, is_method=False):
    offset = 1 if is_method else 0
    specs = inspect.getfullargspec(func)
    d = {}
    for i, parameter in enumerate(specs.args[offset:]):
        i += offset
        if i < len(args):
            d[parameter] = args[i]
        elif parameter in kwargs:
            d[parameter] = kwargs[parameter]
        else:
            d[parameter] = specs.defaults[i - len(args)]
    return d

Now printing the return value of get_arguments inside a decorator like this one

def a_function_decorator(func):
    def inner(*args, **kwargs):
        print(get_arguments(func, args, kwargs))
        return func(*args, **kwargs)

    return inner

and apply it on a function like

@a_function_decorator
def foo(a, b, c="default_c", d="default_d"):
    pass

will give us

foo(1, 2, d="eek")
# {'a': 1, 'b': 2, 'c': 'default_c', 'd': 'eek'}

foo(1, 2, "blah")
# {'a': 1, 'b': 2, 'c': 'blah', 'd': 'default_c'}

foo(1, 2)
# {'a': 1, 'b': 2, 'c': 'default_c', 'd': 'default_d'}

Same works for methods

def a_method_decorator(func):
    def inner(*args, **kwargs):
        print(get_arguments(func, args, kwargs, is_method=True))
        return func(*args, **kwargs)

    return inner

class Bar:
    @a_method_decorator
    def foo(self, a, b, c="default_c", d="default_d"):
        pass

Bar().foo(1, 2, d="eek")
#{'a': 1, 'b': 2, 'c': 'default_c', 'd': 'eek'}
Bar().foo(1, 2, "blah")
# {'a': 1, 'b': 2, 'c': 'blah', 'd': 'default_c'}
Bar().foo(1, 2)
# {'a': 1, 'b': 2, 'c': 'default_c', 'd': 'default_d'}

It's certainly not the prettiest solution, but it's the first one I've seen that does exactly what I want.

Zio
  • 31
  • 3
0

To update a little bit Brian's answer, there is now a nice backport of inspect.signature that you can use in older python versions: funcsigs. So my personal preference would go for

try:  # python 3.3+
    from inspect import signature
except ImportError:
    from funcsigs import signature

def aMethod(arg1, arg2):
    pass

sig = signature(aMethod)
print(sig)

For fun, if you're interested in playing with Signature objects and even creating functions with random signatures dynamically you can have a look at my makefun project.

smarie
  • 4,568
  • 24
  • 39
-1

I was googling to find how to print function name and supplied arguments for an assignment I had to create a decorator to print them and I used this:

def print_func_name_and_args(func):
    
    def wrapper(*args, **kwargs):
    print(f"Function name: '{func.__name__}' supplied args: '{args}'")
    func(args[0], args[1], args[2])
    return wrapper


@print_func_name_and_args
def my_function(n1, n2, n3):
    print(n1 * n2 * n3)
    
my_function(1, 2, 3)

#Function name: 'my_function' supplied args: '(1, 2, 3)'
Brisco
  • 99
  • 9
-1

Is it possible to use inspect API to read constant argument value -1 from the lambda func fun in the code below?

def my_func(v, axis):
  pass

fun = lambda v: my_func(v, axis=-1)
x4444
  • 2,092
  • 1
  • 13
  • 11
-3

What about dir() and vars() now?

Seems doing exactly what is being asked super simply…

Must be called from within the function scope.

But be wary that it will return all local variables so be sure to do it at the very beginning of the function if needed.

Also note that, as pointed out in the comments, this doesn't allow it to be done from outside the scope. So not exactly OP's scenario but still matches the question title. Hence my answer.

jeromej
  • 10,508
  • 2
  • 43
  • 62
  • dir() returns list of all variable names ['var1', 'var2'], vars() returns dictionary in form {'var1': 0, 'var2': 'something'} from within the current local scope. If someone wants to use argument variables names later in the function, they should save in another local variable, because calling it later in the function where they could declare another local variables will "contaminate" this list. In the case they want to use it outside of the function, they must run function at least once and save it in global variable. So it's better to use inspect module. – Peter Majko Jul 26 '17 at 16:10