是否需要范围(len(a))?
经常在SO上的python问题中find这种types的expression式。 要么只是访问迭代的所有项目
for i in range(len(a)): print(a[i])
这只是一个笨拙的写作方式:
for e in a: print(e)
或者分配给迭代元素:
for i in range(len(a)): a[i] = a[i] * 2
这应该是一样的:
for i, e in enumerate(a): a[i] = e * 2 # Or if it isn't too expensive to create a new iterable a = [e * 2 for e in a]
或者用于筛选索引:
for i in range(len(a)): if i % 2 == 1: continue print(a[i])
可以这样expression:
for e in a [::2]: print(e)
或者当你只需要列表的长度,而不是它的内容:
for _ in range(len(a)): doSomethingUnrelatedToA()
这可能是:
for _ in a: doSomethingUnrelatedToA()
在python中,我们enumerate
,切片, filter
, sorted
等等…由于python for
构造是为了遍历iterables而不是只有整数的范围,有真实世界的用例,你需要in range(len(a))
?
如果你需要使用序列索引,那么是的 – 你使用它…例如numpy.argsort的等价物:
>>> a = [6, 3, 1, 2, 5, 4] >>> sorted(range(len(a)), key=a.__getitem__) [2, 3, 1, 5, 4, 0]
简单的回答 :math上来说,不,实际上,是的,例如有意编程。
从技术上讲,我认为math上正确的答案是“不,这是不需要的”,因为它可以用其他构造来expression,也就是说,它等价于其他构造……就像一个语言是图灵完整的, /范式结构,因为无论如何,一切都可以expression出来。
但是在实践中,如果我不需要索引,我将它for i in range(len(a)
(或for _ in range(len(a))
)中,使其明确表示我想迭代多次是序列中的项目,而不需要按顺序使用任何项目中的项目。
所以要回答“有需要吗?” 部分 – 我需要为了可读性目的来expression代码的含义/意图。
另见: https : //en.wikipedia.org/wiki/Intentional_programming
PS,但同时,从意图编程的angular度来看,以下似乎在语义上是等价的:
for _ in a: ...
要么
b = ["hello" for _ in a]
…总而言之,我猜想不同之处在于,你是否真的需要对“重复多次,因为a中有项目”而不是“a中的每个元素,而不pipea
的内容” 。这只是一个故意编程的细微差别。
根据评论和个人经验,我说不, range(len(a))
是没有必要的range(len(a))
。 你可以用range(len(a))
做的所有事情都可以用另一种方式来完成(通常效率更高)。
你在post中给了很多例子,所以我不会在这里重复。 相反,我会给那些说“如果我只想要一个长度而不是项目?”的人举个例子。 这是您可能考虑使用range(len(a))
的唯一时间之一。 但是,即使这样做也可以这样做:
>>> a = [1, 2, 3, 4] >>> for _ in a: ... print True ... True True True True >>>
克莱门茨的回答(如Allik所示)也可以重写以去除range(len(a))
:
>>> a = [6, 3, 1, 2, 5, 4] >>> sorted(range(len(a)), key=a.__getitem__) [2, 3, 1, 5, 4, 0] >>> # Note however that, in this case, range(len(a)) is more efficient. >>> [x for x, _ in sorted(enumerate(a), key=lambda i: i[1])] [2, 3, 1, 5, 4, 0] >>>
所以,总之, range(len(a))
是不需要的 。 它唯一的好处是可读性(它的意图是明确的)。 但是,这只是偏好和代码风格。
我有一个用例,我不相信你的任何例子。
boxes = [b1, b2, b3] items = [i1, i2, i3, i4, i5] for j in range(len(boxes)): boxes[j].putitemin(items[j])
我相对较新的python,虽然很高兴学习一个更优雅的方法。
如果你需要同时访问列表中的两个元素呢?
for i in range(len(a[0:-1])): something_new[i] = a[i] * a[i+1]
你可以使用这个,但可能不太清楚:
for i, _ in enumerate(a[0:-1]): something_new[i] = a[i] * a[i+1]
我个人并不是100%满意!
有时matplotlib需要range(len(y))
,例如,当y=array([1,2,5,6])
, plot(y)
工作正常, scatter(y)
不会。 必须写scatter(range(len(y)),y)
。 (就我个人而言,我认为这是一个scatter
的错误; plot
和它的朋友scatter
和stem
应尽可能使用相同的调用序列。)
当你需要使用索引进行某种操作并使当前元素不够用的时候,很好。 拿一个二进制树存储在一个数组中。 如果你有一个方法要求你返回包含每个节点的元组列表,那么你需要索引。
#0 -> 1,2 : 1 -> 3,4 : 2 -> 5,6 : 3 -> 7,8 ... nodes = [0,1,2,3,4,5,6,7,8,9,10] children = [] for i in range(len(nodes)): leftNode = None rightNode = None if i*2 + 1 < len(nodes): leftNode = nodes[i*2 + 1] if i*2 + 2 < len(nodes): rightNode = nodes[i*2 + 2] children.append((leftNode,rightNode)) return children
当然,如果你正在处理的元素是一个对象,你可以调用一个get儿童方法。 但是,如果你正在做某种操作,你只需要索引。
很简单的例子:
def loadById(self, id): if id in range(len(self.itemList)): self.load(self.itemList[id])
我想不出一个不能很快使用range-len组合的解决scheme。
但可能相反,这应该做的try .. except
留pythonic我猜..
如果必须迭代对象b
(大于a
)的第一个len(a)
项,则应该使用range(len(a))
:
for i in range(len(a)): do_something_with(b[i])
有时候,你真的不关心collections本身 。 例如,创build一个简单的模型拟合线来比较“逼近”与原始数据:
fib_raw = [1, 1, 2, 3, 5, 8, 13, 21] # Fibonacci numbers phi = (1 + sqrt(5)) / 2 phi2 = (1 - sqrt(5)) / 2 def fib_approx(n): return (phi**n - phi2**n) / sqrt(5) x = range(len(data)) y = [fib_approx(n) for n in x] # Now plot to compare fib_raw and y # Compare error, etc
在这种情况下,斐波纳契数列本身的值是不相关的。 我们所需要的只是我们比较的input序列的大小。