Как ‘range ()’ работает внутри?

#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 объекту, причем каждый из них имеет свое собственное представление о рассматриваемом «текущем» значении.