双星号(**)和星号(*)在参数中的作用是什么?

3351

14
请参考以下链接:https://dev59.com/0mw05IYBdhLWcg3w41sp - Russia Must Remove Putin
159
这个问题是一个非常受欢迎的重复目标,但不幸的是,它经常被错误地使用。请记住,这个问题询问如何使用可变参数定义函数(def func(*args))。如果你想要了解在函数调用func(*[1,2]) 表示什么,请参见 这里。如果你想了解如何展开参数列表,请参见 这里。如果你想了解在 字面量 中 ([*[1, 2]]) * 的含义,请参见 这里 - Aran-Fey
4
我认为"What does it mean in function calls"的更好翻译应该是"在函数调用中星号操作符的含义是什么?",而不是你提供的链接"https://dev59.com/wG435IYBdhLWcg3wvyw5"。这个链接并没有真正回答`**`的使用,而且它的问题范围更窄。 - ShadowRanger
1
这个问题 - 像许多非常古老的问题一样 - 有点反向; 通常一个问题应该是关于如何解决新代码中的问题,而不是如何理解现有代码。对于后者,如果您要将其他内容视为重复项,请考虑 https://dev59.com/VnI-5IYBdhLWcg3wKE6x(尽管这仅涵盖 * 而不是 **)。 - Karl Knechtel
我在https://youtu.be/sYeqpnAA7U4中使用星号运算符解释了解包。 - Dr Nisha Arora
显示剩余2条评论
28个回答

19

* 表示接收可变参数元组

** 表示接收可变参数字典

用法如下:

1) 单个 *

def foo(*args):
    for arg in args:
        print(arg)

foo("two", 3)

输出:

two
3

2) 现在**

def bar(**kwargs):
    for key in kwargs:
        print(key, kwargs[key])

bar(dic1="two", dic2=3)

输出:

dic1 two
dic2 3

14

在Python 3.5中,您还可以在listdicttupleset显示(有时也称为文字)中使用此语法。请参见PEP 488:附加解包概述

>>> (0, *range(1, 4), 5, *range(6, 8))
(0, 1, 2, 3, 5, 6, 7)
>>> [0, *range(1, 4), 5, *range(6, 8)]
[0, 1, 2, 3, 5, 6, 7]
>>> {0, *range(1, 4), 5, *range(6, 8)}
{0, 1, 2, 3, 5, 6, 7}
>>> d = {'one': 1, 'two': 2, 'three': 3}
>>> e = {'six': 6, 'seven': 7}
>>> {'zero': 0, **d, 'five': 5, **e}
{'five': 5, 'seven': 7, 'two': 2, 'one': 1, 'three': 3, 'six': 6, 'zero': 0}

它还允许在单个函数调用中解包多个可迭代对象。

>>> range(*[1, 10], *[2])
range(1, 10, 2)

(感谢mgilson提供的PEP链接。)


1
我不确定这是否违反了“只有一种方法来做”的原则。没有其他方法可以从多个可迭代对象初始化列表/元组--您目前需要将它们链接成单个可迭代对象,这并不总是方便的。您可以在PEP-0448中了解相关理由。此外,这不是Python3.x的功能,而是Python3.5+的功能:-)。 - mgilson

11

简述

该函数将传递给函数的参数分别打包到函数体内的listdict中。 当您定义如下的函数签名时:

def func(*args, **kwds):
    # do stuff

这个函数可以用任意数量的参数和关键字参数调用。非关键字参数被打包成一个名为args的列表放在函数体内,而关键字参数则被打包成一个名为kwds的字典放在函数体内。

func("this", "is a list of", "non-keyowrd", "arguments", keyword="ligma", options=[1,2,3])

现在在函数体内,当函数被调用时,有两个本地变量,args 是一个列表,其值为 ["this", "is a list of", "non-keyword", "arguments"],而 kwds 是一个字典,其值为 {"keyword" : "ligma", "options" : [1,2,3]}


这也适用于反向操作,即来自调用方的情况。例如,如果你定义了一个函数:

def f(a, b, c, d=1, e=10):
    # do stuff

你可以通过解包迭代器或映射来调用它,这些对象必须在调用作用域中存在:

您可以通过解包可迭代对象或映射,在当前作用域内调用该函数:

iterable = [1, 20, 500]
mapping = {"d" : 100, "e": 3}
f(*iterable, **mapping)
# That call is equivalent to
f(1, 20, 500, d=100, e=3)

11

我想举一个其他人没有提到的例子

* 也可以解包生成器

一则来自Python3文档的例子

x = [1, 2, 3]
y = [4, 5, 6]

unzip_x, unzip_y = zip(*zip(x, y))

unzip_x将是(1,2,3),unzip_y将是(4,5,6)

zip()接受多个可迭代参数并返回一个生成器。

zip(*zip(x,y)) -> zip((1, 4), (2, 5), (3, 6))

1
解压缩 unzip_x 将是 (1, 2, 3) 而不是 [1, 2, 3]。unzip_y 同理。 - EduardoSaverin

10

在nickd的答案的基础上建立...

def foo(param1, *param2):
    print(param1)
    print(param2)


def bar(param1, **param2):
    print(param1)
    print(param2)


