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


31

是否有方便的单行代码在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)]

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


3
正数应该在负数之前吗?
— 埃里希

2
@Erich不,对我来说顺序不重要。
— upe

6
如果顺序无关紧要,甚至需要列出清单吗?设置是否可以(无序),或者生成器或元组(两者都是有序的)好吗?
— JG

@JG我以后可能想创建一个图形。据说可以使用散点图等等scalar or array-like, shape。
— UPE

2
这些答案大多数都像一些反高尔夫解决方案一样在阅读。排序,生成器功能,迭代工具。与大多数“答案”相比,我宁愿维护您提供的代码。
— Peilonrayz

Answers:


44

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

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

50

创建一个不错的可读函数:

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

用法:

list(range_with_negatives(6, 10))

这就是您获得方便的单线产品的方式。避免试图看起来像个魔术师。


20
对列表/字典理解的偏见在SO上很常见,通常可以通过说它们不“可读”来证明。一个结构的内在可读性(而不是如何它的使用)在很大程度上是主观的。我个人认为理解更容易理解,而这种解决方案就不那么容易了(比较:“写下每个50岁以上男性的血压”与“检查每个人的血压。好,他们是男性吗?如果是,那么...” )。我之所以使用它们,并不是因为我试图“看起来”任何东西。如果这是个问题,那么长时间的理解可能会跨越多行。
— jez

2
很公平。但这就是主观性的体现:就个人的精神压力而言,我个人确实发现list comp解决方案至少与生成器函数一样可读。但是,通过重命名y为,我将提高Datanovice答案的可读性(或者说我的可读性)signedValue。对我来说,“定义函数”方法的真正附加值是按照要求的严格顺序(正,然后是负)交付结果,同时避免出现Barmar一线式的问题,chain在这种情况下,数值参数略有不同硬编码两次。
— jez

2
我同意有关列表理解的一般观点,但是定义这样的函数也是一种非常有用的技巧。(通过编写递归生成器并将顶级结果传递给list或类似方法,从递归算法中收集结果尤为强大。)经过多年的努力,我们总结了如何最好地做出这些决策,这对我来说是唯一有意义的一般原则:如果在程序中赋予明显的好名声,请抓住机会这样做(函数是我们执行此操作的方法之一)。
— Karl Knechtel

42

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

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

这不仅是提出的最短答案,而且也是性能最高的答案,因为它只构造一个列表,并且除了两个之外,不涉及任何函数调用range。

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

答案ID方法时间结果
-------------------------------------------------- ------------------------------------------------
(正在讨论中)[x代表x在范围(6,10)中] + [y代表y在范围(-9,-5)中]每个循环0.843微秒
(此答案)[* range(6,10),* range(-9,-5)]每个循环0.509 us
61348876 [对于x在范围(6,10)中的y对于(x,-x)中的y]每个循环0.754微秒
61349149 list(range_with_negatives(6,10))每个循环0.795微秒
61348914 list(itertools.chain(range(6,10),range(-9,-5)))0.709每个循环使用
61366995 [sign * x表示符号,itertools.product((-1,1),range(6,10))中的x]每个循环0.899微秒
61371302 list(range(6,10))+ list(range(-9,-5))每个循环0.729 usc
61367180 list(range_with_negs(6,10))每个循环1.95微秒

(在我自己的计算机上使用Python 3.6.9执行的timeit测试(平均规格))


2
当您所拥有的只是一个少于10个项目的示例时,我不太热衷于假设性能是相关的,但这显然是最简单的解决方案。
— JollyJoker

您如何在不对负值进行硬编码的情况下找出它们的开始和结束?
— aldokkani

1
@aldokkani[*range(x, y), *range(-y + 1, -x + 1)]
— RoadrunnerWMC

20

您可以itertools.chain()用来串联两个范围。

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

1
我会使用'range(-6,-10,-1)',因为顺序的顺序无关紧要(并用变量替换6和10)
— MaartenFabré

7

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

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

这可能会因为使用乘法而变慢,但应易于阅读。

如果您想要一个纯粹的功能性解决方案,则还可以导入itertools.starmap和operator.mul:

from itertools import product, starmap
from operator import mul

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

但是,这不太可读。


5
我发现使用的product,starmap以及opertaor.mul不必要的钝相比嵌套列表理解,但我赞成用乘法的建议。[x * sign for sign in (1, -1) for x in range(6, 10)]仅比慢10%[y for x in range(6, 10) for y in (x, -x)],并且在顺序很重要的情况下,比对基于元组的方法进行排序快3倍以上。
— 接近

这是一个很好的主意,但对于问题细节可能太具体了。其他人提供的技术更普遍地适用。
— Karl Knechtel

@ApproachingDarknessFish我同意starmap并mul应避免这样product做,但我认为它更具可读性,因为它将迭代器及其元素分别分组。列表理解中的双循环也可能由于其意外的顺序而造成混乱。
— Frank Vel

4

通过组合两个range对象,您真的很接近。但是有一种更简单的方法可以做到这一点:

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

即,将每个range对象转换为一个列表,然后将两个列表连接在一起。

使用itertools的另一种方法:

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

itertools.chain()就像一般化的一样+:与其添加两个列表,不如将一个迭代器链接在一起,以构成一个“超级迭代器”。然后将其传递给list()您,您将获得一个具体列表,其中包含您要在内存中存储的所有数字。


3
这个答案仅对于[x for x in ...]更好地理解的洞察力而言是有价值的list(...)。
— Karl Knechtel

2

称量还有另一种可能性。

如果您希望提高可读性,那么原来的单行代码还不错,但是我将范围更改为与我认为负界限相同的范围,使事情变得不太清楚。

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

2

itertools.chain到目前为止,IMO在其他几个答案中提出的方法绝对是最干净的方法。

但是,由于在您的情况下值的顺序无关紧要,因此可以避免使用两个range对象来定义两个显式对象,从而避免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]

Tad冗长,但足够可读。

另一个类似的选择是使用平原的元组/参数解压缩chain:

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

更为简洁,但是我发现在快速扫描中,元组的解包更加困难。


2

这是一个主题的变化(见@Derte Trdelnik的答案)以下的理念,itertools在那里

迭代器构建块本身或结合使用都是有用的。

这个想法是,当我们定义一个新函数时,我们也可以使其通用:

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

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

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

0

如果要保留指定的顺序,则可以使用Python的内置范围生成器,并附带条件:

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

给您输出:

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

并且也以0作为整个范围的开始。


2
对于的大值,这是非常无效的start。
— 所罗门·乌科

0

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

给定的变量:1.开始= 6 2.停止= 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)

-1

我就像对称。

a = 6 b = 10

nums = [x的x + y(-(a + b-1),0)的y范围(a,b)]

结果应为-9,-8,...,8、9。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.