如何列出Python函数期望的变量?
我在想,能不能在调用一个Python函数之前,先列出这个函数需要的变量,这样我就可以从一个包含很多变量的大字典里,传递这些需要的变量。
我在网上搜索过,但没找到相关的信息。不过,Python解释器可以显示出需要的变量列表,所以肯定有办法在脚本里做到这一点吧?
3 个回答
简单明了:
import inspect #library to import
def foo(bar, baz, spam='eggs', *monty, **python): pass #example function
argspec = inspect.signature(foo)
print(argspec) #print your output
输出结果是: (bar, baz, spam='eggs', *monty, **python)
这个方法在类里面的方法中也能用(非常有用!):
class Complex: #example Class
def __init__(self, realpart, imagpart): #method inside Class
... self.r = realpart
... self.i = imagpart
argspec = inspect.signature(Complex)
print(argspec)
输出结果是: (realpart, imagpart)
作为一个附带的回答,我现在使用另一种方法来传递函数所需的变量:我把所有变量都传过去。
我的意思是,我在根对象中维护一个全局/共享的变量字典(这个根对象是所有其他对象的父对象),例如:
shareddict = {'A': 0, 'B':'somestring'}
然后我只需将这个字典传递给任何需要调用的其他对象的方法,就像这样:
shareddict.update(call_to_func(**shareddict))
正如你所看到的,我们将shareddict中的所有键值对展开,作为关键字参数传递给call_to_func()。我们还会用返回的结果更新shareddict,下面会解释原因。
现在使用这种技术,我可以简单明了地在我的函数/方法中定义是否需要这个字典中的一个或多个变量:
my_method1(A=None, *args, **kwargs):
''' This method only computes on A '''
new_A = Do_some_stuff(A)
return {'A': new_A} # Return the new A in a dictionary to update the shared value of A in the shareddict
my_method2(B=None, *args, **kwargs):
''' This method only computes on B '''
new_B = Do_some_stuff(B)
return {'B': new_B} # Return the new B in a dictionary to update the shareddict
my_method3(A=None, B=None, *args, **kwargs):
''' This method swaps A and B, and then create a new variable C '''
return {'A': B, 'B': A, 'C': 'a_new_variable'} # Here we will update both A and B and create the new variable C
你会注意到,上面所有的方法都返回一个变量字典,这个字典会更新shareddict,并且会传递给其他函数。
这种技术有几个优点:
- 实现起来相当简单
- 优雅地维护一个共享的变量列表,但不需要使用全局变量
- 函数和方法在定义时清楚地显示它们需要什么(当然有一个注意事项,即即使是必需的变量也需要设置为带有默认值(如None)的关键字参数,这通常意味着这个变量是可选的,但在这里并不是)
- 这些方法可以被继承和重载
- 内存占用低,因为同一个shareddict会一直传递
- 子函数/方法定义它们需要什么(自下而上),而不是根对象定义将传递给子对象的参数(自上而下)
- 创建/更新变量非常简单
- 可选地,将所有这些变量导出到文件中非常简单,例如使用
json.dumps(finaldict, sort_keys=True)
。
你可以使用 inspect.signature()
或者 inspect.getfullargspec()
这两个函数:
import inspect
argspec = inspect.getfullargspec(somefunction)
signature = inspect.signature(somefunction)
inspect.fullargspec
会返回一个包含7个元素的命名元组:
- 一个包含参数名称的列表
- 如果定义了一个接收所有位置参数的
*args
参数,它的名称;否则为None
- 如果定义了一个接收所有关键字参数的
**kwargs
参数,它的名称;否则为None
- 一个元组,里面是关键字参数的默认值;这些默认值与参数的最后几个元素对应,数量要一致。
- 一个只接受关键字参数的参数名称列表
- 如果有的话,包含只接受关键字参数的默认值的字典
- 还有一个包含注解的字典
使用 inspect.signature()
你会得到一个 Signature
对象,这个对象不仅把上面的数据以更结构化的方式呈现出来,还可以像调用函数一样把值绑定到参数上。
哪个更好取决于你的具体需求。
示例:
>>> import inspect
>>> def foo(bar, baz, spam='eggs', *monty, python: "kwonly", spanish=42, **inquisition) -> "return annotation":
... pass
...
>>> inspect.getfullargspec(foo)
FullArgSpec(args=['bar', 'baz', 'spam'], varargs='monty', varkw='inquisition', defaults=('eggs',), kwonlyargs=['python', 'spanish'], kwonlydefaults={'spanish': 42}, annotations={'return': 'return annotation', 'python': 'kwonly'})
>>> signature = inspect.signature(foo)
>>> signature
<Signature (bar, baz, spam='eggs', *monty, python: 'kwonly', spanish=42, **inquisition) -> 'return annotation'>
>>> signature.parameters['python'].kind.description
'keyword-only'
>>> signature.bind('Eric', 'Idle', 'John', python='Cleese')
<BoundArguments (bar='Eric', baz='Idle', spam='John', python='Cleese')>
如果你有一个名为 values
的字典,里面是可能的参数值,我会使用 inspect.signature()
,并利用 Signature.parameters
映射来匹配名称:
posargs = [
values[param.name]
for param in signature.parameters.values()
if param.kind is Parameter.POSITIONAL_ONLY
]
skip_kinds = {Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD}
kwargs = {
param.name: values[param.name]
for param in signature.parameters.values()
if param.name in values and param.kind not in skip_kinds
}
上面的操作会给你一个位置参数的值列表,以及一个字典用于其他参数(不包括任何 *args
或 **kwargs
参数)。