解压缩和*运算符

时间:2011-05-06 22:09:45

标签: python unzip

python docs将此代码作为zip的反向操作:

>>> x2, y2 = zip(*zipped)

特别是“zip()与*运算符一起使用可以解压缩列表”。有人可以向我解释*运算符在这种情况下是如何工作的吗?据我所知,*是一个二元运算符,可以用于乘法或浅拷贝......这两种情况似乎都不是这样。

5 个答案:

答案 0 :(得分:59)

尽管hammar's answer解释了在zip()函数的情况下逆转的工作原理,但从更广泛的意义上看参数解包可能会有用。假设我们有一个简单的函数,它需要一些参数:

>>> def do_something(arg1, arg2, arg3):
...     print 'arg1: %s' % arg1
...     print 'arg2: %s' % arg2
...     print 'arg3: %s' % arg3
... 
>>> do_something(1, 2, 3)
arg1: 1
arg2: 2
arg3: 3

我们不是直接指定参数,而是创建一个列表(或者那个元组的元组)来保存它们,然后告诉Python 解包该列表并使用其内容作为参数功能:

>>> arguments = [42, 'insert value here', 3.14]
>>> do_something(*arguments)
arg1: 42
arg2: insert value here
arg3: 3.14

如果您没有足够的参数(或太多),则表现正常:

>>> arguments = [42, 'insert value here']
>>> do_something(*arguments)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/blair/<ipython console> in <module>()

TypeError: do_something() takes exactly 3 arguments (2 given)

在定义函数时可以使用相同的构造来接受任意数量的位置参数。它们作为元组赋予你的功能:

>>> def show_args(*args):
...     for index, value in enumerate(args):
...         print 'Argument %d: %s' % (index, value)
...
>>> show_args(1, 2, 3)
Argument 0: 1
Argument 1: 2
Argument 2: 3

当然,您可以将这两种技术结合起来:

>>> show_args(*arguments)
Argument 0: 42
Argument 1: insert value here

您可以使用double asterix(**)和字典对关键字参数执行类似的操作:

>>> def show_kwargs(**kwargs):
...     for arg, value in kwargs.items():
...         print '%s = %s' % (arg, value)
...
>>> show_kwargs(age=24, name='Blair')
age = 24
name = Blair

当然,您可以通过字典传递关键字参数:

>>> values = {'name': 'John', 'age': 17}
>>> show_kwargs(**values)
age = 17
name = John

混合两者是完全可以接受的,你总是可以为函数提供必需的参数和可选的额外参数:

>>> def mixed(required_arg, *args, **kwargs):
...     print 'Required: %s' % required_arg
...     if args:
...         print 'Extra positional arguments: %s' % str(args)
...     if kwargs:
...         print 'Extra keyword arguments: %s' % kwargs
...
>>> mixed(1)
Required: 1
>>> mixed(1, 2, 3)
Required: 1
Extra positional arguments: (2, 3)
>>> mixed(1, 2, 3, test=True)
Required: 1
Extra positional arguments: (2, 3)
Extra keyword arguments: {'test': True}
>>> args = (2, 3, 4)
>>> kwargs = {'test': True, 'func': min}
>>> mixed(*args, **kwargs)
Required: 2
Extra positional arguments: (3, 4)
Extra keyword arguments: {'test': True, 'func': <built-in function min>}

如果您正在使用可选的关键字参数并且希望拥有默认值,请记住您正在处理字典,因此如果密钥不存在,您可以使用其get()方法使用默认值:

>>> def take_keywords(**kwargs):
...     print 'Test mode: %s' % kwargs.get('test', False)
...     print 'Combining function: %s' % kwargs.get('func', all)
... 
>>> take_keywords()
Test mode: False
Combining function: <built-in function all>
>>> take_keywords(func=any)
Test mode: False
Combining function: <built-in function any>

答案 1 :(得分:20)

zip(*zipped)表示“将zipped的每个元素作为zip的参数”。 zip类似于转置矩阵,因为再次执行此操作会让您回到原来的位置。

>>> a = [(1, 2, 3), (4, 5, 6)]
>>> b = zip(*a)
>>> b
[(1, 4), (2, 5), (3, 6)]
>>> zip(*b)
[(1, 2, 3), (4, 5, 6)]

