获取传递给函数的参数列表/元组/字典?

112

给定以下函数:

def foo(a, b, c):
    pass

如何获取传递的参数列表/元组/字典等,而无需自己构建结构
具体而言,我正在寻找 Python 版本的 JavaScript 的 arguments 关键字或 PHP 的 func_get_args() 方法。
想使用 *args**kwargs 来解决问题;我需要在函数定义中指定参数名称(以确保它们被传递),但在函数内部,我希望以列表或字典形式处理它们。

1
请不要这样做。将完全有效的关键字参数创建成一个无用的字典,会让维护您代码的人员感到非常困惑。他们将被迫重写代码以删除该字典。 - S.Lott
2
@blahdiblah 这个问题是在询问如何获取参数本身,而不是参数的名称。我认为这不是一个重复的问题。 - Anderson Green
17
@S.Lott: 那是一个相当狭隘的观点。有时候我们想要在函数内构建一个字典,并用一些必要的参数进行初始化。函数参数清单可以清晰地表明这些参数,而且请相信我,它们并不一定是“无用”的。请参考Python的DRY原则,并不要像我曾经合作过的一些初级开发人员那样抱怨“为什么有人会想做那个?”当我建议他们的API可以更加灵活时。 - Michael Scheper
8个回答

129
你可以使用locals()函数获取函数内的局部变量字典,像这样:
def foo(a, b, c):
    print locals()

>>> foo(1, 2, 3)
{'a': 1, 'c': 3, 'b': 2}

这种方法有点取巧,因为locals()返回的是本地范围内的所有变量,而不仅仅是传递给函数的参数。因此,如果你不将其放在函数的最顶部调用,结果可能包含比你想要的更多信息:

def foo(a, b, c):
    x = 4
    y = 5
    print locals()

>>> foo(1, 2, 3)
{'y': 5, 'x': 4, 'c': 3, 'b': 2, 'a': 1}

我更愿意在函数顶部构建一个字典或列表,其中包含你需要的变量,就像其他答案中建议的那样。在我看来,这更明确地传达了你代码的意图,以更清晰的方式进行交流。


13
谢谢。如果其他人想使用这种方法,请注意在函数定义之后直接使用 locals(),否则它将包含在函数内定义的所有变量。 - Phillip B Oldham
5
有趣的是,locals()返回的是局部变量的快照,而不是指向某个内置字典的指针。因此,如果稍后定义了变量或者在函数内更改了参数,那么locals()所分配的内容将不会发生变化。这正是你想要的(在Python中)。 - Michael Scheper
5
值得注意的是,如果您在方法中使用locals()函数,字典中会有一个键名为'self'。因此,请确保进行更正/调整。 - Ben
如何处理以下情况? def foo(*args, **kwargs) 如何获取 *args 的字典? - Dust break

14

这里是一个函数,您可以调用它来获取当前函数的kwargs。或者,如果您想直接在自己的函数中使用这些行而不是调用get_kwargs(),只需删除.f_back

import inspect


def get_kwargs():
    frame = inspect.currentframe().f_back
    keys, _, _, values = inspect.getargvalues(frame)
    kwargs = {}
    for key in keys:
        if key != 'self':
            kwargs[key] = values[key]
    return kwargs


def test(x, y=100):
    z = 7
    print(get_kwargs())


test(5, 6)
# Output: {'x': 5, 'y': 6}
test(5)
# Output: {'x': 5, 'y': 100}

1
如果使用关键字参数而不是位置参数也可以正常工作。 - Eric Smith
我认为这是显而易见的正确答案,因为它是明确的,但是在查看文档时,inspect.getargvalues的第四个参数返回当前帧的locals()。因此,有效地调用一个函数来获取其调用环境的帧以返回locals() - 似乎有点迂回。另一方面,它允许您在函数中执行其他操作,例如排除某些参数。感谢您的提交! - John Haberstroh

7
您可以使用 inspect 模块:
def foo(x):
    return x

