如何传递Python中的args和kwargs?
我对Python中的 *args 和 **kwargs 有个大概念(我觉得),但在如何把它们从一个函数传递到另一个函数时遇到了一些困难。下面是我的示例:
from pdb import set_trace as debug
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=30)
def __unicode__(self):
return u'%s' % self.name
def save_name_for(self, *args, **kwargs):
self.name = 'Alex'
return self
def save_name(self, *args, **kwargs):
debug()
self.save_name_for(self, args, kwargs)
self.save()
我把保存名字的功能分成了两个函数。这样,我可以单独测试我通常会放在save_name方法里的逻辑,而是通过测试save_name_for方法来实现。
当我在解释器中运行这个代码,并停在save_name方法时,正如我预期的那样,我看到了这个:
(Pdb) args
self =
args = (1, 2)
kwargs = {'last': 'Doe', 'first': 'John'}
如果我接着进入save_name_for方法,我看到的是:
(Pdb) args
self =
args = (<Person: >, (1, 2), {'last': 'Doe', 'first': 'John'})
kwargs =
有没有办法把在save_name方法中接收到的kwargs直接传递给save_name_for方法,这样它们就能出现在后者的kwargs里?我希望在save_name_for方法的命名空间中看到类似这样的内容:
(Pdb) args
self =
args = (1, 2)
kwargs = {'last': 'Doe', 'first': 'John'} # <= I want this
我意识到我可以在save_name中解析这些kwargs,然后明确地传递给save_name_for,但这样做似乎不太优雅。我还想过这样做,因为args是一个元组……
kwargs = args[2]
……但这似乎不管用。args[2]就是所有内容(我不太明白这个)。有没有什么更Pythonic的方式来做到这一点?
4 个回答
这个例子会帮你搞清楚在开发一个基于另一个函数的函数时,**arg
和 **kwargs
的用法。
def square_area(l, w):
print(f'(l, w): {l, w}')
return l * w
def rectangular_prism_volume(h, *args, **kwargs):
print(f'h: {h}')
return h * square_area(*args, **kwargs)
print(rectangular_prism_volume(1, 2, 3))
print(rectangular_prism_volume(w=1, l=2, h=3))
print(rectangular_prism_volume(1, l=2, w=3))
print(rectangular_prism_volume(1, 2, w=3))
try:
print(rectangular_prism_volume(1, 2, l=3))
except TypeError as e:
print(e)
pass
输出结果:
>>> print(rectangular_prism_volume(1, 2, 3))
h: 1
(l, w): (2, 3)
6
>>> print(rectangular_prism_volume(w=1, l=2, h=3))
h: 3
(l, w): (2, 1)
6
>>> print(rectangular_prism_volume(1, l=2, w=3))
h: 1
(l, w): (2, 3)
6
>>> print(rectangular_prism_volume(1, 2, w=3))
h: 1
(l, w): (2, 3)
6
>>>
>>> try:
... print(rectangular_prism_volume(1, 2, l=3))
... except TypeError as e:
... print(e)
... pass
...
h: 1
square_area() got multiple values for argument 'l'
根据这个例子,其他人已经回答了你的问题,就是使用 self.save_name_for(*args, **kwargs)
。所以,如果你设计一个函数,里面调用了另一个函数,并且想让用户传入参数,记得要把 *args
和 **kwargs
加入到输入中,并把它们传递给另一个函数。
这种方法很有用,比如你想做一个包装函数来绘图,里面调用了 matplotlib.pyplot
。你的包装函数会处理大部分工作,但也允许用户对图形进行一些控制。
在编程中,有时候我们会遇到一些问题,比如代码运行不正常或者出现错误。这些问题可能是因为我们写的代码有bug,或者是因为我们使用的工具和环境不匹配。解决这些问题通常需要我们仔细检查代码,看看哪里出了问题。
有些时候,错误信息会给我们一些提示,告诉我们哪里出了问题。我们可以根据这些提示来调整我们的代码,或者查找相关的资料来帮助我们理解错误的原因。
另外,和其他程序员交流也是一个很好的方法。我们可以在论坛上提问,或者查看别人遇到的类似问题,看看他们是怎么解决的。这不仅能帮助我们解决当前的问题,还能让我们学到更多的知识。
总之,编程过程中遇到问题是很正常的,关键是要耐心分析,寻找解决方案。
Python 3.12.2
def f(a,b,*value_list,c,**key_value_dict):
print(a,b,value_list,c,key_value_dict)
x={'a':1,'b':2,'c':3,'d':4}
f(**x) # 1 2 () 3 {'d': 4}
f(*x) # TypeError: f() missing 1 required keyword-only argument: 'c'
f(*x,c=4) # a b ('c', 'd') 4 {}
y=(1,2,3)
f(*y) # TypeError: f() missing 1 required keyword-only argument: 'c'
f(*y,c=4) # 1 2 (3,) 4 {}
f(*y,**x) # TypeError: f() got multiple values for argument 'a'
f(1,2,5,6,d=7,c=4,e=8) # 1 2 (5, 6) 4 {'d': 7, 'e': 8}
f(1, 2, *(5, 6), c=4, **{'d': 7, 'e': 8}) # 1 2 (5, 6) 4 {'d': 7, 'e': 8}
f(*(),1,*(),2,*(),**{},c=4,**{}) # 1 2 () 4 {}
*
和**
这两个符号在不同的情况下有不同的用法。
当它们出现在函数定义的时候,
def save_name_for(self, *args, **kwargs):
它们用来表示可以接收任意数量的位置参数或关键字参数。需要记住的是,在函数内部,
args
会是一个元组,而kwargs
会是一个字典。当它们出现在函数调用的时候,
args = (1, 2) kwargs = {'last': 'Doe', 'first': 'John'} self.save_name_for(*args, **kwargs)
*
和**
就像是拆包操作符。args
必须是一个可迭代的对象,而kwargs
必须像字典一样。args
中的项目会被拆开,作为位置参数传给函数,而kwargs
中的键值对会作为关键字参数传给函数。因此,self.save_name_for(*args, **kwargs)
等同于
self.save_name_for(1, 2, last='Doe', first='John')
想了解更多,可以查看这个saltycrane博客,里面有解释和例子。
你可以用和传递参数一样的语法来传递它们:
self.save_name_for(*args, **kwargs)
注意,你不需要传入 self
;因为 save_name_for
已经和对象绑定了。