繁体   English   中英

在 Python 中生成数字列表及其负数

[英]Generate list of numbers and their negative counterparts in Python

在 Python 中是否有一个方便的单行生成数字列表及其负对应项?

例如,假设我想生成一个包含数字 6 到 9 和 -6 到 -9 的列表。

我目前的做法是:

l = [x for x in range(6,10)]
l += [-x for x in l]

一个简单的“单线”将是:

l = [x for x in range(6,10)] + [y for y in range(-9, -5)]

但是,生成两个列表然后将它们连接在一起似乎很不方便。

我不确定顺序是否重要,但您可以创建一个元组并将其解压缩到列表理解中。

nums = [y for x in range(6,10) for y in (x,-x)]
print(nums)
[6, -6, 7, -7, 8, -8, 9, -9]

创建一个漂亮且可读的 function:

def range_with_negatives(start, end):
    for x in range(start, end):
        yield x
        yield -x

用法:

list(range_with_negatives(6, 10))

这就是您如何为任何事情获得方便的单线。 避免试图看起来像一个神奇的职业黑客。

我想说最简单的解决方案是使用*解包运算符将两个范围解包到一个列表中:

>>> [*range(6, 10), *range(-9, -5)]
[6, 7, 8, 9, -9, -8, -7, -6]

这不仅是迄今为止提出的最短答案,而且也是性能最高的,因为它只构造一个列表,并且不涉及超出两个range的 function 调用。

我通过使用timeit模块测试所有这个问题的答案来验证这一点:

Answer ID     Method                                                           timeit result
--------------------------------------------------------------------------------------------------
(in question) [x for x in range(6,10)] + [y for y in range(-9, -5)]            0.843 usec per loop
(this answer) [*range(6, 10), *range(-9, -5)]                                  0.509 usec per loop
61348876      [y for x in range(6,10) for y in (x,-x)]                         0.754 usec per loop
61349149      list(range_with_negatives(6, 10))                                0.795 usec per loop
61348914      list(itertools.chain(range(6, 10), range(-9, -5)))               0.709 usec per loop
61366995      [sign*x for sign, x in itertools.product((-1, 1), range(6, 10))] 0.899 usec per loop
61371302      list(range(6, 10)) + list(range(-9, -5))                         0.729 usec per loop
61367180      list(range_with_negs(6, 10))                                     1.95 usec per loop

(在我自己的电脑上使用 Python 3.6.9 进行时间测试(平均规格))

您可以使用itertools.chain()连接两个范围。

import itertools
list(itertools.chain(range(6, 10), range(-9, -5)))

您可以使用itertools.product ,它是笛卡尔积。

[sign*x for sign, x in product((-1, 1), range(6, 10))]
[-6, -7, -8, -9, 6, 7, 8, 9]

这可能会更慢,因为您使用乘法,但应该易于阅读。

如果你想要一个纯函数式的解决方案,你也可以导入itertools.starmapoperator.mul

from itertools import product, starmap
from operator import mul

list(starmap(mul, product((-1, 1), range(6, 10))))

但是,这不太可读。

你真的很接近,结合了两个range对象。 但有一种更简单的方法可以做到这一点:

>>> list(range(6, 10)) + list(range(-9, -5))
[6, 7, 8, 9, -9, -8, -7, -6]

即将每个range object 转换为一个列表,然后将两个列表连接起来。

另一种方法,使用 itertools:

>>> list(itertools.chain(range(6, 10), range(-9, -5)))
[6, 7, 8, 9, -9, -8, -7, -6]

itertools.chain()就像一个通用的+ :它不是添加两个列表,而是将一个迭代器一个接一个地链接起来,形成一个“超级迭代器”。 然后将它传递给list() ,你会得到一个具体的列表,在 memory 中包含你想要的所有数字。

IMO 在其他几个答案中提出的使用itertools.chain的方法绝对是迄今为止提供的最干净的方法。

但是,由于在您的情况下值的顺序无关紧要,因此您可以避免必须定义两个显式range对象,从而避免使用itertools.chain.from_iterable执行负range索引所需的所有非一数学itertools.chain.from_iterable

>>> import itertools
>>> list(itertools.chain.from_iterable((x, -x) for x in range(6, 10)))
[6, -6, 7, -7, 8, -8, 9, -9]

有点冗长,但足够可读。

另一个类似的选择是使用带有普通chain的元组/参数解包:

>>> list(itertools.chain(*((x, -x) for x in range(6, 10))))
[6, -6, 7, -7, 8, -8, 9, -9]

更简洁,但我发现在快速扫描中更难理解元组解包。

权衡另一种可能性。

如果你想要可读性,你原来的单行是相当不错的,但我会将范围更改为相同,因为我认为负边界会使事情变得不那么清晰。

[x for x in range(6, 10)] + [-x for x in range(6, 10)]

这是一个主题的变体(参见@Derte Trdelnik回答),遵循itertools的哲学,其中

迭代器构建块 [...] 可单独使用或组合使用。

这个想法是,当我们定义一个新的 function 时,我们不妨让它通用:

def interleaved_negatives(it):
    for i in it:
        yield i
        yield -i

并将其应用于特定的range迭代器:

list(interleaved_negatives(range(6, 10)))

可以有不同的方法来完成工作。

给定变量: 1. start=6 2. stop=10

你也可以试试这个,不同的方法:

def mirror_numbers(start,stop):
  if start<stop:
    val=range(start,stop)
    return [j if i < len(val) else -j for i,j in enumerate([x for x in val]*2) ]

mirror_numbers(6,10)

使用迭代工具:

>>> list(itertools.chain(range(6, 10), range(-9, -5)))
[6, 7, 8, 9, -9, -8, -7, -6]

itertools.chain()就像一个通用的+ :它不是添加两个列表,而是将一个迭代器一个接一个地链接起来,形成一个“超级迭代器”。 然后将它传递给list() ,你会得到一个具体的列表,在 memory 中包含你想要的所有数字。

如果你想保持你指定的顺序,你可以使用 Python 的内置范围生成器和条件:

def range_with_negs(start, stop):
    for value in range(-(stop-1), stop):      
        if (value <= -start) or (value >= start):
            yield value

这给了你 output:

In [1]: list(range_with_negs(6, 10))
Out[1]: [-9, -8, -7, -6, 6, 7, 8, 9]

并且还可以使用 0 作为整个范围的开始。

我喜欢对称。

a = 6
b = 10

nums = [x+y for x in (-(a+b-1),0) for y in range(a,b)]

结果是[-9, -8, -7, -6, 6, 7, 8, 9]

我相信 nums 表达式可以改进,“in”和“range”后面的内容在我看来仍然不平衡。

似乎只有两个答案真正给出了一条线,所以这是另一个:

[i for l in map(lambda x: (x, -x), range(6, 10)) for i in l]
[6, -6, 7, -7, 8, -8, 9, -9]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM