Ответ 1
Если вам нужно работать с индексами последовательности, тогда да - вы используете его... например, для эквивалента numpy.argsort...
>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]
Часто в выражениях типа 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))
?
Если вам нужно работать с индексами последовательности, тогда да - вы используете его... например, для эквивалента numpy.argsort...
>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]
Краткий ответ: математически, нет, в практическом плане да, например, для преднамеренного программирования.
Технически, я думаю, что математически правильный ответ будет "нет, он не нужен", потому что он выражается с помощью других конструкций, то есть эквивалентен другим конструкциям... что-то вроде того, что язык завершен по Тьюрингу, он делает на самом деле не имеет значения, какие у него синтаксические/парадигматические конструкции, потому что в любом случае в них можно все выразить.
Но на практике я использую 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
"... так что в конце просто нюанс преднамеренного программирования.
Что делать, если вам нужно одновременно получить доступ к двум элементам списка?
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% доволен либо!
Идя по комментариям, а также по личному опыту, я говорю "нет", нет необходимости в 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))
не требуется. Его единственный потенциал - читаемость (его намерение ясно). Но это только предпочтение и стиль кода.
Иногда matplotlib требует range(len(y))
, например, пока y=array([1,2,5,6])
, plot(y)
работает нормально, scatter(y)
нет. Нужно написать scatter(range(len(y)),y)
. (Лично я считаю, что это ошибка в scatter
; plot
, а ее друзья scatter
и stem
должны использовать как можно больше те же последовательности вызовов.)
У меня есть случай использования. Я не считаю, что какой-либо из ваших примеров охватывает.
boxes = [b1, b2, b3]
items = [i1, i2, i3, i4, i5]
for j in range(len(boxes)):
boxes[j].putitemin(items[j])
Я относительно новичок в python, хотя так счастлив узнать более элегантный подход.
Приятно иметь, когда вам нужно использовать индекс для какой-то манипуляции и иметь текущий элемент не хватает. Возьмем, например, двоичное дерево, которое хранится в массиве. Если у вас есть метод, который просит вас вернуть список кортежей, в котором содержатся прямые дочерние узлы, вам нужен индекс.
#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. Но да, вам действительно нужен индекс, если вы делаете какие-то манипуляции.
Иногда вы действительно не заботитесь о самой коллекции. Например, создавая простую модель, подходящую для сравнения "приближения" с необработанными данными:
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
В этом случае значения самой последовательности Фибоначчи были неактуальны. Все, что нам было нужно, это размер входной последовательности, с которой мы сравнивали.
Очень простой пример:
def loadById(self, id):
if id in range(len(self.itemList)):
self.load(self.itemList[id])
Я не могу придумать решение, которое быстро не использует состав диапазона.
Но, скорее всего, это должно быть сделано с try .. except
, чтобы оставаться pythonic, я думаю.
Если вам нужно выполнить итерацию по первым элементам len(a)
объекта b
(который больше, чем a
), вероятно, вы должны использовать range(len(a))
:
for i in range(len(a)):
do_something_with(b[i])
Мой код:
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 или внутреннюю часть можно заменить, чтобы сделать его меньше/лучше.