Цикл for
может обходить любой итерируемый объект.
В других языках это соответствует циклу foreach
то есть тут for
именно итерируется по некой последовательности, а не по просто
проверяет условие как for
в других языках. По этому используется
генератор последовательности range()
чтобы сгенерить что-то по
чему можно итерироваться.
Следующие 2 метода эквивалентны:
for number in range(5):
print('number = ', number)
for num in [0, 1, 2, 3, 4]:
print('num = ', num)
# Вывод
# ('number = ', 0)
# ('number = ', 1)
# ('number = ', 2)
# ('number = ', 3)
# ('number = ', 4)
# ('num = ', 0)
# ('num = ', 1)
# ('num = ', 2)
# ('num = ', 3)
# ('num = ', 4)
Цикл for
применим к словарям, использование словаря эквивалентно
вызову метода .keys()
который возвращает список из ключей
словаря, по скольку словари не отсортированная структура,
полученные ключи тоже не отсортированы, так что вывод в цикле,
тоже будет не отсортирован, мы просто получаем ключи и так их
и выводим.
Следующие 2 метода for
эквивалентны:
a_dict = {"two": 2, "three": 3, "one": 1 }
print('a_dict.keys() = ', a_dict.keys())
print('type = ', type(a_dict.keys()))
for key in a_dict:
print(key)
for key in a_dict.keys():
print(key)
# a_dict.keys() = dict_keys(['two', 'three', 'one'])
# type = <class 'dict_keys'>
# two
# three
# one
# two
# three
# one
Для того чтобы отсортировать ключи, используется спец функция
sorted()
она отсортирует полученные ключи, которые уже и можно
будет выводить.
a_dict = {"C": 3, "B": 2, "A": 1}
keys = a_dict.keys()
keys = sorted(keys)
print('a_dict.keys() = ', a_dict.keys())
print('sorted(a_dict.keys()) = ', keys)
for key in keys:
print(key)
# Вывод
# a_dict.keys() = dict_keys(['C', 'B', 'A'])
# sorted(a_dict.keys()) = ['A', 'B', 'C']
# A
# B
# C
Таким образом можно вывести ключи, и если требуется по ним можно получать и значения по этим ключам:
a_dict = {"C": 3, "B": 2, "A": 1}
keys = sorted(a_dict.keys())
for key in keys:
print(key, ' = ', a_dict[key])
# Вывод
# A = 1
# B = 2
# C = 3
У циклов есть блок else
который исполняется всегда если не было
использовано оператора break
. Такая же логика используется и в
while
.
Блок else
срабатывает по завершению работы цикла, но при
условии, что цикл завершен естественным образом, если же
цикл завершен оператором brake
то блок else
не сработает.
for i in range(1, 11):
print('i = ', i)
else:
print('Блок else')
for i in range(1, 11):
if i == 5:
print('i равен 5 ')
break
print('i = ', i)
else:
print('Блок else')
for i in range(1, 11):
if i == 5:
print('i равен 5 ')
continue
print('i = ', i)
else:
print('Блок else')
# Вывод
# i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7
# i = 8 i = 9 i = 10
# Блок else
# i = 1 i = 2 i = 3 i = 4
# i равен 5
# i = 1 i = 2 i = 3 i = 4
# i равен 5
# i = 6 i = 7 i = 8 i = 9 i = 10
# Блок else
Первоначально в версии python2 существовало 2 метода range()
и
xrange()
метод range() просто создавал обычный список со всеми
значениями, метод xrange()
являлся генератором, спец класса
range
и как обычный генератор, создавал новый элемент
последовательности только при обращении к нему, что позволяло
экономить память, и не хранить созданный список в памяти полностью.
Метод xrange()
работал лучше, чем обычный range()
, дело в том
что python сам по себе очень прожорливый на память язык
программирования, к примеру обычный пустой список в python весит
64 байта
import sys
list_1 = list()
print('list_1 = ', list_1)
print('Empty list in bytes = ', sys.getsizeof(list_1))
# Вывод
# ('list_1 = ', [])
# ('Empty list in bytes = ', 64)
Если же мы используем для генерации списка range() из старой версии python2, для генерации 1000 элементов, то получим следующий результат:
import sys
list_2 = list(range(1, 1001))
print('list_2 = ', list_2)
print('Not empty in bytes = ', sys.getsizeof(list_2))
# Вывод
# list_2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ...
# 995, 996, 997, 998, 999, 1000]
# Not empty in bytes = 9112
Можно увидеть что хранить весь список в памяти просто невыгодно,
он занимает 9112 байт
по этому и использовался метод xrange()
ибо будучи генератором, он не хранил всю последовательноcть в
памяти, а генерировал новое значение сразу при обращении, далее
в версии python3 метод range()
был удален, а метод xrange()
был переименован в метод range()
с ним мы теперь и работаем.
Если запустить следующий код в версии Python-2 то, можно увидеть разницу методов.
func_range = range(1, 11)
print('type(func_range) = ', type(func_range))
print('func_range = ', func_range)
func_xrange = xrange(1, 11)
print('type(func_xrange) = ', type(func_xrange))
print('func_xrange = ', func_xrange)
# Вывод
# ('type(func_range) = ', <type 'list'>)
# ('func_range = ', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# ('type(func_xrange) = ', <type 'xrange'>)
# ('func_xrange = ', xrange(1, 11))
Тут и можно увидеть что метод range()
генерирует список, то время
как xrange()
создает спец тип данных.
Метод range()
генерирует последовательность элементов, с
заданным шагом:
var_1 = range(1, 11)
print('var_1 = ', var_1)
var_2 = range(1, 11, 2)
print('var_2 = ', var_2)
var_3 = range(1, 11, -1)
print('var_3 = ', var_3)
# Вывод
# ('var_1 = ', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# ('var_2 = ', [1, 3, 5, 7, 9])
# ('var_3 = ', [])
Метод range()
создает не список, а особый объект, по которому
можно итерироваться при помощи цикла for
но списком не является,
и для превращения в список требуется указать это явно:
some = range(1, 11)
print(some)
print(type(some))
my_list = list(some)
print(my_list)
print(type(my_list))
# Вывод
# range(1, 11)
# <class 'range'>
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# <class 'list'>