92

我有一个 Python 函数,fetch_data它会访问远程 API,获取一些数据,然后将其返回到一个响应对象中。它看起来有点像下面:

def fetch_data(self, foo, bar, baz, **kwargs):
    response = Response()
    # Do various things, get some data
    return response

现在,响应数据可能会显示“我有更多数据,请使用递增page参数调用我以获得更多数据”。因此,我本质上想将“方法调用”(函数,参数)存储在响应对象中,这样我就可以Response.get_more()查看存储的函数和参数,并以(几乎)相同的方式再次调用该函数参数,返回一个新的Response

现在如果fetch_data被定义为fetch_data(*args, **kwargs)我可以只存储(fetch_data, args, kwargs)response. 但是我有self, foo,barbaz担心 - 我可以存储(fetch_data, foo, bar, baz, kwargs)但这是非常不希望的重复量。

本质上,我正在尝试研究如何从函数中获取完全填充的*argsand **kwargs,包括函数的命名参数。

4

8 回答 8

134

本质上,我正在尝试研究如何从函数中获取完全填充的 *args 和 **kwargs,包括函数的命名参数。

locals()在函数的开头通过保存参数怎么样?

def my_func(a, *args, **kwargs):
    saved_args = locals()
    print("saved_args is", saved_args)
    local_var = 10
    print("saved_args is", saved_args)
    print("But locals() is now", locals())

my_func(20, 30, 40, 50, kwarg1='spam', kwarg2='eggs')

它给出了这个输出:

saved_args is {'a': 20, 'args': (30, 40, 50), 'kwargs': {'kwarg1': u'spam', 'kwarg2': u'eggs'}}
saved_args is {'a': 20, 'args': (30, 40, 50), 'kwargs': {'kwarg1': u'spam', 'kwarg2': u'eggs'}}
But locals is now {'a': 20, 'saved_args': {...}, 'args': (30, 40, 50), 'local_var': 10, 'kwargs': {'kwarg1': u'spam', 'kwarg2': u'eggs'}}

帽子提示:https ://stackoverflow.com/a/3137022/2829764

于 2014-06-01T18:18:16.380 回答
42

不是我会做的事情,但您可以inspect.signature用来反省您的方法所采用的参数:

>>> import inspect
>>> def foobar(foo, bar, baz):
...     return inspect.signature(foobar)
... 
>>> foobar(1, 2, 3)
<Signature (foo, bar, baz)>

返回的Signature实例具有参数的有序集合(.parameters属性),然后可以将其与locals()生成参数值列表一起使用:

>>> def foobar(foo, bar, baz):
...     sig, foobar_locals = inspect.signature(foobar), locals()
...     return [foobar_locals[param.name] for param in sig.parameters.values()]
...
>>> foobar(1, 2, 3)
[1, 2, 3]

然而,在做高级函数装饰器之类的时候,你真的只需要这样的魔法。我觉得这里有点矫枉过正。

于 2012-05-23T17:01:19.327 回答
24

我不确定这是否正是您想要的,但locals()提供了一个局部变量字典。

>>> def foo(bar, toto):
...     print(locals())
...
>>> foo(3,'sometext')
{'toto': 'sometext', 'bar': 3}
于 2018-06-08T14:57:54.667 回答
15

我认为更 Pythonic 的方法是将你的函数变成一个生成器,yield只要服务器不断返回数据,就可以获取和读取数据。

这应该会产生整洁的代码,并使您能够避开在迭代中保留参数的所有复杂性(Python 会神奇地为您做到这一点:-))

于 2012-05-23T16:59:35.567 回答
11

inspect.getargspec自 3.0 版起已弃用。使用signature()和签名对象,它为可调用对象提供了更好的内省 API。

>>> from inspect import signature
>>> def foo(a, *, b:int, **kwargs):
...     pass

>>> sig = signature(foo)

>>> str(sig)
'(a, *, b:int, **kwargs)'

>>> str(sig.parameters['b'])
'b:int'

>>> sig.parameters['b'].annotation
<class 'int'>
于 2016-09-15T18:12:45.633 回答
9

我相信选择的方法是getcallargsfrominspect因为它返回将调用函数的真实参数。您将一个函数以及 args 和 kwargs 传递给它(inspect.getcallargs(func, *args, **kwds)),它将返回用于调用的真实方法的参数,同时考虑到默认值和其他内容。看看下面的例子。

from inspect import getcallargs

# we have a function with such signature
def show_params(first, second, third=3):
    pass

# if you wanted to invoke it with such params (you could get them from a decorator as example)
args = [1, 2, 5]
kwargs = {}
print(getcallargs(show_params, *args, **kwargs))
#{'first': 1, 'second': 2, 'third': 5}

# here we didn't specify value for d
args = [1, 2, 3, 4]
kwargs = {}

# ----------------------------------------------------------
# but d has default value =7
def show_params1(first, *second, d = 7):
    pass


print(getcallargs(show_params1, *args, **kwargs))
# it will consider b to be equal to default value 7 as it is in real method invocation
# {'first': 1, 'second': (2, 3, 4), 'd': 7}

# ----------------------------------------------------------
args = [1]
kwargs = {"d": 4}

def show_params2(first, d=3):
    pass


print(getcallargs(show_params2, *args, **kwargs))
#{'first': 1, 'd': 4}

https://docs.python.org/3/library/inspect.html

于 2019-10-23T16:08:54.123 回答
6
import inspect

def f(x, y):
    print(
        inspect.getargvalues(inspect.currentframe())
    )

f(1, 2)

结果:
ArgInfo(args=['x', 'y'], varargs=None, keywords=None, locals={'y': 2, 'x': 1})

于 2018-04-06T15:01:14.910 回答
1

kwargs 不会有 'foo'、'bar' 或 'bad' 作为键,因此您可以将这些条目(w/它们的值)添加到 kwargs 并存储(fetch_data,kwargs)。

于 2012-05-23T17:00:27.443 回答