def three_params(param1, *param2, **param3):
    print(param1)
    print(param2)
    print(param3)


foo(1, 2, 3, 4, 5)
print("\n")
bar(1, a=2, b=3)
print("\n")
three_params(1, 2, 3, 4, s=5)

输出:

1
(2, 3, 4, 5)

1
{'a': 2, 'b': 3}

1
(2, 3, 4)
{'s': 5}

基本上,任何数量的位置参数可以使用*args,任何命名参数(或kwargs即关键字参数)可以使用**kwargs。

8

除了函数调用之外,*args和**kwargs在类层次结构中也很有用,并且可以避免在Python中编写__init__方法。类似的用法可以在Django代码等框架中看到。

例如:

def __init__(self, *args, **kwargs):
    for attribute_name, value in zip(self._expected_attributes, args):
        setattr(self, attribute_name, value)
        if kwargs.has_key(attribute_name):
            kwargs.pop(attribute_name)

    for attribute_name in kwargs.viewkeys():
        setattr(self, attribute_name, kwargs[attribute_name])

一个子类可以成为:
class RetailItem(Item):
    _expected_attributes = Item._expected_attributes + ['name', 'price', 'category', 'country_of_origin']

class FoodItem(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['expiry_date']

子类可以被实例化为:
food_item = FoodItem(name = 'Jam', 
                     price = 12.0, 
                     category = 'Foods', 
                     country_of_origin = 'US', 
                     expiry_date = datetime.datetime.now())

此外,一个具有仅对该子类实例有意义的新属性的子类可以调用基类__init__来卸载属性设置。 这是通过*args和**kwargs完成的。 kwargs主要用于使用命名参数使代码可读。 例如,
class ElectronicAccessories(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['specifications']
    # Depend on args and kwargs to populate the data as needed.
    def __init__(self, specifications = None, *args, **kwargs):
        self.specifications = specifications  # Rest of attributes will make sense to parent class.
        super(ElectronicAccessories, self).__init__(*args, **kwargs)

可以实例化为

usb_key = ElectronicAccessories(name = 'Sandisk', 
                                price = '$6.00', 
                                category = 'Electronics',
                                country_of_origin = 'CN',
                                specifications = '4GB USB 2.0/USB 3.0')

完整的代码在这里

5

给定一个有三个参数的函数

sum = lambda x, y, z: x + y + z
sum(1,2,3) # sum 3 items

sum([1,2,3]) # error, needs 3 items, not 1 list

x = [1,2,3][0]
y = [1,2,3][1]
z = [1,2,3][2]
sum(x,y,z) # ok

sum(*[1,2,3]) # ok, 1 list becomes 3 items

想象一下,有一个装有三个物品:三角形、圆形和矩形的袋子。这个袋子不能直接使用。你需要打开袋子,取出这三个物品,现在它们可以使用了。Python中的 * 运算符就像是这个解包过程。

进入图片描述


4

*args**kwargs:允许您向函数传递可变数量的参数。

*args:用于向函数发送非关键字可变长度参数列表:

def args(normal_arg, *argv):
    print("normal argument:", normal_arg)

    for arg in argv:
        print("Argument in list of arguments from *argv:", arg)

args('animals', 'fish', 'duck', 'bird')

会产生:
normal argument: animals
Argument in list of arguments from *argv: fish
Argument in list of arguments from *argv: duck
Argument in list of arguments from *argv: bird

**kwargs*

**kwargs允许您向函数传递关键字变量长度的参数。如果您想在函数中处理命名参数,则应使用**kwargs

def who(**kwargs):
    if kwargs is not None:
        for key, value in kwargs.items():
            print("Your %s is %s." % (key, value))

who(name="Nikola", last_name="Tesla", birthday="7.10.1856", birthplace="Croatia")  

将产生:

Your name is Nikola.
Your last_name is Tesla.
Your birthday is 7.10.1856.
Your birthplace is Croatia.

3
一个同时使用它们的函数的好例子是:
>>> def foo(*arg,**kwargs):
...     print arg
...     print kwargs
>>>
>>> a = (1, 2, 3)
>>> b = {'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(*a,**b)
(1, 2, 3)
{'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(a,**b) 
((1, 2, 3),)
{'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(a,b) 
((1, 2, 3), {'aa': 11, 'bb': 22})
{}
>>>
>>>
>>> foo(a,*b)
((1, 2, 3), 'aa', 'bb')
{}

3
这个例子能帮助你一次性了解Python中的*args**kwargssuper和继承。
class base(object):
    def __init__(self, base_param):
        self.base_param = base_param


class child1(base): # inherited from base class
    def __init__(self, child_param, *args) # *args for non-keyword args
        self.child_param = child_param
        super(child1, self).__init__(*args) # call __init__ of the base class and initialize it with a NON-KEYWORD arg

class child2(base):
    def __init__(self, child_param, **kwargs):
        self.child_param = child_param
        super(child2, self).__init__(**kwargs) # call __init__ of the base class and initialize it with a KEYWORD arg

c1 = child1(1,0)
c2 = child2(1,base_param=0)
print c1.base_param # 0
print c1.child_param # 1
print c2.base_param # 0
print c2.child_param # 1

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