答案 2 :(得分:20)

当像这样使用时,*(星号,在某些圆圈中也称为“splat”运算符)是从列表中解包参数的信号。有关示例的更完整定义,请参阅http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists

答案 3 :(得分:0)

一旦您真正了解了zip()的作用,那实际上就非常简单。

zip函数接受几个参数(所有可迭代类型),并根据它们各自的位置配对这些可迭代对象中的项。

例如,假设我们有两个参数ranked_athletes, rewards传递给zip,则函数调用zip(ranked_athletes, rewards)将:

  • 以第一/最佳奖励(位置i = 0)排名第一(位置i = 0)的运动员
  • 它将移动下一个元素i = 1
  • 将第二名运动员与其奖励配对,reward中的第二名。
  • ...

这将重复进行,直到不再有运动员或奖励为止。例如,如果我们在2016年奥运会上获得1亿,并zip所获得的奖励,则:

ranked_athletes = ["Usain Bolt", "Justin Gatlin", "Andre De Grasse", "Yohan Blake"]
rewards = ["Gold medal", "Silver medal", "Bronze medal"]
zip(ranked_athletes, rewards)

将在以下元组(对)上返回迭代器:

('Usain Bolt', 'Gold medal')
('Justin Gatlin', 'Silver medal')
('Andre De Grasse', 'Bronze medal')

请注意 Yohan Blake 没有奖励。

现在,*运算符更加简单,如果您有列表[1, 2],则将其解压缩到1, 2。它基本上将一个对象转换成许多对象(与列表大小一样多)。

因此,如果我们将两者结合起来,zip(*x)实际上意味着: 获取此对象列表,将其解压缩为许多对象,并根据它们的索引将所有这些对象中的项目配对 。仅当对象是可迭代的(例如,例如列表)时才有意义,否则 index 的概念就没有任何意义。

如果逐步执行,将显示以下内容:

>>> print(x)              # x is a list of lists 
[[1, 2, 3], ['a', 'b', 'c', 'd']]

>>> print(*x)             # unpack x
[1, 2, 3]  ['a', 'b', 'c', 'd']

>>> print(list(zip(*x)))  # And pair items from the resulting lists
[(1, 'a'), (2, 'b'), (3, 'c')]

请注意,在这种情况下,如果我们调用print(list(zip(x))),我们将仅将x(属于2个列表)中的项目配对而没有任何内容(因为没有其他可迭代的配对方法):< / p>

[  ([1, 2, 3],    ),  (['a', 'b', 'c', 'd'],    )]
               ^                              ^
    [1, 2, 3] is paired with nothing          |
                                              |
                        same for the 2nd item from x: ['a', 'b', 'c', 'd']

了解zip的工作方式的另一种好方法是实现自己的版本,这与zip或多或少会起到相同的作用,但仅限于两个列表(而不是两个)许多可迭代项):

def zip_two_lists(A, B):
    shortest_list_size = min(len(A), len(B))
    # We create empty pairs
    pairs = [tuple() for _ in range(shortest_list_size)]
    # And fill them with items from each iterable 
    # according to their the items index:
    for index in range(shortest_list_size):
        pairs[index] = (A[index], B[index])
    return pairs

print(zip_two_lists(*x))
# Outputs: [(1, 'a'), (2, 'b'), (3, 'c')]

请注意,我之所以没有调用print(list(zip_two_lists(*x)))是因为与真正的zip不同,该函数不是生成器(构造迭代器的函数),而是在内存中创建一个列表。因此,此功能并不理想,您可以找到更好的approximation to the real zip in Python's documentation。阅读本文档中的所有代码等效项通常是一个好主意,这是一种理解函数做什么而没有任何歧义的好方法。

答案 4 :(得分:-2)

我建议在使用izip_longest完成zip时解压缩列表的列表:

>>> a =[2,3,4,5,6]
>>> b = [5,4,3,2]
>>> c=[1,0]]

>>>[list([val for val in k if val != None]) for k in 
                                       zip(*itertools.izip_longest(a,b,c))]

因为izip_longest为最长的列表追加无,所以我事先删除了None。我回到原来的a,b,c

[[2, 3, 4, 5, 6], [5, 4, 3, 2], [1, 0]]