inspect.getargspec(foo)
Out[23]: ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None)

这是这个问题这个问题的副本。


6
你不能在函数内这样做,对吗? - user2678074

6
我建议使用*args**kwargs,如果参数不符合预期,则引发异常。
如果你想要检查与Python相同的错误,可以尝试以下方法:
def check_arguments(function_name,args,arg_names):
    missing_count = len(arg_names) - len(args)
    if missing_count > 0:
        if missing_count == 1:
            raise TypeError(function_name+"() missing 1 required positionnal argument: "+repr(arg_names[-1]))
        else:
            raise TypeError(function_name+"() missing "+str(missing_count)+" required positionnal argument: "+", ".join([repr(name) for name in arg_names][-missing_count:-1])+ " and "+repr(arg_names[-1]))

使用类似于 something 的东西。
def f(*args):
    check_arguments("f",args,["a","b","c"])
    #whatever you want
    ...

2
这将需要在方法体内进行大量测试,而将参数定义为函数的一部分将引发类似的错误,而无需进行任何额外的测试。 - Phillip B Oldham

4

0
class my_class():
        compulsory_default_kwargs = {'p':20,'q':30,'r':40}
        formal_args_names = ['var1','var2']
        def __init__(self,*args,**kwargs):
            for key , value in locals().items():
                if((key == 'args')):
                    if(len(args) == len(my_class.formal_args_names)):
                        for key_name , arg_value in zip(my_class.formal_args_names,args):
                            setattr(self,key_name,arg_value)
                    else:
                        raise Exception ('Error - Number of formal arguments passed mismatched required {} whereas passed {}'.format(len(my_class.formal_args_names),len(args)))

                elif((key == 'kwargs') & (len(kwargs)!=0)):
                    for kw_key , kw_value in kwargs.items():
                        if kw_key in my_class.compulsory_default_kwargs.keys():
                            setattr(self,kw_key,kw_value)
                        else:
                            raise Exception ('Invalid key-word argument passed {}'.format(kw_key))
                #elif((key!='self') & (key!='kwargs') ):
                   # setattr(self,key,value)
            for key , value in my_class.compulsory_default_kwargs.items():
                if key not in kwargs.keys():
                    setattr(self,key,value)


    this_value = 'Foo'    
    my_cl = my_class(3,this_value,p='B',r=10)
    my_cl.__dict__

-2

你在头部指定了参数吗?

为什么不直接在正文中使用相同的信息呢?

def foo(a, b, c):
   params = [a, b, c]

我错过了什么?


6
иҝҷдёҺJavaScriptзҡ„argumentsжҲ–PHPзҡ„func_get_args()дёҚжҳҜзұ»дјјзҡ„пјҢиҖҢжҲ‘жӯЈеңЁеҜ»жүҫиҝҷз§ҚеҠҹиғҪгҖӮ - Phillip B Oldham
1
他可能想要将其抽象化,因此他编写了一个助手函数,可以与所有函数一起使用,而不必每次手动编写它们的参数。例如,log_methods_arguments(arguments)。 - Hejazzman
@Nikos:你的log_methods_arguments示例可以使用*args,**kwargs参数。OP说必须指定确切的参数。这种代码异味的动机不清楚。 - Oddthinking
1
log_methods_arguments采用*args和**kwargs并不会改变任何东西。您仍然需要从要记录的方法中获取参数列表,即调用log_method_arguments的方法。 - Hejazzman
1
指定确切的参数并不是什么“代码异味”!这实际上是最佳实践...只有在需要时才应使用*args,**kwargs... - Hejazzman
显示剩余2条评论

-2
您可以使用以下代码将它们创建为列表:
args = [a, b, c]
您可以使用以下代码将它们轻松地创建为元组:
args = (a, b, c)

1
这与JavaScript的 arguments 或PHP的 func_get_args() 不类似,这正是我所寻找的。 - Phillip B Oldham

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