问题:如何获取方法参数名称?

鉴于Python函数:

def a_method(arg1, arg2):
    pass

如何提取参数的数量和名称。即,鉴于我有提及func,因此我希望func.[something]返回("arg1", "arg2")

这样做的使用场景是我有一个装饰器,并且我希望以与实际函数作为键一样的顺序使用方法参数。即,"a,b"我调用时装饰工的外观如何a_method("a", "b")

Given the Python function:

def a_method(arg1, arg2):
    pass

How can I extract the number and names of the arguments. I.e., given that I have a reference to func, I want the func.[something] to return ("arg1", "arg2").

The usage scenario for this is that I have a decorator, and I wish to use the method arguments in the same order that they appear for the actual function as a key. I.e., how would the decorator look that printed "a,b" when I call a_method("a", "b")?


回答 0

看一下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中定义的某些内置函数不提供有关其参数的元数据。结果,ValueError如果您inspect.getfullargspec()在内置函数上使用,将得到一个。

从Python 3.3开始,您可以inspect.signature()用来查看可调用对象的调用签名:

>>> inspect.signature(foo)
<Signature (a, b, c=4, *arglist, **keywords)>

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

回答 1

在CPython中,参数数量为

a_method.func_code.co_argcount

他们的名字在

a_method.func_code.co_varnames

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

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

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.


回答 2

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

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]

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]

回答 3

我认为您要寻找的是locals方法-


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

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

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}

回答 4

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的字典。

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.


回答 5

我认为这可以使用装饰器满足您的需求。

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.

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.

回答 6

Python 3.5以上版本:

DeprecationWarning:不建议使用inspect.getargspec(),而应使用inspect.signature()代替

所以以前:

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

Python 3.5+:

DeprecationWarning: inspect.getargspec() is deprecated, use inspect.signature() instead

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

回答 7

在带有Signature对象的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}

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}

回答 8

这是无需使用任何模块即可获取功能参数的另一种方法。

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 = {}
    for i in sorted(kwargs.keys(), key=sorter.get):
        sorted_kwargs[i] = kwargs[i]      
    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'})

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

回答 9

返回参数名称列表,负责部分函数和常规函数:

def get_func_args(f):
    if hasattr(f, 'args'):
        return f.args
    else:
        return list(inspect.signature(f).parameters)

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)

回答 10

更新Brian的答案

如果Python 3中的函数具有仅关键字参数,则需要使用inspect.getfullargspec

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

产生这个:

FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(10,), kwonlyargs=['c', 'd'], kwonlydefaults={'c': 20, 'd': 30}, annotations={})

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={})

回答 11

在python 3中,下面是make *args**kwargsinto dictOrderedDict用于python <3.6维护dict命令):

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

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

回答 12

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

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

回答 13

要稍微更新Brian的答案,现在inspect.signature可以在较旧的python版本中使用它的一个很好的反向端口:funcsigs。所以我个人的喜好会

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

def aMethod(arg1, arg2):
    pass

sig = signature(aMethod)
print(sig)

有趣的是,如果您有兴趣玩Signature对象甚至动态创建带有随机签名的函数,您可以看看我的makefun项目。

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.


回答 14

怎么样dir()vars()现在?

似乎完全可以简单地完成被要求的工作……

必须在功能范围内调用。

但请注意,它将返回所有局部变量,因此请确保在函数的开始处进行此操作(如果需要)。

还要注意,正如注释中指出的那样,这不允许在范围之外进行操作。因此,OP的情况不完全相同,但仍与问题标题匹配。因此,我的答案。

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.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。