- Регистрация
- 14.07.18
- Сообщения
- 548
- Онлайн
- 34д 2ч 1м
- Сделки
- 2
- Нарушения
- 0 / 6
Давайте окунёмся немного глубже базовых знаний и посмотрим, насколько можно упростить свой код, как сделать его читаемым и не потерять желание возвращаться к своей работе снова. Добро пожаловать в подробный Python.
Если вы начали изучать Python, посмотрели с десяток обучающих видео, прошли несколько бесплатных курсов, интенсивов и митапов, и пробуете написать свой первый проект, то эта статья, безусловно, вам поможет. Поверхностный анализ обучающих русскоязычных материалов по Python в интернете натолкнул на мысль, что начинающему Python-разработчику редко показывают всю красоту и эффективность этого языка. Базовое (чаще непрофессиональное) обучение предполагает знакомство с простейшими механиками, которые часто встречаются и в других языках. Дорогу осилит идущий, а значит, давайте стремиться к большему.
List comprehensions (Генератор списков)
Генераторы списков в большинстве случаев используют для создания списка из набора значений — чтобы не прибегать к более сложным конструкциям через for и append.
Если говорить предметно, то генератор списка может создать коллекцию значений всего в одну строку. Посмотрим пример:
lst = []
for x in range(10):
lst.append(x**2)
print (lst)
В примере мы взяли последовательность чисел от 0 до 9 (наш range) и каждую итерацию цикла возвели в квадрат, после чего написали результат в конец объявленного выше пустого списка.
Итак, результат:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Пример с использованием генератора списков:
print([x**2 for x in range(10)])
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Из четырёх строк в одну.
Теперь подробнее про синтаксис генератора. В целом, он выглядит так:
[ выражение for итератор in итерируемый объект if условие ]
Да, генератор может содержать ещё и условие, при выполнении которого итерируемые элементы будут попадать в список. Пример:
print([x for x in range(20) if x%2==0])
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
В список попали только чётные числа (если читать по условию, это те, которые делятся на 2 без остатка)
Здесь эффективность генератора проявляется ещё ярче. Вы совмещаете и цикл, и условный оператор в одном выражении и получаете на выходе упорядоченный, изменяемый список.
Теперь, что называется, «контрольный в голову»:
def some_function(y):
return (y + 5) / 2
print([some_function(x) for x in range(10)])
[2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0]
Вы можете использовать выражение как аргумент функции. Это удобно, и ваш код оставит о вас приятные впечатления.
Стоит упомянуть, что генератор существует не только для списков, но и для dict (словарей) и set (множеств) и называется Dict comprehensions и Set comprehensions соответственно. Базовый синтаксис у них аналогичен. Различия я покажу примером:
dict = {num: num**2 for num in range(5)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
set = {x for x in range(10)}
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Обратите внимание на тип скобок в списке и в этих двух примерах.
Распаковка элементов из списка
Если вам необходимо получить определённый элемент из коллекции, то первый и очевидный метод, получить его по индексу.
lst = [1,2,3,4] где lst[1] = 2
Этот метод можно эффективно использовать, если ваша коллекция неизменяемая, и lst[1] всегда будет содержать нужное вам значение. Минус подхода — так называемые «magic numbers». Представьте, что вы читаете чужой код, в котором разработчик получает значение, как в примере. У вас не возникнут вопросы: «А что такое lst[1]? Почему 1 а не 2 и не 20?» Потому и называют такие цифры в скобках «magic numbers». Возникли из ниоткуда, обозначают что-то внутри. Вам стоит научиться применять распаковку.
Python позволяет присваивать значения коллекции отдельным переменным. Это эффективно, если коллекция небольшая. Подробнее в примере:
lst = [1, 2, 3 ]
x, y, z = lst
print(x, y, z)
1 2 3
Вместе с этим, стоит посмотреть, как присваивать несколько значений сразу нескольким переменным, и как красиво заменить переменные.
Множественное присваивание
x, y, z = 1, 2, 3
Это выражение равносильно следующему:
x = 1
y = 2
z = 3
Но вы заняли 3 строки вместо одной. Ничего страшного если ваш проект будет 50 строк, а если 300 или 900?
Замена переменных
Для того чтобы поменять переменные местами, вы можете сделать так:
a=10
b=15
_tmp=a
a=b
b=_tmp
print(a, b)
15 10
Но есть способ сделать эту запись короче:
a, b = 10, 15
a, b = b, a
print(a, b)
15 10
Slicing (Срезы или Слайсы)
Кстати, о больших коллекциях. Что если нам нужны несколько значений из коллекции? А что если они нужны из середины или через одного? Python предоставляет такой механизм, который называется Slicing или Срезы. Синтаксис достаточно прост:
sequence[start:stop:step]
x = [10, 5, 13, 4, 12, 43, 7, 8]
print( x[1:6:2])
[5, 4, 43]
Мы взяли каждый второй элемент в списке от между 1 и 6 индексами. В своей работе вы будете часто прибегать к помощи слайсов, не стоит недооценивать их.
Слайсы можно писать проще. Если начало или конец слайса эквивалентно началу и окончанию списка, их можно не указывать. Это выглядит так:
x = [10, 5, 13, 4, 12, 43, 7, 8]
print( x[:3])
[10, 5, 13]
Здесь берётся срез от 0 до 2 индекса элемента с шагом 1. Мы не указывали начало или шаг, а указали только конец среза. Обратите внимание, что 3 — это индекс конечного элемента, но он не попадает в итоговый список, поэтому срез будет от 0 до 2 индекса.
Теперь пример с указанием старта:
x = [10, 5, 13, 4, 12, 43, 7, 8]
print( x[3:])
[4, 12, 43, 7, 8]
Срез начался с 3 индекса, но в этой ситуации элемент с индексом 3 попал в срез. Аналогичный принцип работает в range. Просто запомните это. Так вы исключите ряд ошибок в своем коде.
Немного симпатичных трюков языка Python
Многие считают, что следить за памятью было модно в эпоху программирования на ассемблере. Многие, но не все. Приведу вам наглядный пример, почему иногда стоит задуматься какую конструкцию применять:
import sys
range_list = range(0, 10000)
print(sys.getsizeof(range_list))
48 # байт
Представьте, что последовательность чисел от 0 до 9999 занимает всего 48 байт.
А вот пример с такой же последовательностью:
import sys
real_list = [x for x in range(0, 10000)]
print(sys.getsizeof(real_list))
87616 # байт = 87 Кб
Две одинаковые последовательности от 0 до 9999. Занимают память с разницей в почти 2000 раз. А если программа содержит 100 таких списков?
Дело в том, что range только притворяется списком, ведёт себя как список, но на самом деле, функция range возвращает класс и, безусловно, загружает меньше памяти.
И наконец, небольшой фокус на количество повторений значения в коллекции (излюбленная задачка в различных обучалках и курсах):
from collections import Counter
print(Counter('abracadabra').most_common(1))
[('a', 5)]
Можно вместо строки использовать и список:
from collections import Counter
lst = [2, 2, 2, 5, 5, 6, 7, 6, 9, 2, 4, 8, 5]
print(Counter(lst).most_common(1))
[(2, 4)]
Аргумент в most_common задаёт количество повторяемых элементов, которые необходимо подсчитать:
from collections import Counter
print(Counter('abracadabra').most_common(3))
[('a', 5), ('b', 2), ('r', 2)]
Стоит обратить внимание, что для корректной работы описанных в статье механизмов необходимо иметь полноценное представление о списках (list), словарях (dict), множествах (set) и кортежах (tuple). Эти типы данных очень коварны и имеют важные различия, на которые стоит обратить внимание.
Если вы начали изучать Python, посмотрели с десяток обучающих видео, прошли несколько бесплатных курсов, интенсивов и митапов, и пробуете написать свой первый проект, то эта статья, безусловно, вам поможет. Поверхностный анализ обучающих русскоязычных материалов по Python в интернете натолкнул на мысль, что начинающему Python-разработчику редко показывают всю красоту и эффективность этого языка. Базовое (чаще непрофессиональное) обучение предполагает знакомство с простейшими механиками, которые часто встречаются и в других языках. Дорогу осилит идущий, а значит, давайте стремиться к большему.
List comprehensions (Генератор списков)
Генераторы списков в большинстве случаев используют для создания списка из набора значений — чтобы не прибегать к более сложным конструкциям через for и append.
Если говорить предметно, то генератор списка может создать коллекцию значений всего в одну строку. Посмотрим пример:
lst = []
for x in range(10):
lst.append(x**2)
print (lst)
В примере мы взяли последовательность чисел от 0 до 9 (наш range) и каждую итерацию цикла возвели в квадрат, после чего написали результат в конец объявленного выше пустого списка.
Итак, результат:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Пример с использованием генератора списков:
print([x**2 for x in range(10)])
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Из четырёх строк в одну.
Теперь подробнее про синтаксис генератора. В целом, он выглядит так:
[ выражение for итератор in итерируемый объект if условие ]
Да, генератор может содержать ещё и условие, при выполнении которого итерируемые элементы будут попадать в список. Пример:
print([x for x in range(20) if x%2==0])
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
В список попали только чётные числа (если читать по условию, это те, которые делятся на 2 без остатка)
Здесь эффективность генератора проявляется ещё ярче. Вы совмещаете и цикл, и условный оператор в одном выражении и получаете на выходе упорядоченный, изменяемый список.
Теперь, что называется, «контрольный в голову»:
def some_function(y):
return (y + 5) / 2
print([some_function(x) for x in range(10)])
[2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0]
Вы можете использовать выражение как аргумент функции. Это удобно, и ваш код оставит о вас приятные впечатления.
Стоит упомянуть, что генератор существует не только для списков, но и для dict (словарей) и set (множеств) и называется Dict comprehensions и Set comprehensions соответственно. Базовый синтаксис у них аналогичен. Различия я покажу примером:
dict = {num: num**2 for num in range(5)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
set = {x for x in range(10)}
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Обратите внимание на тип скобок в списке и в этих двух примерах.
Распаковка элементов из списка
Если вам необходимо получить определённый элемент из коллекции, то первый и очевидный метод, получить его по индексу.
lst = [1,2,3,4] где lst[1] = 2
Этот метод можно эффективно использовать, если ваша коллекция неизменяемая, и lst[1] всегда будет содержать нужное вам значение. Минус подхода — так называемые «magic numbers». Представьте, что вы читаете чужой код, в котором разработчик получает значение, как в примере. У вас не возникнут вопросы: «А что такое lst[1]? Почему 1 а не 2 и не 20?» Потому и называют такие цифры в скобках «magic numbers». Возникли из ниоткуда, обозначают что-то внутри. Вам стоит научиться применять распаковку.
Python позволяет присваивать значения коллекции отдельным переменным. Это эффективно, если коллекция небольшая. Подробнее в примере:
lst = [1, 2, 3 ]
x, y, z = lst
print(x, y, z)
1 2 3
Вместе с этим, стоит посмотреть, как присваивать несколько значений сразу нескольким переменным, и как красиво заменить переменные.
Множественное присваивание
x, y, z = 1, 2, 3
Это выражение равносильно следующему:
x = 1
y = 2
z = 3
Но вы заняли 3 строки вместо одной. Ничего страшного если ваш проект будет 50 строк, а если 300 или 900?
Замена переменных
Для того чтобы поменять переменные местами, вы можете сделать так:
a=10
b=15
_tmp=a
a=b
b=_tmp
print(a, b)
15 10
Но есть способ сделать эту запись короче:
a, b = 10, 15
a, b = b, a
print(a, b)
15 10
Slicing (Срезы или Слайсы)
Кстати, о больших коллекциях. Что если нам нужны несколько значений из коллекции? А что если они нужны из середины или через одного? Python предоставляет такой механизм, который называется Slicing или Срезы. Синтаксис достаточно прост:
sequence[start:stop:step]
- start = индекс элемента, с которого начинается срез;
- stop = индекс элемента, которым заканчивается срез;
- step = шаг среза.
x = [10, 5, 13, 4, 12, 43, 7, 8]
print( x[1:6:2])
[5, 4, 43]
Мы взяли каждый второй элемент в списке от между 1 и 6 индексами. В своей работе вы будете часто прибегать к помощи слайсов, не стоит недооценивать их.
Слайсы можно писать проще. Если начало или конец слайса эквивалентно началу и окончанию списка, их можно не указывать. Это выглядит так:
x = [10, 5, 13, 4, 12, 43, 7, 8]
print( x[:3])
[10, 5, 13]
Здесь берётся срез от 0 до 2 индекса элемента с шагом 1. Мы не указывали начало или шаг, а указали только конец среза. Обратите внимание, что 3 — это индекс конечного элемента, но он не попадает в итоговый список, поэтому срез будет от 0 до 2 индекса.
Теперь пример с указанием старта:
x = [10, 5, 13, 4, 12, 43, 7, 8]
print( x[3:])
[4, 12, 43, 7, 8]
Срез начался с 3 индекса, но в этой ситуации элемент с индексом 3 попал в срез. Аналогичный принцип работает в range. Просто запомните это. Так вы исключите ряд ошибок в своем коде.
Немного симпатичных трюков языка Python
Многие считают, что следить за памятью было модно в эпоху программирования на ассемблере. Многие, но не все. Приведу вам наглядный пример, почему иногда стоит задуматься какую конструкцию применять:
import sys
range_list = range(0, 10000)
print(sys.getsizeof(range_list))
48 # байт
Представьте, что последовательность чисел от 0 до 9999 занимает всего 48 байт.
А вот пример с такой же последовательностью:
import sys
real_list = [x for x in range(0, 10000)]
print(sys.getsizeof(real_list))
87616 # байт = 87 Кб
Две одинаковые последовательности от 0 до 9999. Занимают память с разницей в почти 2000 раз. А если программа содержит 100 таких списков?
Дело в том, что range только притворяется списком, ведёт себя как список, но на самом деле, функция range возвращает класс и, безусловно, загружает меньше памяти.
И наконец, небольшой фокус на количество повторений значения в коллекции (излюбленная задачка в различных обучалках и курсах):
from collections import Counter
print(Counter('abracadabra').most_common(1))
[('a', 5)]
Можно вместо строки использовать и список:
from collections import Counter
lst = [2, 2, 2, 5, 5, 6, 7, 6, 9, 2, 4, 8, 5]
print(Counter(lst).most_common(1))
[(2, 4)]
Аргумент в most_common задаёт количество повторяемых элементов, которые необходимо подсчитать:
from collections import Counter
print(Counter('abracadabra').most_common(3))
[('a', 5), ('b', 2), ('r', 2)]
Стоит обратить внимание, что для корректной работы описанных в статье механизмов необходимо иметь полноценное представление о списках (list), словарях (dict), множествах (set) и кортежах (tuple). Эти типы данных очень коварны и имеют важные различия, на которые стоит обратить внимание.