问题:如何获取方法参数名称?
鉴于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
和**kwargs
into dict
(OrderedDict
用于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.