#python #range
#python #диапазон
Вопрос:
Как range () различает выполняемый вызов в этом случае?
Пример:
def ex():
list = [1,2,3,4]
for val in range(len(list)):
print(val)
break
for val in range(len(list)):
print(val)
break
Вывод —
0
0
Короче говоря, мой вопрос в том, почему выходные данные не выдаются таким образом?
0
1
Во время первого вызова range() в ‘первом цикле for’ вызовом является ‘range(len (list))’,
а при первом вызове range() во втором цикле for ‘ вызовом является ‘range (len (list))’что эквивалентно второму вызову range () в ‘первом цикле for’. Как range () узнает, был ли вызов из ‘второго цикла for’, а не ‘первого цикла for’?
Комментарии:
1.
range()
с 1 аргументом возвращает итератор, который начинается с 0 и увеличивается доarg
. Он не вызывается повторно первым циклом for. Затем вы выходите из первого цикла, поэтому запускаете новый цикл, который вызывает другой вызовrange()
, который возвращает итератор, начинающийся с 0.2. Почему это приведет к такому результату? Циклы не начинаются с того места, где остановился другой, и вы прерываетесь на первой итерации.
3. Ответ (на вопрос в вашем названии) зависит от того, какую версию Python вы используете. Python 3
range
отличается от Python 2. Также — не используйтеlist
в качестве идентификатора. Он перезаписывает встроенное имя.4. Однако результаты одинаковы в обеих версиях python.
5.
range(len(list))
в этом случае выдает,range(0, 4)
. Но что дает range (0, 4)?
Ответ №1:
Я не уверен, почему вы ожидаете range
, что помните, что он был вызван ранее. Класс не поддерживает никакого состояния в отношении предыдущих вызовов; он просто делает то, что вы просите. Каждый вызов range(x)
возвращает новый range
объект, который предоставляет числа от 0 до x-1
, когда вы перебираете его. Каждый вызов не зависит от любых предыдущих вызовов.
Чтобы получить описываемое вами поведение, вам необходимо повторно использовать один и тот же итератор для range
объекта в каждом цикле.
Python 3.5.1 (default, Apr 18 2016, 11:46:32)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> l = [1,2,3,4]
>>> r = range(len(l))
>>> for val in r:
... print(val)
... break
...
0
>>> for val in r:
... print(val)
... break
...
0
>>> i = iter(r)
>>> for val in i:
... print(val)
... break
...
0
>>> for val in i:
... print(val)
... break
...
1
Вы можете представить это как
for x in xs:
do_something(x)
это сокращение от
i = iter(xs)
while True:
try:
x = next(i)
except StopIteration:
break
do_something(x)
iter
возвращает свой аргумент, если он уже фактически является итератором, поэтому каждый for
цикл возвращает новый итератор, начинающийся с начала, когда вы пытаетесь выполнить итерацию по итерируемому не-итератору.
Комментарии:
1. каждый вызов возвращает новый объект итератора, инициализированный заданной последовательностью (0, 1, …). Этот итератор — это отмеченный «объект диапазона», но я думаю, что правильный термин — итератор. Цикл for вызывает функцию «next», чтобы фактически выполнить итерацию по последовательности.
2. Сам
range
объект не является итератором; использованиеr
в обоих циклах все равно приводит к выводу в вопросе, потому что каждыйfor
цикл извлекает новый итератор изrange
объекта (черезr.__iter__
).3. Итератор — это то, что предоставляет
__next__
метод, в то время как итерируемый — это то, что предоставляет__iter__
метод.range
определяет__iter__
, но нет__next__
. (range.__iter__
возвращает экземплярrange_iterator
, который является определяющим классом__next__
).4. @chepner Я не знал, как точно выразить свой вопрос. Я думаю, что мы добрались до вашего последнего комментария. Можете ли вы объяснить мне свой последний комментарий более простым способом? Я не понимаю, что заставляет ‘range’ (в вашем ответе) выдавать другой результат, чем ‘iter’ (в вашем ответе)? Я ожидал, что оба будут вести себя одинаково.
5. Игнорируя точную реализацию, главное, что нужно понять, это то, что Python различает объект, который может быть повторен, и состояние текущей итерации.
range
представляет статическую последовательность чисел;range_iterator
представляет процесс итерации по диапазону. У вас может быть несколько активных итераторов, которые выполняют итерации по одному и тому жеrange
объекту, причем каждый из них имеет свое собственное представление о рассматриваемом «текущем» значении.