是否需要范围(len(a))?

时间:2013-10-04 14:52:50

标签: python for-loop range

经常在SO上的python问题中找到这种类型的表达式。要么只是访问可迭代的所有项目

for i in range(len(a)):
    print(a[i])

这只是一种繁琐的写作方式:

for e in a:
    print(e)

或者分配给iterable的元素:

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])

可以这样表达:

for e in a [::2]:
    print(e)

或者当您只需要列表的长度而不是其内容时:

for _ in range(len(a)):
    doSomethingUnrelatedToA()

可能是:

for _ in a:
    doSomethingUnrelatedToA()

在python中,我们有enumerate,切片,filtersorted等...因为python for构造旨在迭代迭代而不是只有整数范围,是否存在需要in range(len(a))的真实用例?

12 个答案:

答案 0 :(得分:8)

如果您需要处理序列的索引,那么是 - 您使用它...例如相当于numpy.argsort ......:

>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]

答案 1 :(得分:6)

简短回答:从数学角度讲,不是,实际上,是的,例如对于故意编程。

从技术上讲,我认为数学上正确的答案是“不,它不需要”,因为它可以使用其他结构表达,即它等同于其他结构...就像语言是图灵一样完成后,它所具有的句法/范式构造并不重要,因为无论如何都可以用它来表达。

但实际上,我使用for i in range(len(a)(或for _ in range(len(a))如果我不需要索引)使其明确表示我想迭代的次数与序列中的项目一样多次需要使用序列中的项目。

所以回答“是否需要?”部分-I 需要它来表达代码的含义/意图以便于阅读。

另请参阅:https://en.wikipedia.org/wiki/Intentional_programming

P.S。但与此同时,从故意编程的观点来看,以下似乎在语义上是等效的:

for _ in a:
    ...

b = ["hello" for _ in a]

...总而言之,我猜不同之处在于您是否要真正明确“重复多次,因为a”中有项目而不是< strong>“对于a中的每个元素,无论a”的内容如何......最后只是一个故意编程的细微差别。

答案 2 :(得分:6)

如果您需要同时访问列表中的两个元素,该怎么办?

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]

就我个人而言,我并非百分之百满意!

答案 3 :(得分:2)

根据评论和个人经验,我说不,range(len(a))没有需要。你可以用range(len(a))做的所有事情都可以用另一种方式(通常效率更高)完成。

您在帖子中提供了很多示例,因此我不在此重复。相反,我会为那些说“如果我只想要a的长度,而不是项目?”的人举例说明。这是您考虑使用range(len(a))的唯一时间之一。但是,即使这样也可以这样做:

>>> a = [1, 2, 3, 4]
>>> for _ in a:
...     print True
...
True
True
True
True
>>>

Clements答案(如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))不是需要。它的唯一优势是可读性(其目的很明确)。但这只是偏好和代码风格。

答案 4 :(得分:2)

有时matplotlib需要range(len(y)),例如y=array([1,2,5,6])plot(y)工作正常,scatter(y)则不然。一个人必须写scatter(range(len(y)),y)。 (就个人而言,我认为这是scatter; plot中的错误,其朋友scatterstem应尽可能使用相同的调用序列。)

答案 5 :(得分:2)

我有一个用例,我不相信你的任何一个例子。

boxes = [b1, b2, b3]
items = [i1, i2, i3, i4, i5]
for j in range(len(boxes)):
    boxes[j].putitemin(items[j])

我对python相对较新,但很高兴学习更优雅的方法。

答案 6 :(得分:2)

当你需要使用索引进行某种操作并且当前元素不够时,这很好。例如,存储在数组中的二叉树。如果你有一个方法要求你返回一个包含每个节点直接子节点的元组列表,那么你需要索引。

#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 children方法。但是,如果你正在进行某种操作,你只需要索引。

答案 7 :(得分:1)

有时候,你真的不关心收藏本身。例如,创建一个简单的模型拟合线来比较“近似值”与原始数据:

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

在这种情况下,Fibonacci序列本身的值是无关紧要的。我们在这里需要的只是我们比较的输入序列的大小。

答案 8 :(得分:0)

很简单的例子:

def loadById(self, id):
    if id in range(len(self.itemList)):
        self.load(self.itemList[id])

我想不出一个不能快速使用range-len组合的解决方案。

但可能应该用try .. except完成这个以保持pythonic我猜...

答案 9 :(得分:0)

如果必须迭代对象len(a)的第一个b项(大于a),则应该使用range(len(a))

for i in range(len(a)):
    do_something_with(b[i])

答案 10 :(得分:0)

我的代码是:

s=["9"]*int(input())
for I in range(len(s)):
    while not set(s[I])<=set('01'):s[i]=input(i)
print(bin(sum([int(x,2)for x in s]))[2:])

它是一个二进制加法器,但我不认为范围len或内部可以被替换以使其更小/更好。

答案 11 :(得分:0)

有人说for i in range len(a)适合初学者,但如果我想同时访问元素和索引怎么办。