如何传递Python中的args和kwargs?

34 投票
4 回答
31978 浏览
提问于 2025-04-18 07:01

我对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 个回答

-3

这个例子会帮你搞清楚在开发一个基于另一个函数的函数时,**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。你的包装函数会处理大部分工作,但也允许用户对图形进行一些控制。

-1

在编程中,有时候我们会遇到一些问题,比如代码运行不正常或者出现错误。这些问题可能是因为我们写的代码有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 {}
46

***这两个符号在不同的情况下有不同的用法。

  1. 当它们出现在函数定义的时候,

    def save_name_for(self, *args, **kwargs):
    

    它们用来表示可以接收任意数量的位置参数或关键字参数。需要记住的是,在函数内部,args会是一个元组,而kwargs会是一个字典

  2. 当它们出现在函数调用的时候,

    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博客,里面有解释和例子。

21

你可以用和传递参数一样的语法来传递它们:

self.save_name_for(*args, **kwargs)

注意,你不需要传入 self;因为 save_name_for 已经和对象绑定了。

撰写回答