解压缩和*运算符

问题描述 投票:29回答:5

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

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

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

python unzip
5个回答
22
投票

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


60
投票

虽然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>

21
投票

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)]

0
投票

一旦你真正理解了zip()的作用,这实际上非常简单。

zip函数采用多个参数(所有可迭代类型),并根据各自的位置对这些迭代中的项进行配对。

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

  • 以第一名/最佳奖励(位置i = 0)排名第一(位置i = 0)的运动员
  • 它将移动下一个元素,i = 1
  • 第二名运动员获得奖励,第二名来自reward
  • ...

这将重复进行,直到不再有运动员或奖励为止。例如,如果我们在2016年奥运会和zip上取得100米的奖励,我们将获得以下奖励:

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)实际上意味着:获取此对象列表,将其解压缩到许多对象,并根据索引将所有这些对象中的项配对。只有对象是可迭代的(例如列表)才有意义,否则索引的概念实际上没有意义。

如果您一步一步地执行此操作,请执行以下操作:

>>> 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中的项目(两个列表)与任何内容配对(因为没有其他可迭代的配对):

[  ([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。阅读这些文档周围的代码等价通常是一个好主意,这是理解函数做什么没有任何歧义的好方法。


-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]]
© www.soinside.com 2019 - 2024. All rights reserved.