如何获取方法参数的名称?

353

假设已经定义了一个名为a_method的函数,如下所示:

def a_method(arg1, arg2):
    pass

a_method方法本身开始,我该如何获得参数名称 - 例如,作为字符串元组,如("arg1", "arg2")


1
对于一个几乎相同的问题的不同答案列表,请参见这个其他的stackoverflow帖子 - dan mackinlay
6
你的标题有误导性:当某人在谈到“函数”时提到“方法”,通常会想到一个类方法。对于函数,你选择的答案(来自Jouni K. Seppanen)很好。但是对于(类)方法,它不起作用,应该使用Brian提供的inspect解决方案。 - Juh_
20个回答

491

请看inspect模块 - 这将为您检查各种代码对象属性。

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

其他结果是`*args`和`**kwargs`变量的名称以及提供的默认值。例如:
>>> def foo(a, b, c=4, *arglist, **keywords): pass
>>> inspect.getfullargspec(foo)
(['a', 'b', 'c'], 'arglist', 'keywords', (4,))

请注意,在某些Python实现中,一些可调用对象可能无法进行内省。例如,在CPython中,一些在C中定义的内置函数不提供有关其参数的元数据。因此,如果您在内置函数上使用inspect.getfullargspec(),则会收到ValueError
自Python 3.3以来,您可以使用inspect.signature()查看可调用对象的调用签名:
>>> inspect.signature(foo)
<Signature (a, b, c=4, *arglist, **keywords)>

44
这段代码怎么可能知道默认参数(4,)对应的是特定的关键字参数c - fatuhoku
86
@fatuhoku 我也曾经想过同样的问题。事实证明,这并不含糊,因为你只能在连续块的末尾添加默认参数。从文档中可以看到:“如果这个元组有n个元素,它们对应于args中列出的最后n个元素。” - Soverman
9
Python 3.x中,getargspec(...)被inspector.signature(func)所替代。 - Diego Andrés Díaz Espinoza
2
从2.6版本开始更改:返回一个命名元组ArgSpec(args,varargs,keywords,defaults)。 - theannouncer
5
没错,@DiegoAndrésDíazEspinoza - 在Python 3中,inspect.getargspec已被弃用,但替代方法是inspect.getfullargspec - j08lue
显示剩余6条评论

105
在CPython中,参数的数量是:
a_method.func_code.co_argcount

它们的名称位于开头

a_method.func_code.co_varnames

这些是CPython的实现细节,所以在其他Python实现(如IronPython和Jython)中可能不起作用。

一种可移植的传递参数的方法是使用带有func(*args, **kwargs)签名的函数定义。这在例如matplotlib中经常使用,其中外部API层将许多关键字参数传递给较低级别的API。


2
co_varnames 在标准 Python 中可以使用,但这种方法并不理想,因为它还会显示内部参数。 - MattK
13
为什么不使用aMethod.func_code.co_varnames[:aMethod.func_code.co_argcount]? - hochl
1
不支持在 *args 后面使用参数,例如: def foo(x, *args, y, **kwargs): # foo.__code__.co_argcount == 1 - Nikolay Makhalin
@Nikolay 请查看 https://dev59.com/lnVC5IYBdhLWcg3w51ry - Brian McCutchon
3
请使用inspect替代。否则,在3.4+版本中,您的代码与functools.wraps无法很好地配合使用。请参见https://dev59.com/lnVC5IYBdhLWcg3w51ry。 - Brian McCutchon

40

Python 3 版本是:

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

该方法返回一个包含args和kwargs两者的字典。


5
请注意 [:fn.__code__.co_argcount] 如果你要查找函数参数非常重要 --否则它会包括函数内部创建的名称。 - Soren Bjornstad
其中一个问题是它不能显示参数是 *args 还是 **kwargs - Ciro Santilli OurBigBook.com
整洁的解决方案。如果可以推广到实例方法和类方法,那就更好了,因为偏移量需要从1开始以跳过self/cls参数。 - Dmytro Bugayev
哪一个更好?fn.__code__.co_varnames[:fn.__code__.co_argcount] 还是 inspect.getargspec(f)[0] - Dust break

25

在装饰器方法中,您可以按照以下方式列出原始方法的参数:

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)
如果**kwargs对你很重要,那么会有点复杂:
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()

示例:

@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]

4
此答案部分已过时,需要更新。 - Imago
2
这段代码甚至无法运行。 - Nicholas Jela

22

我认为你正在寻找的是locals方法 -


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

In [7]: test(1,2)              
{'a': 1, 'b': 2}

12
这段话的意思是:这个东西在函数之外没有用处,而我们现在关注的是函数中的语境(装饰器)。我的翻译如下:这在一个函数之外没有用处,而我们现在要关注的是函数内部的语境,也就是装饰器。 - Piotr Dobrogost
17
实际上这正是我在寻找的,尽管它并不是这里问题的答案。 - javabeangrinder

19

Python 3.5+:

DeprecationWarning: inspect.getargspec()自Python 3.0开始已弃用,使用inspect.signature()或inspect.getfullargspec()

因此以前:

func_args = inspect.getargspec(function).args

现在:

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

测试:

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

假设我们有一个名为“function”的函数,它需要参数“arg”,这个函数将计算为True,否则为False。

以下是Python控制台的示例:

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

1
如果你只需要参数列表,那么 list(inspect.signature(function).parameters) 就足够了,不需要调用 .keys() 方法。无论如何,这是一个很好的答案。 - Michał Jabłoński

15

我认为以下内容可以满足你的需求,使用一个装饰器来实现。

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")

运行它,它将产生以下输出:

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.

14
在Python 3.+版本中,使用Signature对象,一种轻松的获取参数名称到值映射的方法是使用bind()方法!例如,以下是一个用于打印这样的映射的装饰器:
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}

这个修饰器可以使函数报告传递的参数,然后执行它原本要做的事情。虽然相关且有用,但 OP 想要的是参数而不是参数,并且这个接口也有点笨拙(实际上只是一个概念验证)。 - Karl Knechtel

11

这里有另一种方法可以在不使用任何模块的情况下获取函数参数。

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))

输出:

(('a', 'b'), {'c': 'hello', 'd': 'world'})

5

inspect.signature非常缓慢。最快的方法是

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 包含关键字参数,因此不需要 f_code.co_kwonlyargcount - Thingamabobs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接