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")?
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")?
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)> (4,) corresponds to the keyword parameter c specifically?inspect.getargspec is deprecated, but the replacement is inspect.getfullargspec.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.
*args, e.g.: def foo(x, *args, y, **kwargs): # foo.__code__.co_argcount == 1The 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.
[:fn.__code__.co_argcount] is very important if you're looking for the function arguments -- otherwise it includes names created within the function as well.*args or **kwargs.fn.__code__.co_varnames[:fn.__code__.co_argcount] or inspect.getargspec(f)[0]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] 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 list(inspect.signature(function).parameters) is enough, you don't need to call the .keys() method. Anyway, this is a great answer.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} 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. 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} 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'}) f(a, b, c="hello", d="world", *args, x="!", **kwargs). func.__kwdefaults__ is then required to get x. Nevertheless I still do not know how to differenciate *args and **kwargs when only one of them if used.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') co_argcount includes keywords, so f_code.co_kwonlyargcount is not needed.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={}) 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 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' 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.
get_arguments() function in the case when positional args fill the function kwarg arguments. The else statement should contain d[parameter] = specs.defaults[i - non_default_args] where non_default_args = len(specs.args) - (len(specs.defaults) if specs.defaults else 0).IndexError when too many arguments are passed, so that should be caught and re-raised as the more desirable TypeError instead.Here is a solution without the inspect module.
def getfullargspec(func): # Get code object attributes code = func.__code__ varnames = code.co_varnames argcount = code.co_argcount kwonlyargcount = getattr(code, 'co_kwonlyargcount', 0) # Get all arguments pos_args = varnames[0:argcount] kwonly_args = varnames[argcount:argcount + kwonlyargcount] # Check for *args and **kwargs has_varargs = bool(code.co_flags & 0x04) has_varkw = bool(code.co_flags & 0x08) # Get varargs and varkw names if they exist varargs = None varkw = None if has_varargs: varargs = varnames[argcount + kwonlyargcount] if has_varkw: offset = 1 if has_varargs else 0 varkw = varnames[argcount + kwonlyargcount + offset] # Get defaults for positional arguments defaults = func.__defaults__ if func.__defaults__ else () # Get defaults for keyword-only arguments kwdefaults = func.__kwdefaults__ if func.__kwdefaults__ else {} return { 'args': list(pos_args), 'varargs': varargs, 'varkw': varkw, 'defaults': defaults, 'kwonlyargs': list(kwonly_args), 'kwonlydefaults': kwdefaults, } Compared to other proposed versions, (1) it takes into account defaut key-word arguments using __kwdefaults__ and (2) differentiate between varargs and kwargs at using co_flags.
For example:
def func1(a, b, c="hello", d="world", *args, x="!", **kwargs): ... def func2(a, b, c="hello", d="world", *args, x="!"): ... def func3(a, b, c="hello", d="world", **kwargs): ... def func4(a, b, c="hello", d="world", *args): ... flist = [func1, func2, func3, func4] for func in flist: print(f"{func.__name__}") print(getfullargspec(func)) gives
func1 {'args': ['a', 'b', 'c', 'd'], 'varargs': 'args', 'varkw': 'kwargs', 'defaults': ('hello', 'world'), 'kwonlyargs': ['x'], 'kwonlydefaults': {'x': '!'}} func2 {'args': ['a', 'b', 'c', 'd'], 'varargs': 'args', 'varkw': None, 'defaults': ('hello', 'world'), 'kwonlyargs': ['x'], 'kwonlydefaults': {'x': '!'}} func3 {'args': ['a', 'b', 'c', 'd', 'x'], 'varargs': None, 'varkw': 'kwargs', 'defaults': ('hello', 'world', '!'), 'kwonlyargs': [], 'kwonlydefaults': {}} func4 {'args': ['a', 'b', 'c', 'd', 'x'], 'varargs': 'args', 'varkw': None, 'defaults': ('hello', 'world', '!'), 'kwonlyargs': [], 'kwonlydefaults': {}} 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.
I faced this problem today while doing a course so I decided to share what I found.
Try using built in attribute for functions and class methods called "__code__" where you have a lot of different attributes like "co_varnames" what I believe you were looking for. So in your case line: parameters = a_method.__code__.co_varnames will return tuple with strings that are your parameters names.
parameters = a_method.__code__.co_varnames print(parameters) output:
("arg1", "arg2")
Other example:
class A: def function_1(self, a, b, c): return a + b + c def function_2(a, b, c): return a + b + c print(A.function_1.__code__.co_varnames) print(function_2.__code__.co_varnames) output:
('self', 'a', 'b', 'c')
('a', 'b', 'c')
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)' 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.