是否有方便的单行代码在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)]
但是,生成两个列表然后将它们合并在一起似乎很不方便。
是否有方便的单行代码在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)]
但是,生成两个列表然后将它们合并在一起似乎很不方便。
Answers:
创建一个不错的可读函数:
def range_with_negatives(start, end):
for x in range(start, end):
yield x
yield -x
用法:
list(range_with_negatives(6, 10))
这就是您获得方便的单线产品的方式。避免试图看起来像个魔术师。
y为,我将提高Datanovice答案的可读性(或者说我的可读性)signedValue。对我来说,“定义函数”方法的真正附加值是按照要求的严格顺序(正,然后是负)交付结果,同时避免出现Barmar一线式的问题,chain在这种情况下,数值参数略有不同硬编码两次。
list或类似方法,从递归算法中收集结果尤为强大。)经过多年的努力,我们总结了如何最好地做出这些决策,这对我来说是唯一有意义的一般原则:如果在程序中赋予明显的好名声,请抓住机会这样做(函数是我们执行此操作的方法之一)。
我会说最简单的解决方案是使用*拆包运算符将两个范围拆包到列表中:
>>> [*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测试(平均规格))
[*range(x, y), *range(-y + 1, -x + 1)]
您可以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.starmap和operator.mul:
from itertools import product, starmap
from operator import mul
list(starmap(mul, product((-1, 1), range(6, 10))))
但是,这不太可读。
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倍以上。
starmap并mul应避免这样product做,但我认为它更具可读性,因为它将迭代器及其元素分别分组。列表理解中的双循环也可能由于其意外的顺序而造成混乱。
通过组合两个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()您,您将获得一个具体列表,其中包含您要在内存中存储的所有数字。
[x for x in ...]更好地理解的洞察力而言是有价值的list(...)。
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]
更为简洁,但是我发现在快速扫描中,元组的解包更加困难。
如果要保留指定的顺序,则可以使用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作为整个范围的开始。
start。