连接两个范围函数结果
Posted
技术标签:
【中文标题】连接两个范围函数结果【英文标题】:Concatenating two range function results 【发布时间】:2012-12-15 12:15:55 【问题描述】:范围函数是否允许连接?就像我想做一个range(30)
并将它与range(2000, 5002)
连接起来。所以我的连接范围将是0, 1, 2, ... 29, 2000, 2001, ... 5001
这样的代码不适用于我最新的python(版本:3.3.0)
range(30) + range(2000, 5002)
【问题讨论】:
我的python版本是3.3.0 ..我的问题也更新了 你想得到什么结果(比如,什么类型的数据——一个简单的列表,一个生成器,或者别的什么)?你想对结果做什么? 【参考方案1】:您可以在范围函数周围使用列表函数来制作列表 喜欢这个
list(range(3,7))+list(range(2,9))
【讨论】:
你对我的回答做了什么改变?你可以给我发邮件吗?我没注意到。是缩进吗 通过在行首添加 4 个空格字符,表明它包含源代码并应突出显示。您可以改为选择代码块并单击编辑器的
按钮。【参考方案2】:
python >= 3.5
您可以在列表中使用可迭代解包(请参阅 PEP 448:Additional Unpacking Generalizations)。
如果你需要一份清单,
[*range(2, 5), *range(3, 7)]
# [2, 3, 4, 3, 4, 5, 6]
这会保留顺序并且不会删除重复项。或者,您可能想要一个元组,
(*range(2, 5), *range(3, 7))
# (2, 3, 4, 3, 4, 5, 6)
...或一组,
# note that this drops duplicates
*range(2, 5), *range(3, 7)
# 2, 3, 4, 5, 6
它也恰好比调用itertools.chain
更快。
from itertools import chain
%timeit list(chain(range(10000), range(5000, 20000)))
%timeit [*range(10000), *range(5000, 20000)]
738 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
665 µs ± 13.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
不过,chain
的好处是您可以传递任意范围列表。
ranges = [range(2, 5), range(3, 7), ...]
flat = list(chain.from_iterable(ranges))
OTOH,解包概括尚未“概括”到任意序列,因此您仍需要自己解包各个范围。
【讨论】:
谢谢 - 这比建议的答案更好,更干净。【参考方案3】:借助extend方法,我们可以连接两个列表。
>>> a = list(range(1,10))
>>> a.extend(range(100,105))
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 101, 102, 103, 104]
【讨论】:
这适用于 Python 2,OP 使用 Python 3。 Extend 不是关键字,它是 Python 列表中的一种方法。【参考方案4】:range()
在 Python 2.x 中返回一个列表:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
xrange()
在 Python 2.x 中返回一个迭代器:
>>> xrange(10)
xrange(10)
并且在 Python 3 中 range()
还返回一个迭代器:
>>> r = range(10)
>>> iterator = r.__iter__()
>>> iterator.__next__()
0
>>> iterator.__next__()
1
>>> iterator.__next__()
2
所以很明显,您不能像其他人指出的那样使用chain()
连接其他迭代器。
【讨论】:
您的回答很有帮助,但没有提供解决方案。【参考方案5】:我来这个问题是因为我试图连接未知数量的范围,这些范围可能重叠,并且不希望最终迭代器中出现重复值。我的解决方案是像这样使用set
和union
运算符:
range1 = range(1,4)
range2 = range(2,6)
concatenated = set.union(set(range1), set(range2)
for i in concatenated:
print(i)
【讨论】:
除非python3.7,否则这不会保持顺序,这也不会允许范围内的部分重叠(即,允许重复)。 @cs95 即使在 Python-3.7 中也不会维护订单 - 保持订单的是dict
,而不是 set
,它会根据值哈希对它们进行排序。【参考方案6】:
我喜欢尽可能简单的解决方案(包括效率)。解决方案是否如此并不总是很清楚。无论如何,Python 3 中的 range()
是一个生成器。您可以将其包装到任何进行迭代的构造中。 list()
能够从任何可迭代对象构造列表值。列表的 +
运算符进行连接。我在示例中使用了较小的值:
>>> list(range(5))
[0, 1, 2, 3, 4]
>>> list(range(10, 20))
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> list(range(5)) + list(range(10,20))
[0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
这正是 range(5) + range(10, 20)
在 Python 2.5 中所做的——因为 range()
返回了一个列表。
在 Python 3 中,它仅在您真的想要构建列表时才有用。否则,我推荐Lev Levitsky's 和itertools.chain 的解决方案。该文档还显示了非常简单的实现:
def chain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
Inbar Rose 的解决方案很好,功能上等效。无论如何,我的 +1 是给 Lev Levitsky 和他关于使用标准库的论点。来自Python之禅...
面对模棱两可,拒绝猜测的诱惑。
#!python3
import timeit
number = 10000
t = timeit.timeit('''\
for i in itertools.chain(range(30), range(2000, 5002)):
pass
''',
'import itertools', number=number)
print('itertools:', t/number * 1000000, 'microsec/one execution')
t = timeit.timeit('''\
for x in (i for j in (range(30), range(2000, 5002)) for i in j):
pass
''', number=number)
print('generator expression:', t/number * 1000000, 'microsec/one execution')
在我看来,itertools.chain
更具可读性。但真正重要的是……
itertools: 264.4522138986938 microsec/one execution
generator expression: 785.3081048010291 microsec/one execution
...大约快 3 倍。
【讨论】:
这太棒了,我承认被高效构建的标准库模块打败了。但是你的时间很奇怪,在我的机器上经过多次测试,我发现我的解决方案只慢了 1.8 倍,而不是慢了 3 倍。但它仍然较慢。 这绝对是硬件相关的,也可能是操作系统相关的。我确实使用了我相当过时的计算机,配备 AMD Athlon 64 X2 双核处理器 3800+,频率为 2.01 GHz,内存为 3 GB。操作系统是 Windows 7 Home Premium 64 位(处理器和内存得分仅为 4,9 -- windows.microsoft.com/en-US/windows7/…)。我不确定 Python 的实现。比如说,你可能有更多的处理器内核。【参考方案7】:可以使用list-comprehension 完成。
>>> [i for j in (range(10), range(15, 20)) for i in j]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 17, 18, 19]
符合您的要求,但答案很长,所以我不会在这里发布。
注意:可以制作成生成器以提高性能:
for x in (i for j in (range(30), range(2000, 5002)) for i in j):
# code
甚至是生成器变量。
gen = (i for j in (range(30), range(2000, 5002)) for i in j)
for x in gen:
# code
【讨论】:
@mkind 谢谢,我讨厌依赖,人们总是跳来回答大量的导入和库,但它们并不总是可用的,而且它们经常做与普通代码相同的事情,只是包装在一个包中,它不是魔法。 :) 我不认为不使用标准库本身就是一种美德,但这是一个很好的答案。 @inbar,你是对的。另外两个优势是您知道代码的作用以及您正在了解有关问题的一些知识。 @LevLevitsky /mkind : the code for chain: docs.python.org/2/library/itertools.html#itertools.chain 正是我的 list-comprehension/generator 所做的,只是你不需要导入任何东西,你可以在你面前看到它/如果您需要条件等,请修改它... @mkind 和 Inbar Rose:讨厌依赖可能没问题,但itertools
模块是标准模块。由于其更有效的实现,itertools.chain
的解决方案速度快了大约 3 倍——请参阅***.com/a/14101734/1346705。寓意是:“永不言败!”【参考方案8】:
您可以为此使用itertools.chain
:
from itertools import chain
concatenated = chain(range(30), range(2000, 5002))
for i in concatenated:
...
它适用于任意迭代。请注意,您应该知道 Python 2 和 3 之间 range()
的行为存在差异:在 Python 2 中,range
返回一个列表,在 Python3 中返回一个迭代器,这是节省内存的,但并不总是可取的。
列表可以与+
连接,迭代器不能。
【讨论】:
对我来说,这一定是 Python 中最丑陋的部分之一。对于这样一个基本任务有外部库要求是荒谬的...... @amos-folarin 我倾向于同意,这就是为什么我发现coconut 给我留下了深刻的印象,这是一种类似于 python 的语言,所有功能工具都可以作为语法糖使用。 当心:您不能保存链以供第二次使用。您可以对其进行一次迭代。如果你再试一次,你什么也得不到。这与范围的工作方式形成鲜明对比。以上是关于连接两个范围函数结果的主要内容,如果未能解决你的问题,请参考以下文章
为 PostgreSQL 函数调用获取“范围内”的两行:交叉连接?
Excel VBA - 搜索范围和连接的 SQL ADODB 记录集以在列中匹配写入结果集