是否需要range(len(a))?


83

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

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

可以这样表示:

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

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

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

可能是:

for _ in a:
    doSomethingUnrelatedToA()

在蟒蛇我们enumerate,切片,filtersorted,等...至于蟒蛇for结构旨在遍历iterables不仅范围的整数,是你需要有真实世界的使用情况in range(len(a))


5
我认为range(len(a))通常是对Python经验不足的人(尽管不一定对编程有普遍的了解)。
rlms

我只range(len(a))在学习Python时使用过。如今,我不是因为像您所说的那样很容易更换。

并不是的。我range(len(a))经常使用,因为我不需要列表a的内容,而只需要长度。
aikid 2013年

8
如果在循环中我需要在当前元素之前和之后访问元素怎么办?我通常有for i in range(len(a)): doSomethingAbout(a[i+1] - a[i])如何解决这个问题?

1
@JaakkoSeppälä同意。我只是举一个例子来说明必须遍历索引(而不仅仅是值)的主要问题,要理解的是,最后还有一个极端的情况。

Answers:


17

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

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

好的,这看起来很明智。非常感谢你。但是问题是:您将如何处理新排序的索引列表。如果再次通过此列表访问某些可迭代项,则狗咬自己的尾巴。
Hyperboreus

1
等效于:[ix for ix, _ in sorted(enumerate(a), key=lambda i: i[1])]尽管可以说您的人更好/更好。
埃里克·卡普伦

10

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

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%满意的!


1
for ix, i in enumerate(a)似乎是等效的,不是吗?
埃里克·卡普伦

1
一个应该成对使用。
飞羊

在那种情况下,我会这样做:for a1,a2 in zip(a[:-1],a[1:])
卡·阿梅里奥

7

简短答案:从数学上讲,实际上不是,例如,对于有意编程,是。

从技术上讲,答案将是“不,不需要”,因为它可以使用其他构造表达。但是在实践中,我使用for i in range(len(a)(或者for _ in range(len(a))如果不需要索引)明确表明我要迭代序列中的项的次数,而无需对序列中的项进行任何操作。

所以:“有需要吗?” ?—是的,出于可读性考虑,我需要它来表达代码的含义/意图。

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

显然,如果根本没有与迭代相关联的集合,则for ... in range(len(N))是唯一的选择,以免诉诸于i = 0; while i < N; i += 1 ...


有什么优势for _ in range(len(a))for _ in a
Hyperboreus

@Hyperboreus:是的,我只是在您发表评论前几秒钟修改了答案...所以我想,不同之处在于您是否真的想明确表示“重复很多次,a因为每个aa项目中都有而不是“元素中的元素,无论 ... 的内容如何,因此只是故意编程的细微差别。
埃里克·卡普伦

谢谢你的榜样。我已将其包含在我的问题中。
Hyperboreus

2
要获取具有'hello'与list中一样多的项的列表a,请使用b = ['hello'] * len(a)
steabert

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))不需要的。它唯一的好处就是可读性(其意图很明确)。但这只是首选项和代码样式。


非常感谢你。再者,可读性(部分)在情人眼中。我解释for _ in a:为“遍历a,但忽略其内容”,但我解释for _ in range(len(a))为“获取a的长度,然后创建相同长度的多个整数,然后最终忽略内容”。
Hyperboreus

1
@Hyperboreus-非常正确。这只是代码样式。我的目标是表明永远不会出现“我必须使用range(len(a))或我不能这样做”的情况。

附带说明:例如,在erlang中,单个下划线是匿名变量。与其他变量不同,它是唯一可以重新分配(或“匹配”)的变量,因为erlang不允许破坏性分配(通常来说是可憎的,并且削弱了我们与下层领域之间的面纱,在他的宫殿里的玻璃墙后面等着。
Hyperboreus

2

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


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方法。但是,是的,只有在进行某种操作时,才真正需要索引。


1

我有一个用例,我不相信您的任何示例都涉及到。

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

我对python比较陌生,尽管很高兴学习更优雅的方法。


4
我的无知 有一个zip,这是一种通过python并行遍历2个列表的方式。
2015年

1
嗯,我来这里时有一个非常相似的用例...[a - b for a, b in zip(list1, list2)][list1[i] - list2[i] for i in range(len(list1))]..好得多。谢谢!
kevlarr

1

如果您必须遍历len(a)一个对象的前一项b(大于a),则可能应该使用range(len(a))

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

2
这可能会更清楚:for b_elem in b[:len(a)]:...
aquirdturtle

@aquirdturtle也许更清楚,但是您的解决方案会创建一个新列表,如果b&a很大,则可能会很昂贵。
下午19年

应该使用itertools.islice代替来处理。
MiyaMiyagi '19

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

在这种情况下,斐波那契数列本身的值无关紧要。我们需要的只是与之比较的输入序列的大小。


我是Python新手,在这种情况下**会做什么?我读过有关* args和** kwargs的信息,但这看起来有所不同。
lukas_o

1
求幂。phi为n的幂。
Mateen Ulhaq,

0

很简单的例子:

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

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

但是大概应该这样做,try .. except以保持pythonic。


1
if id < len(self.itemList) 但是try...except更好,正如您所说。
saulspatz

这种不考虑ID <0
IARI

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范围或内部范围以使其更小/更好。

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.