Существует ли необходимость в диапазоне (len (a))?

Часто в выражениях типа python на SO часто встречаются выражения этого типа. Либо для доступа ко всем элементам итерации

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

В python мы имеем enumerate, slicing, filter, sorted и т.д.... Поскольку конструкции python for предназначены для итерации по итерам, а не только диапазоны целых чисел, существуют ли реальные -World, где вам нужно in range(len(a))?

Ответ 1

Если вам нужно работать с индексами последовательности, тогда да - вы используете его... например, для эквивалента numpy.argsort...

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

Ответ 2

Краткий ответ: математически, нет, в практическом плане да, например, для преднамеренного программирования.

Технически, я думаю, что математически правильный ответ будет "нет, он не нужен", потому что он выражается с помощью других конструкций, то есть эквивалентен другим конструкциям... что-то вроде того, что язык завершен по Тьюрингу, он делает на самом деле не имеет значения, какие у него синтаксические/парадигматические конструкции, потому что в любом случае в них можно все выразить.

Но на практике я использую for i in range(len(a) (или for _ in range(len(a)), если мне не нужен индекс), чтобы явно указать, что я хочу выполнять итерацию столько раз, сколько есть элементов в последовательности, без необходимости использовать элементы в последовательность для всего.

Таким образом, чтобы ответить "Есть ли необходимость?" часть - мне это нужно, чтобы выразить значение/намерение кода для удобства чтения.

Смотрите также: https://en.wikipedia.org/wiki/Intentional_programming

P.S., но в то же время, с точки зрения преднамеренного программирования, кажется, что семантически эквивалентно следующее:

for _ in a:
    ...

или

b = ["hello" for _ in a]

... в общем, я думаю, разница в том, хотите ли вы быть действительно откровенным о "повторять КАК МНОГО РАЗ, так как в a есть элементы" в отличие от " для каждого элемента в a, независимо от содержимого a "... так что в конце просто нюанс преднамеренного программирования.

Ответ 3

Что делать, если вам нужно одновременно получить доступ к двум элементам списка?

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% доволен либо!

Ответ 4

Идя по комментариям, а также по личному опыту, я говорю "нет", нет необходимости в 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)) не требуется. Его единственный потенциал - читаемость (его намерение ясно). Но это только предпочтение и стиль кода.

Ответ 5

Иногда matplotlib требует range(len(y)), например, пока y=array([1,2,5,6]), plot(y) работает нормально, scatter(y) нет. Нужно написать scatter(range(len(y)),y). (Лично я считаю, что это ошибка в scatter; plot, а ее друзья scatter и stem должны использовать как можно больше те же последовательности вызовов.)

Ответ 6

У меня есть случай использования. Я не считаю, что какой-либо из ваших примеров охватывает.

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

Я относительно новичок в python, хотя так счастлив узнать более элегантный подход.

Ответ 7

Приятно иметь, когда вам нужно использовать индекс для какой-то манипуляции и иметь текущий элемент не хватает. Возьмем, например, двоичное дерево, которое хранится в массиве. Если у вас есть метод, который просит вас вернуть список кортежей, в котором содержатся прямые дочерние узлы, вам нужен индекс.

#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. Но да, вам действительно нужен индекс, если вы делаете какие-то манипуляции.

Ответ 8

Иногда вы действительно не заботитесь о самой коллекции. Например, создавая простую модель, подходящую для сравнения "приближения" с необработанными данными:

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

В этом случае значения самой последовательности Фибоначчи были неактуальны. Все, что нам было нужно, это размер входной последовательности, с которой мы сравнивали.

Ответ 9

Очень простой пример:

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

Я не могу придумать решение, которое быстро не использует состав диапазона.

Но, скорее всего, это должно быть сделано с try .. except, чтобы оставаться pythonic, я думаю.

Ответ 10

Если вам нужно выполнить итерацию по первым элементам len(a) объекта b (который больше, чем a), вероятно, вы должны использовать range(len(a)):

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

Ответ 11

Мой код:

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 или внутреннюю часть можно заменить, чтобы сделать его меньше/лучше.