Базовый синтаксис

Введение

На начало 2022 года Python является самым популярным языком программирования по версии TIOBE. Python понятен, прост, универсален. Говорят, что как первый язык программирования для изучения Python отличный вариант. В этом блоке постепенно и подробно разберемся со всеми разделами базового синтаксиса, начиная от типов данных и заканчивая функциями.

Установка Python

Если вы пользуетесь, например, Ubuntu, то python у вас уже предустановлен по умолчанию, но если нет, то придется заняться его установкой. В установке python нет ничего особенного, есть официальный сайт, с которого вы можете скачать актуальную версию. Python активно развивающийся язык и его версии обновляются достаточно часто, за всеми новостями, связанными с обновлениями, также можно следить в соответствующем разделе официального сайта. Каждый раз идти на сайт и обновлять версию под актуальную совершенно необязательно, скорее всего версии, установленной вами в самом начале вашего обучения, хватит надолго, поскольку тонкие изменения выпускаемые разработчиками новичку никак не будут полезны.
Скачав файл установки с официального сайта достаточно провести установку следуя инструкциям, рекомендую использовать ручную установку, оставив все галочки на странице Optional Features, а в Advanced Options изменить путь установки, установив вместо пути по умолчанию, свой путь до папки с названием python(версия) на корневом диске. После установки вы можете зайти в cmd и прописать python, если после Enter вы увидите информацию о версии python значит установка прошла успешно.

Редактор кода

После успешной установки вы уже можете пользоваться python, правда писать программы в cmd или во встроенной среде программирование Python IDLE не очень удобно. Есть более мощные инструменты для написания кода, все они имеют название IDE, что расшифровывается как интегрированная среда разработки. Их существует большое количество, но когда речь заходит о разработке на python сразу на ум приходит IDE под названием PyCharm, можете установить его, но это совершенно необязательный выбор, если вы уже пользуетесь VScode или Atom и вам удобно, то используйте их.
Установить PyCharm можно с официального сайта. Существуют две версии: Professional - платная и Community - бесплатная, воспользуемся Community версией. Для Ubuntu традиционно существует команда
sudo snap install [pycharm-professional|pycharm-community] --classic,
для Windows и macOS используются установочные файлы.
Поговорим о windows, установка совершается обычным нажатием 'Далее' пока программа не установится, после установки вы увидите стартовое окно, где у вас будет выбор создать новый проект или открыть существующей, создайте новый проект, дайте ему любое название и после этого разверните Project Interpreter. В этом окне мы рассказываем PyCharm'у какую версию python мы бы хотели использовать в нашем проекте, переключитесь на Existing Interpreter, скорее всего в этой строке у вас подтянется ранее установленная версия python, но если нет вы можете выбрать ее вручную, нажав на значок ручного выбора справа от строки с выбором версии и во вкладке System Interpreter будут показаны все доступные версии python, установленные на вашем устройстве.
Pycharm готов к использованию, в папке проекта вы можете создавать новые файлы и папки нажатием правой кнопкой мыши и выбором необходимо расширения файла. В самом файле вы можете писать код, исполнять который можно либо нажатием правой кнопкой мыши по области и нажатием на кнопку run, либо сочетанием горячих клавиш Ctrl + Shift + f10. Вывод вы увидите в окне run, по умолчанию расположенным внизу PyCharm. PyСharm очень мощный инструмент с большим количеством настроек, но сейчас отвлекаться на них не вижу большого смысла, когда вам понадобится воспользоваться настройками я думаю вы без особых проблем сами с этим разберетесь.
Весь код пишется согласно соглашению PEP 8, это соглашение описывает какие должны быть отступы пробелы между строками и прочие оформительские правила. Не рекомендую открывать этот мануал и заучивать эти правила, ваши руки быстро привыкнут к правильному написанию сами, PyCharm поддерживает PEP 8 и сочетанием клавиш Alt + Ctrl + L ваш код отформатируется согласно этому соглашению. Периодически используйте сочетание этих клавиш и вы очень быстро освоитесь с PEP 8 и будете самостоятельно писать код правильно.
На этом подготовка к программированию закончена, можно приступать непосредственно к знакомству с самим языком написанию своей первой программы.

Типы данных

Для написания программ используются данные разных типов.

Название Тип Пример
Целочисленный тип (integer) int 1, 2, 3, 15 ...
Вещественный тип (floating point) float 1.5, 2.3, 0.3, 15.0 ...
Строковый тип (string) str "hello", "a", "100" ...
Списки (list) list [1, 2, "hello"], [3.5, "400"], [200, 300, 8] ...
Словари (dictionary) dict {"key": "value"}, {"phone": "33333", "name":"jora", "language":"python"} ...
Кортежи (tuple) tuple (1, 3, "abc"), (100.25, "200", 5), ("a", "b", "c") ...
Множества (set) set {1, 2, 3}, {"a", 20.5, "hello"}, {2.0, "cba", 15} ...
Булевы значения (boolean) bool True, False

Конечно со временем мы познакомимся со всеми, но для начала познакомимся с первыми тремя. С самими типами вопросов не возникает, целочисленный тип - целые числа, вещественный тип - дробные числа, строковый тип - последовательности символов. Но как представить эти типы данных внутри языка программирования? Конечно, при помощи переменных. Для того чтобы создать переменную в Python достаточно дать ей имя и через знак равно написать ее значение.

a = 5
float_a = 5.0
name = 'jora'

b = a + float_a

c = b * a

d = b / c

names = name * 2

Сразу посмотрим на примере, создадим переменную 'а' со значением 5 - целочисленный тип, переменную float_a со значением 5.0 - вещественный тип, обратите внимание .0 после цифры тоже делает значение вещественным и переменную 'name' со значением 'jora', тут хочу обратить ваше внимание на то, что разницы между двойными - " " и одинарными - ' ' кавычками нету. Далее я написал пару примеров с этими переменными, как думаете какие значения примут переменные b, c, d и names? Для того чтобы понять, как реализуются простые математические вычисления в python, обратимся к консоли.

Python Console
>>> 2 + 2
4
>>> 8 - 5
3
>>> 4 - 7
-3
>>> 3.3 + 2
5.3
>>> 10 / 5
2.0
>>> 3 * 5
15
>>> 3 * 5.0
15.0
>>> 8.0 / 4.2
1.9047619047619047
>>> 0 * 10
0

Числа можно складывать, вычитать, умножать и делить, все как в обычной математике. Ничего необычного, кроме одного момента, деление всегда возвращает вещественный тип. Также если в выражении есть хоть одно вещественное значение, независимо от типа операции результат ее исполнения всегда будет вещественным. Помимо классических математических операций есть еще несколько.

Символ Математическое действие
+ Сложение
- Вычитание
* Умножение
/ Деление
** Возведение в степень
// Целочисленное деление
% Остаток от деления

 

Python Console
>>> 2 ** 10
1024
>>> 15 % 3
0
>>> 17 % 3
2
>>> 15 // 3
5
>>> 17 // 3
5
>>> 19 // 3
6

Целочисленное деление показывает сколько раз делитель помещается в делимом, то есть 15 // 3 = 5 и 17 // 3 = 5, потому что в обоих случаях 3 помещается в эти числа 5 раз. Остаток от деления показывает, какое число остается после совершения деления. Больше математических действий доступно во встроенной библиотеке math, но ее мы коснемся позже.

b =  10.0   c =  50.0   d =  0.2   names =  jorajora

Process finished with exit code 0

Разобравшись с простыми математическими операциями можно посмотреть что же получится в примерах выше. Думаю с числами все понятно интуитивно, а вот со строками результат неочевиден. Если строки умножать на значение, то результатом такой операции будет строка повторяющаяся столько раз, на какое значение она была умножена. А как думаете что если ниже добавить такое выражение:
names_2 = names + name
names_2 будет равняться 'jorajorajora', отсюда сделаем вывод, что строки можно умножать и складывать, а вот поделить строку на строку или вычесть строку из строки не выйдет.

Если вы тоже писали этот код, то возможно вы уже попробовали исполнить его, но вместо результата как в примере вы увидели пустой ответ и сообщение Process finished with exit code 0, которое свидетельствует о том, что программа отработала успешно. Вы ничего не увидели, потому что вы не вывели результат на печать. Для того чтобы вывести результат программы, существует функция print(), а для того чтобы не вводить значение внутри программы, а запросить его у пользователя существует функция input().

Давайте теперь выведем результат. Для того чтобы в pycharm запустить программу можно воспользоваться комбинацией горячих клавиш ctrl + shift + f10, либо нажать правой кнопкой мыши по окну программы и нажать на зеленый треугольник с надписью run.

CODE
 
a = 5
float_a = 5.0
name = 'jora'

b = a + float_a

c = b * a

d = b / c

names = name * 2

names_2 = names + name

print('b = ', b, '  c = ', c, '  d = ', d, '  names = ', names)
print(names_2)
RESULT
b =  10.0   c =  50.0   d =  0.2   names =  jorajora
jorajorajora

Process finished with exit code 0

Как упоминалось выше функция print() выводит на экран значение, переданное внутрь функции print(). Для того чтобы передать внутрь print какую-то переменную используемую в программе, достаточно ее написать, кавычки в таком случае не требуются. Для того чтобы передать текст, его необходимо обернуть в кавычки. Все значения внутри print должны быть перечислены через запятую. print может быть не один, если в программе 2 print, то значение второго будет выведено на новой строке, так же как и следующий print, если таковой имеется. Также обратите внимание, что пробелы внутри кавычек это тоже символы и они учитываются в выводе. Внутри print() можно также выполнять математические операции.

CODE
a = 5
b = 10.0
print(a - b)
print(a + b - (b * 20))
print((b - b - b) * (a * 22) / (100 - (a * a * 15)))
RESULT
-5.0
-185.0
4.0

Process finished with exit code 0

Например, это можно сделать так. Комбинирование чисел и переменных также не запрещено.

Теперь давайте из абстрактных примеров перейдем к реальным задачам.

CODE
a = float(input('a = '))
b = float(input('b = '))
print('Площадь прямоугольника = ', a * b)
print('Периметр прямоугольника = ', 2 * (a + b))
RESULT
a = 5
b = 10
Площадь прямоугольника =  50.0
Периметр прямоугольника =  30.0

Process finished with exit code 0

Например напишем вот такую программу. Функция input() запрашивает данные у пользователя и присваивает их той переменной, внутри которой была вызвана эта функции, а текст написанный внутри команды input() будет виден пользователю. Во вкладке с результатом это хорошо видно, 'a = ' и 'b = ' - содержимое команд input(), зеленые цифры данные введенные пользователем, в данном случае мной. Обратите внимание, по умолчанию любое содержимое переданное в функцию input() будет ровняться строковому типу, по этому если мы хотим преобразовать значение к другому типу, нужно обернуть input() в функцию соответсвующую преобразованию в тот тип данных, который нас интересует, в данном случае обернем input() в int() для преобразования полученных данных к целочисленному типу данных. С функцией print() мы уже разобрались ранее. Вот так в четыре строчки можно посчитать площадь и периметр любых прямоугольников, стороны которых являются целыми числами. Если мы захотим считать площадь и периметр прямоугольников с дробными сторонами, то просто заменим int() на float(). При чем во float() можно будет передавать целочисленное значение, например 5, и функция float() автоматически преобразует цифру 5 к виду 5.0.

Еще пару слов о Print
CODE
a = float(input('a = '))
b = float(input('b = '))
print('Площадь прямоугольника = ', a * b, '\n'
      'Периметр прямоугольника = ', 2 * (a + b))
RESULT
a = 5
b = 3.81
Площадь прямоугольника =  19.05
Периметр прямоугольника =  17.62

Process finished with exit code 0

Нам необязательно для переноса строки писать новый print(), для этих целей в Python есть служебный символ \n, который равен переносу строки. Правда как можно заметить перед словом 'периметр' появился пробел, конечно, хотелось бы, чтобы его не было. Этот пробел появляется из-за скрытого параметра функции print(), который по умолчанию равен пробелу.

CODE
a = float(input('a = '))
b = float(input('b = '))
print('Площадь прямоугольника = ', a * b, '\n',
      'Периметр прямоугольника = ', 2 * (a + b), sep='')
RESULT
a = 12
b = 15
Площадь прямоугольника = 180.0
Периметр прямоугольника = 54.0

Process finished with exit code 0

Параметр 'sep=' поможет нам решить проблему с пробелом, просто заменим его содержимое на пустое значение, в python это делается пустыми кавычками. Пробела теперь нет. В параметр sep можно передать любую последовательность символов, тогда содержимое print() будет разделено содержимым параметра sep (sep от англ. separate - разделять). Служебный, или грамотней назвать его экранированный, символ \n не единственный в python.

Экранированная последовательность Описание
\n Перенос строки
\\ Используется для записи обратного слэша
\' Используется для записи одинарной кавычки
\" Используется для записи двойной кавычки
\t Горизонтальная табуляция
\v Вертикальная табуляция
\r Возврат каретки (курсора) в начало строки
\b Удаление предыдущего символа (аналог клавиши backspace)
\f Перенос на новую строку, причем отступ новой строки равен длине символов стоящих до \f
\0 Null
\ooo Восьмеричный код символа
\xhh Шестнадцатеричный код символа
\N{id} id Unicode символа
\uhhhh Шестнадцатибитный код символа Unicode
\Uhhhhhhhh Тридцатидвухбитный код символа Unicode
\a Гудок (ни разу не встречал, чтобы его использовали)

На самом деле, ни разу не встречал я не только символ \a, но и шесть символов, стоящих до него. Скорее всего, пользоваться любыми символами, кроме первых четырех, вы будете очень редко, тем не менее знать об их существовании не помешает.

Последний необязательный параметр функции print(), который хотелось бы упомянуть это end.

CODE
a = float(input('a = '))
b = float(input('b = '))
print('Площадь прямоугольника = ', a * b, '\n',
      'Периметр прямоугольника = ', 2 * (a + b), sep='',
      end='\n\nНовая строка')
RESULT
a = 5
b = 4
Площадь прямоугольника = 20.0
Периметр прямоугольника = 18.0

Новая строка
Process finished with exit code 0

'end=' - необязательный параметр, который по умолчанию равен '\n'. Содержимым, которе передано в параметр end, будет заканчиваться содержимое функции print().

Работа со строками

Давай напишем классическую программу, для обучения работы со строками.

CODE
first_name = input()
last_name = input()
print(f'Приветствую, {first_name} {last_name}')
RESULT
иван
иванов
Приветствую, иван иванов

Process finished with exit code 0

f-строка поможет нам в реализации этой задачи. f-строкой вы будете пользоваться очень часто, это полезный, простой и удобный инструмент для работы со строками. В f-строку можно поместить любые переменные, заключив их в фигурные скобки. Все пробелы и прочее форматирование внутри f-строк сохраняется.

CODE
first_name = input().title()
last_name = input()
print(f'Приветствую, {first_name} {last_name.title()}')
RESULT
иван
иванов
Приветствую, Иван Иванов

Process finished with exit code 0

Еще один полезный метод для работы со строками - title(). title() преобразует каждый первый символ новой последовательности в верхнему регистру. Можно применить его к нужной строке в любом месте кода, как показано на примере.

CODE
first_name = input().lower()
last_name = input().upper()
print(f'Приветствую, {first_name} {last_name}')
RESULT
ИВАН
иванов
Приветствую, иван ИВАНОВ

Process finished with exit code 0

Для смены регистра всех символов существую методы lower() и upper() для преобразования символов в нижний и верхний регистр соответственно.

CODE
first_name = 'Иван '
last_name = ' Иванов '
print(f'Приветствую, {first_name.rstrip()} {last_name.strip()}')
RESULT
Приветствую, Иван Иванов

Process finished with exit code 0

Если требуется удалить лишние пробелы существует метод strip(), который удалит пробелы с обеих сторон. Если требуется удалить пробелы только с левой или с правой стороны, существуют методы lstrip() и rstrip() соответственно, где l - left, а r - right.

first_name = 'Иван '.strip()  # .strip(), .lstrip(), .rstrip() уберут лишние пробелы по краям
last_name = input().title()   # title() преобразует первый символ каждой новой последовательности к верхнему регистру
# f-строка позволяет пользоваться переменными, для этого достаточно поместить их в {}, внутри {} к переменным можно
# применить доступные методы, такие например как .lower() и .upper() для перевода символов в нужный регистр
print(f'Приветствую, {first_name.lower()} {last_name}')

Внутри кода можно, и даже нужно, оставлять комментарии. Делается это после символа #. Комментарии никак не влияют на работу кода, и они не видны пользователю. Они нужны для других программистов, которые по каким-то причинам будут просматривать ваш код, чтобы они могли быстрее разобраться, что и для чего вы написали. Естественно комментарии не нужно оставлять ко всему подряд, как я сделал в примере, комментарии оставляются в более сложных программах, в местах где они действительно нужны. Писать уместные, лаконичные и понятные комментарии это важный навык в работе программиста. Работа над проектом обычно происходит не в одиночку, поэтому опытные программисты обычно ожидают увидеть комментарии в том коде, за который они берутся.

Строки. Срезы. Неизменяемые типы данных

Типы данных в python можно поделить на изменяемые(mutable) и неизменяемые(immutable). Все три типа данных, которые мы с вами разобрали, являются неизменяемыми, также к неизменяемым типам данных относятся кортежи. Как понять, чем отличается неизменяемые и изменяемые типы данных в python. Каждая переменная, которую мы объявили внутри программы, занимает определенную ячейку памяти. Эту ячейку можно увидеть, если применить к переменной функцию id().

CODE
a = 2
print(id(a))
a = a + 2
print(id(a))
RESULT
139780941527376
139780941527440

Process finished with exit code 0

Так вот, если например мы создадим переменную 'a' со значением 2, а после перезапишем ее значение на 4, прибавив еще 2, то ячейка памяти, отведенная для переменной 'a' изменится. Это поведение характерно для неизменяемых типов данных. Изменяемые типы сохраняют ячейку памяти отведенную им, даже после их изменения.

Числа мы изменяем обычными математическими действиями. А что со строками. Мы уже знаем, что строки можно умножить на какое-нибудь целочисленное значение, и результатом такого действия будет повторение строки, количество повторений зависит от числа, на которое она была умножена. Строки можно складывать, результатом будут склеенные в одну строки, сложение строк в программировании называется конкатенация. Строка, как упоминалось ранее это последовательность символов. И в python с этой последовательностью можно взаимодействовать.

CODE
string = 'Hello, Ivan Ivanov'
print(string[1:])
print(string[2:5])
print(string[:10])
print(string[1:12:2])
print(string[::3])
print(string[::-1])
RESULT
ello, Ivan Ivanov
llo
Hello, Iva
el,Ia 
Hl aIn
vonavI navI ,olleH

Process finished with exit code 0

При помощи квадратных скобок мы можем взять срез символов из строки. Первое значение это символ, с которого мы начинаем срез. Через двоеточие значение, которым мы заканчиваем срез. Обратите внимание, если последовательность заканчивается на цифру например 5, то последним возьмется четвертый символ последовательности, а не пятый. Также обращаю ваше внимание, что индексы символов начинаются не с 1, а с 0.

Например, вот такие индексы принадлежат символам строки из примера. Пробелы это тоже символы, которым присвоен свой индекс.
Если в срезе указано только первое значение, то вторым значением будет автоматически считаться значение последнего индекса, если не указано первое значение, то срез начнется со значения нулевого индекса. Если указать [:] это будет означать, что следует взять всю последовательность символов.

В срезе можно указать третье значение. Его значение это шаг, с которым мы проходимся по последовательности символов. То есть, если третьим значением указано 2, это будет означать, что мы пропускаем каждый второй символ последовательности. На примере хорошо видно как это устроено.

Срез можно брать в обратном порядке. Последнему символу присвоен индекс -1. Обратные индексы всегда начинаются с -1, а не с нуля. Например запись [::-1] перевернет строку задом наперед.

CODE
string = 'Hello, Ivan Ivanov'
slice_1 = string[:3]
slice_2 = string[6]
slice_3 = string[10:]
print(slice_1 + slice_2 + slice_3)
RESULT
Hel n Ivanov

Process finished with exit code 0

Срез можно присвоить переменной, а после использовать их. Также можно взять конкретный символ, просто указав в квадратных скобках его индекс.

bool. Операторы сравнения

Логический тип bool может вернуть только одно из двух значений. True - истина, False - ложь. Для того чтобы посмотреть, как это работает на практике, познакомимся с операторами сравнения.

Символ Описание
< Меньше
> Больше
<= Меньше или равно
>= Больше или равно
== Равно
!= Не равно
not Логическое НЕ
in Проверка на вхождение, например, символа в последовательность
not in Проверка на не вхождение, например, символа в последовательность
and Логическое И. Через оператор and можно добавлять в конструкции дополнительные условия
or Логическое ИЛИ. Оператор or, используется там, где требуется соблюдения хотя бы одного условия

 

CODE
string = 'Hello, Ivan Ivanov'
a = 10
b = 4
print(a < b)
print(a != b)
print(10 >= a)
print(4 == b)
print('o' in string)
print('g' not in string)
RESULT
False
True
True
True
True
True

Process finished with exit code 0

Тип bool также относится к неизменяемым типам данных.

Остальные операции и методы строк
CODE
string = 'Hello, Ivan Ivanov'
print(len(string), '\n')

count_a = string.count('a')
count_a_in_slice = string.count('a', 4, 10)
print(count_a)
print(count_a_in_slice, '\n')

replace_string = string.replace('l', 'd')
replace_string_count = string.replace('l', 'd', 1)
print(replace_string)
print(replace_string_count, '\n')

find_I = string.find('I')
find_I_in_slice = string.find('I', 9)
print(find_I)
print(find_I_in_slice, '\n')

rfind_I = string.find('I')
print(rfind_I)
RESULT
18 

2
1 

Heddo, Ivan Ivanov
Hedlo, Ivan Ivanov 

7
12 

7

Process finished with exit code 0

Первое о чем нужно упомянуть - функция len(). len() возвращает количество символов в строке.
Метод .count() вернет количество вхождений указанного символа в строку. Метод .count() можно применить к срезу, для этого срез указывается после необходимого символа через запятую.
Метод .replace() заменяет символы в сроке. Первое значение - символ, который нужно заменить, второе - символ, на который нужно заменить. Если третьим параметров передать цифру, то это будет означать сколько из присутствующих в последовательности символов нужно заменить.
Метод .find() вернет индекс первого найденного символа, переданного в метод .find(). Если вторым параметром указать цифру, то это будет означать с какого индекса строки начать поиск.
Метод .rfind() делает тоже, что и .find() только .rfind() проходит по строке в обратном направлении.

CODE
string = 'Hello, Ivan Ivanov'

print(len(string), '\n')

rjust = string.rjust(22)
print(rjust)
print(len(rjust), '\n')

rjust_2 = string.rjust(22, 'a')
print(rjust_2, '\n')

split = string.split()
print(split, '\n')

split_2 = string.split("n")
print(split_2, '\n')

join = ' '.join(string)
print(join, '\n')

join_2 = '+'.join(string.split())
print(join_2)
RESULT
18 

    Hello, Ivan Ivanov
22 

aaaaHello, Ivan Ivanov 

['Hello,', 'Ivan', 'Ivanov'] 

['Hello, Iva', ' Iva', 'ov'] 

H e l l o ,   I v a n   I v a n o v 

Hello,+Ivan+Ivanov

Process finished with exit code 0

Методы .rjust() и .ljust() увеличивают длину строки до значения, которое указано в этих методах. .rjust() добавляет символы слева, .ljust() добавляет символы справа. Если значение не указано, то заполняет пробелами, если указано, то заполняет указанным символом.
Метод .split() делит строку по указанному символу, по умолчанию разделяет по пробелам.
Метод .join() обратное действие метода .split(), предварительно превращена в список строка, конкатенируется в новую строку по указанному символу.

Для удобства представлю все эти методы таблицей.

Методы и функции строк Описание
len() Функция, которая возвращает количество символов в строке
.title() Преобразует каждый первый символ нового слова в строке к верхнему регистру
.lower() Преобразует все символы в нижний регистр
.upper() Преобразует все символы в верхний регистр
.strip(), .lstrip(), .rstrip() Обрезание пробелов по краям строк
.count() Вернет количество вхождений указанного символа в строку. Можно применить к срезу
.replace() Заменяет символы в строке
.find(), .rfind() Вернет индекс первого найденного символа, переданного в метод
.rjust(), .ljust() Увеличивают длину строки до значения, переданного в эти методы
.split() Делит строку по указанному символу, по умолчанию разделяет по пробелам
.join() Конкатенирует список строк по переданному указателю
Списки

Первый изменяемый тип данных, который мы разберем, - списки. Список - упорядоченная коллекция объектов произвольных типов. Создаются списки внутри квадратных скобок через запятую.

CODE
list_1 = [1, 2, 3, 4, 5]
list_2 = ['hello', 'ivan', 'ivanov']
list_3 = ['hello', 3.4, 'abc']
string = 'hello'

print(list(string))
print(list_1[2])
print(list_2[1])
print(list_3[0:2])
RESULT
['h', 'e', 'l', 'l', 'o']
3
ivan
['hello', 3.4]

Process finished with exit code 0

Как было сказано выше, тип данных не важен. Со списками можно работать так же, как и со строками, то есть обращаться по индексу к элементу списка, первый индекс также равен нулю. Также можно брать их срезы, с любым шагом и в любом направлении.
Функция list() превращает строку в список, как видно на примере. Для чисел эта функция не работает.

CODE
list_1 = [1, 10, -12, 4.89, 5, 11.2]
print(len(list_1))
print(max(list_1))
print(min(list_1))
print(sum(list_1))
RESULT
6
11.2
-12
20.09

Process finished with exit code 0

К списку можно применить функцию len(), которая вернет длину списка.
Функция max() вернет максимально число из списка.
Функция min() вернет минимальное число из списка.
Функция sum() вернет сумму чисел списка.

CODE
list_1 = ['hello', 'ivan', 'ivanov']
print(len(list_1))
print(max(list_1))
print(min(list_1))
RESULT
3
ivanov
hello

Process finished with exit code 0

Функции max() и min() применимы также к строкам, они возвратят строку максимальной и минимальной длины соответственно. Функция sum() к строкам не применима.

CODE
list_1 = [1, 10, -12, 4.89, 5, 11.2]
list_2 = ['b', 'c', 'wd', 'g', 'a', 'e']
print(sorted(list_1))
print(sorted(list_2), '\n')

print(list_1)
print(list_2, '\n')

print(sorted(list_1, reverse=True))
RESULT
[-12, 1, 4.89, 5, 10, 11.2]
['a', 'b', 'c', 'e', 'g', 'wd'] 

[1, 10, -12, 4.89, 5, 11.2]
['b', 'c', 'wd', 'g', 'a', 'e'] 

[11.2, 10, 5, 4.89, 1, -12]

Process finished with exit code 0

Списки можно отсортировать функцией sorted(). Числа сортируются по возрастанию, буквы по алфавитному порядку. Обратите внимание функция sorted() не изменяет список, то есть если вывести список, к которому была применена функция sorted() после, то список будет представлен в первоначальном виде. Необязательный параметр 'reverse=' в значении True развернет сортировку, по умолчанию установлено значение False.

CODE
list_1_1 = [1, 10, -12]
list_2_2 = ['b', 'c', 'wd']
list_3_3 = list_1_1 + list_2_2
list_4_4 = list_3_3 * 2
print(list_3_3)
print(list_4_4)
print(list_1_1 + [1, 2, 3])
RESULT
[1, 10, -12, 'b', 'c', 'wd']
[1, 10, -12, 'b', 'c', 'wd', 1, 10, -12, 'b', 'c', 'wd']
[1, 10, -12, 1, 2, 3]

Process finished with exit code 0

Списки, так же как и строки, можно конкатенировать и умножать.

CODE
list_11 = [1, 2, 3, [1, 2, ['hello', 'abc'], 3], 4, [2, 'cba']]
print(len(list_11))
print(list_11[3])
print(list_11[3][2])
print(list_11[3][2][1])
RESULT
6
[1, 2, ['hello', 'abc'], 3]
['hello', 'abc']
abc

Process finished with exit code 0

Списки могут быть вложенными, для вложенности достаточно открыть новый список внутри существующего, как это сделано на примере. На первом уровне вложенности один список равен одному элементу. Это видно, если применить функцию len() к данному списку. Для того чтобы обратится к элементу вложенного списка, нужно сначала обратиться к индексу данного списка и далее, если вложенность продолжается продолжать это действие пока не доберемся до нужного элемента. Вложенность списков не ограничена.

Основные методы списков
CODE
ex_list = [35, 16, 5, 123, 3]

a = ex_list.copy()
a.append(13)
print(a, '\n')

print(ex_list, '\n')

a.clear()
print(a, '\n')

print(ex_list.count(16))
print(ex_list.index(16), '\n')

ex_list.pop()
print(ex_list, '\n')

b = ex_list.pop()
print(ex_list)
print(b)
RESULT
[35, 16, 5, 123, 3, 13] 

[35, 16, 5, 123, 3] 

[] 

1
1 

[35, 16, 5, 123] 

[35, 16, 5]
123

Process finished with exit code 0

Метод .copy() создаст точную копию списка, работает точно также, как если бы мы взяли полный срез списка записью [:]. Ранее я упоминал о разнице между изменяемыми и неизменяемыми типами данных. Так вот копия списка будет занимать новую ячейку памяти.
Метод .append() добавит значение в конец списка.
Метод .clear() очистит список и оставит его пустым.
Метод .count() вернет количество вхождений элемента в список.
Метод .index() вернет индекс элемента, возвращает индекс первого соответствия.
Метод .pop() вырежет последнее значение списка. В метод .pop() можно передать индекс элемента, который нужно вырезать.

CODE
ex_list = [35, 16, 5, 123, 3]

ex_list.insert(0, 15)
print(ex_list, '\n')

ex_list.remove(123)
print(ex_list, '\n')

ex_list.reverse()
print(ex_list, '\n')

ex_list.sort()
print(ex_list, '\n')

ex_list.sort(reverse=True)
print(ex_list)
RESULT
[15, 35, 16, 5, 123, 3] 

[15, 35, 16, 5, 3] 

[3, 5, 16, 35, 15] 

[3, 5, 15, 16, 35] 

[35, 16, 15, 5, 3]

Process finished with exit code 0


Метод .insert() добавит значение по указанному индексу. Первый параметр - индекс, второй - значение.
Метод .remove() вырежет указанное значение из списка.
Метод .reverse() перевернет список задом наперед.
Метод .sort() отсортирует список по возрастанию. Если передать параметр reverse в значении True список отсортируется в обратном порядке.

Методы списков Описание
.copy() Создает копию списка
.append() Добавляет элемент в конец списка
.clear() Очищает список и оставляет его пустым
.count() Возвращает количество вхождений элемента в список
.index() Возвращает индекс указанного элемента
.pop() Вырезает последний элемент списка
.insert() Добавляет элемент по указанному индексу
.remove() Вырезает указанный элемент из списка
.reverse() Переворачивает список задом наперед
.sort() Сортирует список по возрастанию, параметр reverse по умолчанию в значении False
if/else/elif

С помощью конструкции if/else/elif можно проверять программу на соответствие каким-либо условиям. Сразу посмотрим на примере.

CODE
digit = int(input('Введите цифру '))

if digit % 2 == 0:
    print('Вы ввели четную цифру')
else:
    print('Вы ввели нечетную цифру')
RESULT
Введите цифру 120
Вы ввели четную цифру

Process finished with exit code 0

Напишем простую программу, которая будет проверять является ли число четным или нет. Переменная digit запрашивает число у пользователя. Далее в условии if мы делаем проверку числа на четность, сделать это можно проверкой по остатку от деления. И если условие выполняется, возвратим сообщение 'Вы ввели четную цифру', в противном случае выведем 'Вы ввели нечетную цифру'. В else всегда попадают любые другие условия, несоответствующие главному if.

CODE
digit = int(input('Введите цифру '))

if digit < 10:
    print('Ваше число меньше 10')
elif 10 <= digit <= 100:
    print('Ваше число в промежутке от 10 до 100')
else:
    print('Ваше число больше 100')
RESULT
Введите цифру 55
Ваше число в промежутке от 10 до 100

Process finished with exit code 0

elif, буквально можно перевести как - иначе если (else if). Внутри elif прописываются дополнительные условия, if всегда стоит вначале else всегда стоит в конце, elif, если он нужен, всегда стоит между ними. Количество elif не ограничено.

CODE
digit = int(input('Введите цифру '))

if digit < 10:
    if digit > 5:
        print('Ваше число в промежутке от 5 до 10')
    else:
        print('Ваше число в промежутке от 0 до 5')
elif 10 <= digit <= 100:
    print('Ваше число в промежутке от 10 до 100')
else:
    print('Ваше число больше 100')
    if digit > 500:
        print('Я бы сказал сильно больше 100')
        if digit > 1000:
            print('...')
RESULT 1             
Введите цифру 1200
Ваше число больше 100
Я бы сказал сильно больше 100
...

Process finished with exit code 0
RESULT 2
Введите цифру 2
Ваше число в промежутке от 0 до 5

Process finished with exit code 0

Условный оператор может иметь вложенность, причем вложенность неограниченна. Условия всегда проверяются сверху вниз, и как только какое-то условие выполняется мы погружаемся на более глубокий уровень вложенности, пока не доберемся до условия, которое максимально подходит для созданной ситуации. Каждый новый, более глубокий уровень вложенности, должен начинаться с отступа.

CODE
a = int(input('Введите число '))

res = print('четное') if a % 2 == 0 else print('нечетное')

b = int(input('Введите 3 '))

ex_list = [1, 2, b if b == 3 else print('Я же просил'), 4, 5]
print(ex_list)
RESULT 1
Введите число 15
нечетное
Введите 3 4
Я же просил
[1, 2, None, 4, 5]

Process finished with exit code 0
RESULT 2
Введите число 12
четное
Введите 3 3
[1, 2, 3, 4, 5]

Process finished with exit code 0

Существует более лаконичный способ записи условного оператора, он называется - тернарный условный оператор. Тернарный условный оператор записывается в сроку, действие выполняемое при соблюдении первого условия пишется перед ним, действие выполняемое при нарушении условия пишется в конце конструкции, а сами условия по центру конструкции. Тернарный условный оператор поддерживает только условия if else, elif использовать внутри тернарного условного оператора не получится.

CODE
a = int(input('Введите число '))

res = (print('<10') if a < 10 else print('>10')) if a % 2 == 0 else print('нечетное')

b = int(input('Введите 3 '))

ex_list = [1, 2, b if b == 3 else print('Я же просил'), 4, 5]
print(ex_list)
RESULT
Введите число 6
<10
Введите 3 3
[1, 2, 3, 4, 5]

Process finished with exit code 0

А вот вложенность на тернарный условный оператор распространяется. Работает она точно также, сначала проверяем условие первого уровня и если есть углубление, то погружаемся на уровень ниже при соблюдении условия.

Цикл while

В прошлом разделе мы написали несколько программ, проверяющих число на какое-нибудь условие. Но после одной итерации программа останавливалась, и для проверки нового числа приходилось ее перезапускать. Циклы помогут нам это изменить.

CODE
n = 0
even_list = []
not_even_list = []

while n < 5:
    a = int(input('Введите цифру: '))
    if a % 2 == 0:
        even_list.append(a)
    else:
        not_even_list.append(a)
    n += 1

mes = input('Напишите Четное/Нечетное ')
if mes.lower() == 'четное':
    print(even_list)
elif mes.lower() == 'нечетное':
    print(not_even_list)
else:
    print('Неизвестная команда')
RESULT 1
Введите цифру: 2
Введите цифру: 13
Введите цифру: 25
Введите цифру: 14
Введите цифру: 10
Напишите Четное/Нечетное Четное
[2, 14, 10]

Process finished with exit code 0
RESULT 2
Введите цифру: 15
Введите цифру: 23
Введите цифру: 8
Введите цифру: 12
Введите цифру: 6
Напишите Четное/Нечетное НЕЧЕТНОЕ
[15, 23]

Process finished with exit code 0

Посмотрим пример. While буквально означает 'пока', т.е 'пока какое-то условие соблюдается действие внутри цикла продолжается'. Создадим 2 списка even_list - для четных чисел, not_even_list - для нечетных, а также создадим переменную n равную нулю. В условии цикла скажем, что он будет повторяться пока n меньше 5 и далее в конце каждого цикла будем увеличивать n на 1(запись n += 1 равносильна записи n = n + 1). Внутри цикла каждый раз создаем переменную 'a', значение которой запрашиваем у пользователя и с помощью остатка от деления проверяем является ли число четным или нет, и в зависимости от проверки заносим его в нужный список. После того как условие перестало выполняться, цикл заканчивается и программа продолжает выполняться. Ниже будем запрашивать у пользователя какой список он хочет увидеть и далее с помощью конструкции if/elif/else выведем пользователю тот список, который ему нужен. Обратите внимание я применил к этой переменной метод .lower(), чтобы независимо от регистра букв слово воспринималось правильно.

CODE
n = 0
even_list = []
not_even_list = []

while n < 5:
    a = int(input('Введите цифру: '))
    if a % 2 == 0:
        even_list.append(a)
    else:
        not_even_list.append(a)
    n += 1
    if n == 4:
        break
    elif n == 2:
        continue

    print('Отработал')

else:
    print('n больше четырех')

mes = input('Напишите Четное/Нечетное ')
if mes.lower() == 'четное':
    print(even_list)
elif mes.lower() == 'нечетное':
    print(not_even_list)
else:
    print('Неизвестная команда')
RESULT
Введите цифру: 13
Отработал
Введите цифру: 24
Введите цифру: 32
Отработал
Введите цифру: 14
Напишите Четное/Нечетное break / continue
Неизвестная команда

Process finished with exit code 0

У цикла while есть три оператора: break, continue и else. break прерывает цикл и полностью останавливает его работу, в данном примере напишем условие - как только n станет равное четырем цикл следует прервать, как видно на примере цикл отработал четыре раза, а не пять. continue пропустит все действия цикла расположенные ниже этого оператора и вернет исполнение программы в начало цикла, на примере напишем условие в конце каждой итерации цикла выводить слово 'отработало' и, как видно, в том месте где continue выполняется слово 'отработало' не выводится. else, все действия принадлежащие этому оператору будут исполнены, когда цикл штатно прервется. Почему в данном примере else не сработал? Потому что цикл был прерван оператором break и поскольку данный else относится к циклу, а break прерывает работу всего цикла, условие не выполнилось. Прерывание по break считается не штатным. Если удалить условие break, то фраза 'n больше четырех' вывелась бы на экран после пятой итерации.

Функции range() и enumerate()

While не единственный цикл в python, но перед переходом к следующему следует разобрать функцию range.

CODE
print(range(5))
print(list(range(5)))
print(list(range(-5)))
print(list(range(1, 6)))
print(list(range(1, 8, 2)))
print(list(range(-5, -1)))
RESULT
range(0, 5)
[0, 1, 2, 3, 4]
[]
[1, 2, 3, 4, 5]
[1, 3, 5, 7]
[-5, -4, -3, -2]

Process finished with exit code 0

Функция range() генерирует арифметическую последовательность. Для преобразования последовательности к списку обернем range() в функцию list(). Если просто распечатать range() через print() мы увидим первую и последнюю точку последовательности, как видно на примере. А еще из этого примера хорошо видно, что если в range передать одну цифру, то эта цифра является конечной точкой последовательности и если первая точка не задана, то последовательность начнется с нуля. Отсюда становится понятно, что если передана первая цифра, то она и будет являться начальной точкой. Третья цифра - шаг последовательности. Начальная точка всегда должна быть меньше конечной.

CODE
ex_list = ['a', 'b', 'c', 'e', 'f', 'g']
print(list(enumerate(ex_list)))
RESULT
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'e'), (4, 'f'), (5, 'g')]

Process finished with exit code 0

Функция enumerate() возвратит кортеж. enumerate() принимает два значения, первое точка отсчета, второе объект итератор, об этом позже. Если передано одно значение, то отсчет по умолчанию начнется с 0.

Цикл for

Цикл for используется для перебора итерируемых объектов, что такое итерируемые объекты мы разберем подробнее чуть позже.

CODE
ex_list = list(range(5))
new_list = []

for x in ex_list:
    print(x)

for x_2 in ex_list:
    new_list.append(x_2 * 2)
print(new_list)

ex = 0
for n in range(5):
    ex += n
print(ex)
RESULT
0
1
2
3
4
[0, 2, 4, 6, 8]
10

Process finished with exit code 0

Цикл for последовательно проходит по итерируемому объекту и каждую итерацию переменная, переданная в цикл for, поочередно принимает значение итерируемого объекта. Создадим список ex_list с помощью функции range и пробежимся по нему в цикле, поочередно выводя переменную x, видим каждое значение списка выведенное на новую строку. По аналогии пробежимся по этому списку, и каждое значение будем умножать на 2, добавляя получившееся значение в новый список new_list. И в последнем цикле for посчитаем сумму арифметической прогрессии от 0 до 4. Функция range часто и тесно взаимодействует с циклом for. И отсюда можно сделать вывод, что цикл for возвращает итерируемый объект.

CODE
for i in range(1, 5):
    for j in range(1, 3):
        print(f'i = {i}, j = {j}', end='\n')
    print()
RESULT
i = 1, j = 1
i = 1, j = 2

i = 2, j = 1
i = 2, j = 2

i = 3, j = 1
i = 3, j = 2

i = 4, j = 1
i = 4, j = 2


Process finished with exit code 0

Цикл for может быть вложенным. На примере представлен частый пример, демонстрирующий работу вложенного цикла.

Схематично это можно изобразить так. Мы заходим в цикл внутри переменной i и поочередно перебираем для нее все значения переменной j вложенного цикла. Повторяем это действие пока остались элементы итерируемого объекта главного цикла. Второй print добавлен для лишнего пробела.

Итерируемые объекты

Можно сказать, итерируемый объект - объект, который способен возвращать по одному элементу. Какие типы данных в python являются итерируемыми объектами? Списки, строки, словари, кортежи, а также файлы. Ко всем этим типам данных можно применить функции iter() и next(). Функция iter() нужна для преобразования типа данных к объекту под названием итератор, а уже по итератору мы можем поочередно проходиться функцией next(). Эти два метода и используются, так сказать, 'под капотом' цикла for.

CODE
a = list(range(4))
a = iter(a)
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
RESULT
Traceback (most recent call last):
  File "/home/tsarkoilya/pylibrary/test/test.py", line 7, in <module>
    print(next(a))
StopIteration
0
1
2
3

Process finished with exit code 1

Вот так устроено цикл for для списка числе от 0 до 3. Функция iter() превращает список в объект итератор, а функция next() поочередно перебирает каждый элемент этого итератора. Как только, с помощью функции next() все элементы пройдены, мы получаем ошибку StopIteration, благодаря обработке этой ошибки цикл for заканчивается, но об обработке ошибок мы поговорим позже.

Всегда для освежения информации удобнее возвращаться к структурированной информации, поэтому оберну прошлые 5 тем в таблицу.

Имя Описание
if/elif/else Условный оператор, с помощью него можно создавать логические ветвления, можно также назвать его - оператор выбора
while Повторение действия, пока выполняется условие. Для манипуляций с while используются операторы break, continue и else
for Повторение действия, пока не закончились элементы итератора
range() Генерирует арифметическую последовательность
enumerate() Возвращает кортеж вида (start, типа int(), элемент объекта, поддерживающий метод next())
iter() и next() iter() превратит последовательность в объект итератор, next() поочередно переберет элементы итератора
Словари

Словари (dict) неотъемлемая часть реализации python. Важно понимать, что в основе словарей лежат хэш-таблицы, это означает, что словари работают очень быстро. Идея хэш-таблиц заключается в том, что каждому значению присвоен свой ключ, и когда мы хотим обратиться к какому-нибудь значению словаря мы делаем это по ключу, а не перебираем всю последовательность пока не дойдем до нужного элемента, отсюда и очень высокая скорость работы.

CODE
ex_dict = {'t_list': 'list', 't_dict': 'dict', 't_set': 'set', 't_tuple': 'tuple'}

print(ex_dict)
print(ex_dict['t_list'], '\n')

ex_dict_2 = dict(one=1, two=2, three=3)
print(ex_dict_2, '\n')

ex_list = [[1, 'one'], [2, 'two'], [3, 'three']]
print(dict(ex_list), '\n')

ex_dict['new_key'] = 'new_value'
print(ex_dict)
ex_dict['new_key'] = 'new_new_value'
print(ex_dict)
RESULT
{'t_list': 'list', 't_dict': 'dict', 't_set': 'set', 't_tuple': 'tuple'}
list 

{'one': 1, 'two': 2, 'three': 3} 

{1: 'one', 2: 'two', 3: 'three'} 

{'t_list': 'list', 't_dict': 'dict', 't_set': 'set', 't_tuple': 'tuple', 'new_key': 'new_value'}
{'t_list': 'list', 't_dict': 'dict', 't_set': 'set', 't_tuple': 'tuple', 'new_key': 'new_new_value'}

Process finished with exit code 0

Словарь можно создать двумя способами, первый и самый часто используемый - фигурные скобки, внутри которых через двоеточие помещаются пары ключ:значение. Второй - функция dict(). Если мы создаем словарь с нуля мы делаем это через фигурные скобки, если хотим преобразовать уже существующий тип данных к словарю используем функцию dict(). Обращаться мы можем только к ключу, для того чтобы это сделать поместим ключ нужного значения в квадратные скобки после названия словаря. Для добавления новой пары в словарь достаточно написать название словаря, после него в квадратных скобках задать название нового ключа и через равно задать значение этого ключа. Значение ключа можно переписать просто задав для ключа новое значение. Ключом словаря может являться любой неизменяемый тип данных, значением может быть как изменяемые так и неизменяемые типы данных.

Методы словарей
CODE
lst = ['a', 'b', 'c', 'd']
dct = dict.fromkeys(lst)
print(dct)
dct = dict.fromkeys(lst, 'letter')
print(dct)
dct_2 = dict.copy(dct)
dct.clear()
print(dct_2, dct)
print(dct_2.get('a'), '\n')

dct_2.setdefault('a')
print(dct_2)
dct_2.setdefault('e')
print(dct_2, '\n')

print(dct_2.pop('e'))
print(dct_2, '\n')

print(dct_2.popitem(), '\n')

print(dct_2.keys(), '\n')

print(dct_2.values(), '\n')

print(dct_2.items())
RESULT
{'a': None, 'b': None, 'c': None, 'd': None}
{'a': 'letter', 'b': 'letter', 'c': 'letter', 'd': 'letter'}
{'a': 'letter', 'b': 'letter', 'c': 'letter', 'd': 'letter'} {}
letter 

{'a': 'letter', 'b': 'letter', 'c': 'letter', 'd': 'letter'}
{'a': 'letter', 'b': 'letter', 'c': 'letter', 'd': 'letter', 'e': None} 

None
{'a': 'letter', 'b': 'letter', 'c': 'letter', 'd': 'letter'} 

('d', 'letter') 

dict_keys(['a', 'b', 'c']) 

dict_values(['letter', 'letter', 'letter']) 

dict_items([('a', 'letter'), ('b', 'letter'), ('c', 'letter')])

Process finished with exit code 0

Метод .fromkeys() преобразует значения коллекций к ключам словаря, если второй параметр не указан, то значениями ключей будет None, если указан, то ключом будет являться указанное значение.
Метод .copy() создаст копию словаря.
Метод .clear() очистит словарь.
Метод .setdefault(). Внутрь .setdefault() передается ключ, если таковой уже есть в словаре, то ничего не изменится, если такового нет, то создастся новый ключ, либо со значением заданным по умолчанию, либо с тем, что мы передадим в качестве второго аргумента.
Метод .pop() вырежет из словаря пару ключ:значение, ключ пары передается в качестве аргумента. Если попытаться удалить несуществующий ключ, то получим ошибку KeyError, если хотим ее избежать, то в качестве второго параметра можно передать значение, которое будет выводиться вместо ошибки, в случае отсутствия переданного ключа.
Метод .popitem() вырежет из словаря последнюю пару ключ:значение.
Метод .keys() вернет список ключей словаря.
Метод .values() вернет список значений словаря.
Метод .items() вернет пары ключ:значения словаря и преобразует эти пары к кортежам.

CODE
dct_1 = {1: 'one', 2: 'two', 3: 'three'}

for key, value in dct_1.items():
    print(key, value)

dct_2 = {4: 'four', 5: 'five', 6: 'six'}

dict_3 = {**dct_1, **dct_2}
print(dict_3)
RESULT
1 one
2 two
3 three
{1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six'}

Process finished with exit code 0

Например вот так в цикле for можно вывести пары ключ:значения словаря, по аналогии можно вывести только ключи или только значения. Объединить 2 словаря можно с помощью оператора **.

Метод Описание
.fromkeys() Преобразовывает значения коллекции к ключам словаря, второй аргумент - значения этих ключей
.copy() Создает копию словаря
.clear() Очищает словарь
.setdefault() Создает новый ключ, если таковой не имеется
.pop() Вырезает из словаря пару ключ:значение
.popitem() Вырезает из словаря последнюю пару ключ:значение
.keys() Возвращает список ключей
.values() Возвращает список значений
.items() Возвращает кортежи пар ключ:значений
Множества

Множества (set) - это неупорядоченная коллекция уникальных элементов. Уникальных означает, что все повторяющиеся элементы множества будут удалены. Очень удобно использовать множества, когда требуется убрать дубли из данных. Элементами множества могут быть только неизменяемые типы данных.

CODE
ex_set = {1, 'two', 3, 4, False}

print(ex_set)
print(type(ex_set), '\n')

lst = [1, 3, 2, 2, 3, 1, 5, 6]
print(set(lst), '\n')

ex_str = 'abrakadabra'
print(set(ex_str), '\n')

ex_set.add(10)
print(ex_set, '\n')

ex_set.update(range(5, 7))
print(ex_set, '\n')

ex_set.discard(6)
print(ex_set, '\n')

ex_set.remove('two')
print(ex_set)
RESULT
{False, 1, 3, 4, 'two'}
<class 'set'> 

{1, 2, 3, 5, 6} 

{'b', 'd', 'r', 'a', 'k'} 

{False, 1, 3, 4, 'two', 10} 

{False, 1, 3, 4, 'two', 5, 6, 10} 

{False, 1, 3, 4, 'two', 5, 10} 

{False, 1, 3, 4, 5, 10}

Process finished with exit code 0

Для создания множества используются фигурные скобки, но в отличие от словаря в множество передаются значения через запятую. Обратите внимание при выводе множества на печать значения расположены в хаотичном порядке, это и есть признак неупорядоченной коллекции. Как и было сказано выше главное преимущество множеств - удаление дублей, на примере списка из чисел и строки это хорошо видно.

Методы множеств

Метод .add() добавит новое значение в множество.
Метод .update() добавит в множество значения итерируемого объекта, который содержит несколько элементов.
Методы .discard() и .remove() выполняют одно и тоже, удаляют переданное значение из множества. Единственное отличие, если передать в эти методы элемент, которого нет в списке, то .remove() вернет ошибку, а .discard() нет.

Метод Описание
.add Добавляет значение в множество.
.update() Добавляет значения итерируемого объекта в множество.
.remove() и .discard() Удаляет переданное значение из множества.

Операции над множествами.

Оператор & вернет результат пересечения множеств, то есть покажет элементы повторяющиеся в первом и втором множестве. Оператор | вернет объединение двух множеств, исключив все дубли. Оператор - оставит уникальные элементы того множества, из которого производилось вычитание. Оператор ^ вернет все уникальные элементы множеств, к которым был применен этот оператор.

CODE
ex_set_a = {1, 2, 3, 4}
ex_set_b = {3, 4, 5, 6}
print(ex_set_a & ex_set_b)
print(ex_set_a - ex_set_b)
print(ex_set_b - ex_set_a)
print(ex_set_a | ex_set_b)
print(ex_set_a ^ ex_set_b)
RESULT
{3, 4}
{1, 2}
{5, 6}
{1, 2, 3, 4, 5, 6}
{1, 2, 5, 6}

Process finished with exit code 0

Так это выглядит на практике.

Кортежи

Последний базовый тип данных, который осталось рассмотреть - кортежи. Кортежи - упорядоченная неизменяемая коллекция элементов.

CODE
ex_tuple = 1, 2, 3, 'hello', [1, 2, 3]

print(ex_tuple)
print(type(ex_tuple), '\n')

ex_tuple_2 = (1, 2, 3)
print(type(ex_tuple_2), '\n')

a = 5
print(type(a))
a = (5,)
print(type(a), '\n')

x, y = (1, 2)
print(x)
print(y, '\n')

b, c = 3, 4
print(b)
print(c)
RESULT
(1, 2, 3, 'hello', [1, 2, 3])
<class 'tuple'> 

<class 'tuple'> 

<class 'int'>
<class 'tuple'> 

1
2 

3
4

Process finished with exit code 0

Создать кортеж можно несколькими способами. Первый - передать в переменную данные через запятую, необязательно цифры, можно передать также строку или, например, список. Круглые скобки свидетельствуют о том, что перед нами кортеж. Второй - передать данные сразу в круглых скобках, результат будет таким же. Если передать в переменную один элемент, то он не будет являться кортежем. Чтобы единственный элемент являлся кортежем нужно предать его с запятой после элемента. Кортеж можно распаковать в переменные, работает это как обычное множественное присвоение в python.

Функция len(), выбор элемента по индексу, а также работа со срезами работает так же как и в списках, за тем исключением, что если мы возьмем полный срез при помощи - [:], то копия среза не создастся. Зачем нужны кортежи, если есть списки, которые выполняют все то же самое и даже больше? Первое - кортежи неизменяемый тип данных и по этому эих можно использовать например в качестве ключа словаря или в том куске программы где нельзя допустить изменение данных. Второе - кортеж занимает меньше памяти чем аналогичный список, в реальных проектах память важный показатель, поэтому там где можно использовать кортеж предпочтительней это сделать. Методы .count() и .index() также применимы к кортежам и работают аналогично.

Генераторы списков, множеств и словарей

Очень полезной и частоиспользуемой конструкцией являются генераторы. Начнем с генераторов списков. Фактически генераторы заменяют конструкцию for, но работают значительно быстрее.

CODE
n = 5

ex_list = []
for x in range(n):
    ex_list.append(x ** 2)

print(ex_list)

ex_comp_list = [x ** 2 for x in range(n)]
print(ex_comp_list)
RESULT
[0, 1, 4, 9, 16]
[0, 1, 4, 9, 16]

Process finished with exit code 0

Найдем все квадраты чисел от 0 до 4. Можно сделать это, как и говорилось выше, в цикле, а можно сгенерировать этот же список в одну строку. Всегда, когда можно воспользоваться генератором списков, а не циклом for, лучше предпочесть первое. Генератору могут включать в себя тернарный оператор либо какие-нибудь условия.

CODE
even_list = [x for x in range(11) if x % 2 == 0]
print(even_list)

sec_list = [x for x in range(17) if x % 2 != 0 and x > 5]
print(sec_list)

new_list = even_list + sec_list
print(new_list)

ev_or_not = ['even' if x % 2 == 0 else 'not_even' for x in new_list]
print(ev_or_not)
RESULT
[0, 2, 4, 6, 8, 10]
[7, 9, 11, 13, 15]
[0, 2, 4, 6, 8, 10, 7, 9, 11, 13, 15]
['even', 'even', 'even', 'even', 'even', 'even', 'not_even', 'not_even', 'not_even', 'not_even', 'not_even']

Process finished with exit code 0

Например можно сгенерировать список четных чисел и список нечетных чисел, которые больше пяти. Далее сконкатенировать эти списки и с помощью тернарного оператора вывести четным иил нечетным является каждое число из этого нового списка.

CODE
list_a = [[x for x in range(1, 5)] for y in range(1, 4)]
print(list_a)

list_b = [[x ** 2 for x in lst_a] for lst_a in list_a]
print(list_b)
RESULT
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
[[1, 4, 9, 16], [1, 4, 9, 16], [1, 4, 9, 16]]

Process finished with exit code 0

Генераторы списков могут быть вложенными. Например, с помощью такого вложенного генератора списков можно сгенерировать матрицу чисел и далее возвести каждый элемент матрицы в квадрат.

CODE
ex_comp_set = {x ** 2 for x in range(6)}
print(ex_comp_set)
print(type(ex_comp_set))

ex_comp_dict = {x: x ** 2 for x in range(6)}
print(ex_comp_dict)
print(type(ex_comp_dict))
RESULT
{0, 1, 4, 9, 16, 25}
<class 'set'>
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
<class 'dict'>

Process finished with exit code 0

Также нам доступны генераторы словарей и множеств, используются такие генераторы гораздо реже, чем генераторы списков, но знать об этих конструкциях тоже нужно. Для того чтобы сгенерировать множество, поменяем квадратные скобки на фигурные, а чтобы вместо множества сгенерировать словарь в качестве генерируемой переменной передадим не одно значение, а два, через двоеточие.

Введение в функции

Можно сказать, что вступление в базовый синтаксис пройдено. Теперь преступим к телу каждой программы - функциям. Функции позволяют нам не повторяться, а записать фрагмент кода внутрь функции и в нужный момент вызывать ее.

CODE
def first_def():
    print('Первая функция')

first_def()
first_def()

def square(a, b):
    print(f'Площадь прямоугольника: {a * b}')

square(4, 20)
square(5, 8)
RESULT
Первая функция
Первая функция
Площадь прямоугольника: 80
Площадь прямоугольника: 40

Process finished with exit code 0

Определяется функция с помощью ключевого слова def. Для вызова функции достаточно написать ее имя внутри программы. Внутрь функции можно передать какие-нибудь переменные, которые потом будут использоваться внутри тела функции. Для использования этих переменных достаточно передать их при вызове функции. Задача на вычисление площади прямоугольника теперь выглядит гораздо компактнее.

CODE
x = int(input('a = '))
y = int(input('b = '))

def square_and_perimeter(a, b):
    print(f'Площадь прямоугольника: {a * b}')
    print(f'Периметр прямоугольника: {2 * a + 2 * b}')

square_and_perimeter(x, y)
RESULT
a = 5
b = 10
Площадь прямоугольника: 50
Периметр прямоугольника: 30

Process finished with exit code 0

Но ничего не мешает передать в функцию данные запрошенные у пользователя, обратите внимание названия переменных внутри определения функции и названия переменных передаваемых внутрь вызова функции могут быть разными.

CODE
x = int(input('a = '))
y = int(input('b = '))

def square_and_perimeter(a, b):
    print(f'Площадь прямоугольника: {a * b}')
    print(f'Периметр прямоугольника: {2 * a + 2 * b}')

res = square_and_perimeter(x, y)
print(res)
RESULT
a = 10
b = 5
Площадь прямоугольника: 50
Периметр прямоугольника: 30
None

Process finished with exit code 0
Problems: Current File 1 - (эта цифра  в pycharm число проблем в файле)
! Function'square_and_perimeter'doesn't return anything: 10 - (эта цифра в pycharm строка, на которой проблема обнаружена)

А что если сохранить результат работы функции в переменную и вывести на экран результат этой переменной. Мы получим None, почему так произошло? PyCharm подчеркивает 10 строку (в pycharm это будет волнистое подчериквание) и обработчик проблем сообщает нам, что функция ничего не возвращает. Дело в том, что любая функция имеет параметр return и если в этот оператор ничего не передать, то функция возвратит значение return по умолчанию, то есть - None.

CODE
x = int(input('a = '))
y = int(input('b = '))

def square_and_perimeter(a, b):
    return f'Площадь прямоугольника: {a * b} ' \
           f'\nПериметр прямоугольника: {2 * a + 2 * b}'

res = square_and_perimeter(x, y)
print(res)
RESULT
a = 10
b = 5
Площадь прямоугольника: 50 
Периметр прямоугольника: 30

Process finished with exit code 0

Теперь, познакомившись с оператором return, приведем программу к правильному виду.

Области видимости

Возможно вы обратили внимание, что к переменным созданным внутри функции не получится обратиться за пределами этой функции, такое поведение касается не только функций, но разберем это на их примере.

CODE
def some_def():
    # local
    a, b, c = 1, 2, 3
    return a, b, c

# global
res = some_def()
print(res)
print(a)
RESULT
Traceback (most recent call last):
  File "/home/tsarkoilya/pylibrary/test/test.py", line 9, in <module>
    print(a)
NameError: name 'a' is not defined
(1, 2, 3)

Process finished with exit code 1

Вот демонстрация того о чем я говорю. Переменная 'a' существует внутри функции, а за ее пределами при попытке обращения к ней мы получим ошибку, говорящую, что такая переменная не найдена. Переменные существующие внутри функции называются локальными, переменные существующие в основном теле программы называются глобальными.

CODE
a = 10
d = 5
e = 20

def some_def():
    # local
    d = 15
    print(id(d))
    a, b, c = 1, 2, 3
    return a, b, c, d, e

# global
res = some_def()
print(res)
print('a =', a)
print('d =', d)
print(id(d))
RESULT
139763137882864
(1, 2, 3, 15, 20)
a = 10
d = 5
139763137882544

Process finished with exit code 0

Нам ничего не мешает создать переменную с одним названием в глобальной области видимости и в локальной, правда PyCharm из-за этого на нас немного поругается. Переменная 'a' и 'd' демонстрируют нам, что для функций переменные расположенные в локальной области видимости приоритетней чем в глобальной, а переменная 'e' демонстрирует, что если в локальной области соответствующей переменной не обнаружено, то функция обратится к глобальной области видимости и возьмет ее оттуда. Изменение одноименных переменных внутри локальной области видимости никак не влияет на них в глобальной области, потому что id у таких переменных разные несмотря на одинаковые названия.

CODE
a = 10

def some_def():
    global a
    a = a ** 5

some_def()
print(a)
RESULT
100000

Process finished with exit code 0

Если мы все-таки хотим, чтобы изменения переменной в локальной области видимости повлияли на нее в глобальной области, то используем инструкцию global. Она буквально говорит, что переменные внутри этой инструкции должны интерпретироваться как глобальные.

CODE
a = 100

def some_def():
    a = 200

    def some_def_2():
        nonlocal a
        a = 50
        print(a)

    some_def_2()
    print(a)

some_def()
print(a)
RESULT
50
50
100

Process finished with exit code 0

Существует еще одна похожая инструкция - nonlocal, которая позволяет изменять переменные в ближайшей области видимости, за исключением глобальной. Отсюда возникает правило инструкции nonlocal - nonlocal используется только внутри вложенных функций. То есть внутри функции some_def_2 мы обращаемся к переменной 'a' определенной внутри функции some_def и изменяем ее, глобальную переменную 'a' мы при этом не затрагиваем.

LEGB правило используется интерпретатором для поиска имен. Поиск начинается с локальной области и в случае отсутствия имени в локальной области поиск продолжается на более широких уровнях.

Именованные аргументы. Передача произвольного числа аргументов
CODE                                                                                                           
def perimetr(a, b):                 | def perimetr(a, b):                 | def perimetr(a, b=5):
    return 2 * (a + b)              |     return 2 * (a + b)              |     return 2 * (a + b)
                                    |                                     |
                                    |                                     |
res = perimetr(2, 5)                | res = perimetr(b=2, a=5)            | res = perimetr(2)
print(res)                          | print(res)                          | print(res)
RESULT                                                                                                 
14                                  | 14                                  | 14
                                    |                                     |
Process finished with exit code 0   | Process finished with exit code 0   | Process finished with exit code 0

Аргументы мы можем передать классическим способом, как на первом примере, два аргумента внутри функции значит два значения мы передаем в вызов функции, в таком случае значения встают на места аргументов последовательно. Второй вариант, объявить аргументы в произвольном порядке. Третий вариант, объявить значение аргумента непосредственно в объявлении функции. Параметры, которые не имеют значения внутри объявления функции называются - фактические, в противном случае - формальные.

CODE  
def perimetr(a, b=5):
    return 2 * (a + b)

res = perimetr(2)
print(res)

def second_perimetr(a, b=5):
    return 2 * (a + b)

res = second_perimetr(2, 10)
print(res)
RESULT       
14
24

Process finished with exit code 0

Причем это не означает, что значение этого аргумента обязательно будет таким, это означает, что если такому аргументу не соответствует ни одно значение в вызове, то значение будет взято из объявления аргумента внутри функции. Думаю пример понятно иллюстрирует такое поведение.

Но не всегда мы можем точно знать какое количество аргументов потребуется передать в функцию. На эти случаи существуют два особых параметра - *args и **kwargs.

CODE  
def amount(*args):
    e_sum = 0
    for i in args:
        e_sum += i

    return e_sum

res = amount(1, 2, 3, 4, 5)
print(res)
res_2 = amount(5, 2, 3)
print(res_2)
RESULT       
15
10

Process finished with exit code 0

Параметр *args позволяет передать в функцию произвольное количество фактических параметров.

CODE  
def amount(**kwargs):
    ex_dict = {}
    for key, value in kwargs.items():
        ex_dict[key] = value

    return ex_dict

res = amount(a=1, b=2, c=3, d=4, e=5)
print(res)
res_2 = amount(a=5, b=2, c=3)
print(res_2)
RESULT 
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
{'a': 5, 'b': 2, 'c': 3}

Process finished with exit code 0

А параметр **kwargs позволяет передать в функцию произвольное количество формальных параметров. Причем параметр **kwargs является словарем, поэтому к параметру **kwargs применимы методы словарей.

CODE  
a, b, *c = [1, 2, 3, 4, 5]
print(a, b, c)
a, *b, c = [1, 2, 3, 4, 5]
print(a, b, c)
*a, b, c = [1, 2, 3, 4, 5]
print(a, b, c)
*a, b, c = [1, 2]
print(a, b, c)
RESULT 
1 2 [3, 4, 5]
1 [2, 3, 4] 5
[1, 2, 3] 4 5
[] 1 2

Process finished with exit code 0

Благодаря параметру * также можно распаковать коллекции произвольного количества параметров.

Декораторы

Декораторы - тема, которая поначалу может показаться достаточно запутанной и непонятной, но ее не стоит пугаться. Постараемся разобраться в ней максимально подробно. И первое о чем нужно поговорить - замыкания.

CODE                                
(lesson_20.1.0.py)                 | (lesson_20.1.0.py)                 | (lesson_20.1.1.py)
                                   |                                    |
def your_name(name):               | def your_name(name):               | def your_name(name):
    def say_hello():               |     def say_hello():               |     def say_hello():
        print(f'Hello {name}')     |         print(f'Hello {name}')     |         print(f'Hello {name}')
                                   |                                    |
    say_hello()                    |     return say_hello               |     return say_hello
                                   |                                    |
                                   |                                    |
your_name('Ilya')                  | your_name('Ilya')                  | ex = your_name('Ilya')
                                   |                                    | ex()
RESULT                     
Hello Ilya                         |                                    | Hello Ilya
                                   |                                    |
Process finished with exit code 0  | Process finished with exit code 0  | Process finished with exit code 0

Когда в прошлом разделе мы разбирали инструкцию nonlocal мы писали вложенные функции и для работы такой конструкции мы исполняли вложенную функцию внутри основной. Теперь посмотрим на эти две программы. В обоих случаях мы передаем в главную функцию переменную name и во вложенной функции используем ее внутри print(). В первой программе мы поступили так, как поступали ранее - исполнили функцию в теле главной функции и после вызвали эту главную функцию, передав в нее параметр. Результат ожидаемый и логичный - Hello Ilya. Теперь посмотрим на вторую программу. Вместо исполнения внутренней функции мы ее возвращаем, а значит внутренняя функция нигде не исполняется. И, действительно, если выполнить главную функцию как в первом примере мы не увидим никакого результата, а вот если присвоить результат ее исполнения какой-нибудь переменной и исполнить программу через эту переменную, то результат мы увидим. Почему так происходит? Не напрасно перед переходом к теме декораторов мы разобрались с областями видимости. Откуда внутренняя функция во втором случае взяла переменную 'name', ведь переменная 'name' является локальной и после исполнения функции your_name() она должна была исчезнуть. Дело тут в том, что пока исполнение главной функции присвоено какой-то глобальной переменной цепочка функций, закрепленная за этой переменной, продолжает существовать.

Представим это так. Переменная 'ex' держит возвращаемую функцию say_hello() за нее цепляется функция your_name() и вся эта цепочка цепляется за наше глобальное окружение. Таким образом, существование в глобальной области видимости глобальной переменной, которой присвоено исполнение возвращаемой вложенной функции, позволяет пользоваться переменными внешних окружений вложенных функций. Такое поведение и называется замыканием.

CODE  
def your_name(name):
    def say_hello():
        print(f'Hello {name}')

    return say_hello

ex = your_name('Ilya')
second_ex = your_name('Jora')
ex()
second_ex()
RESULT
Hello Ilya
Hello Jora

Process finished with exit code 0

Причем каждый новый вызов такой функции создаст свое локальное окружение, внутри которого будут содержаться свои локальные переменные.

CODE  
def decorator_def(func):
    def wrapper():
        print('Старт')
        func()
        print('Конец')

    return wrapper

def some_def():
    print('Середина')

res = decorator_def(some_def)
res()
RESULT
Старт
Середина
Конец

Process finished with exit code 0

Разобравшись с замыканиями можно перейти к написанию декораторов. Напишем функцию decorator_def(), которая в качестве параметра будет принимать какую-то функцию. Далее создадим вложенную функцию-обертку выполняющую некоторые действия, и помимо этих действий она будет выполнять функцию переданную в качестве параметра основной функции. После вернем функцию-обертку. Мы создали обычное замыкание, с которыми мы уже знакомы. Ниже объявим некоторую функцию также выполняющую какое-то действие. После всего объявим в глобальной области видимости переменную которая будет исполнять нашу функцию декоратор и в качестве параметра будет принимать нашу стороннюю функцию, тем самым сцепив наши функции между собой. Исполнив теперь нашу глобальную переменную мы увидим, что результат сторонней функции исполнился внутри функции-обертки.

CODE  
def decorator_def(func):
    def wrapper():
        print('Старт')
        func()
        print('Конец')

    return wrapper

def some_def():
    print('Середина')

some_def = decorator_def(some_def)
some_def()
RESULT
Старт
Середина
Конец

Process finished with exit code 0

А теперь заменим название 'res' на название нашей сторонней функции, таким образом мы поменяли поведение нашей сторонней функции.

CODE  
def decorator_def(func):
    def wrapper(param):
        print('Старт')
        func(param)
        print('Конец')

    return wrapper

def some_def(param):
    print(param)

some_def = decorator_def(some_def)
some_def('Середина')
RESULT
Старт
Середина
Конец

Process finished with exit code 0

Теперь ничего не мешает передать нам параметр в нашу стороннюю функцию, но ранее мы познакомились с параметрами *args и **kwargs, поэтому для того, чтобы постоянно внутри функции wrapper() не менять параметры в зависимости от параметров сторонней функции удобней передать в нее параметры *args и **kwargs. Также наша функция ничего не возвращает это поведение тоже следует исправить.

CODE  
def decorator_def(func):
    def wrapper(*args, **kwargs):
        print('Старт')
        res = func(*args, **kwargs)
        print('Конец')
        return res

    return wrapper

def some_def(param, param_2):
    print('Середина')
    return f"{param} {param_2}"

some_def = decorator_def(some_def)
result = some_def('...', 2)
print(result)
RESULT
Старт
Середина
Конец
... 2

Process finished with exit code 0

Сделаем это примерно так. Функция some_def() теперь независимо от того что и с каким количеством параметров мы внутри нее напишем, будет обернута в функцию decorator_def() и помимо исполнения тела функции some_def() будут выполняться действия тела функции decorator_def().

CODE  
def decorator_def(func):
    def wrapper(*args, **kwargs):
        print('Старт')
        res = func(*args, **kwargs)
        print('Конец')
        return res

    return wrapper

def some_def(param, param_2):
    print('Середина')
    return f"{param} {param_2}"

def some_def_2(param, param_2):
    print('Середина 2')
    return f"{param} {param_2}"

some_def = decorator_def(some_def)
some_def_2 = decorator_def(some_def_2)
result = some_def('...', 2)
result_2 = some_def(2, '...')
print(result, result_2)
RESULT
Старт
Середина
Конец
Старт
Середина
Конец
... 2 2 ...

Process finished with exit code 0

Причем теперь мы можем создавать произвольное количество сторонних функций и каждую оборачивать в нашу функцию decorator_def(). Оборачивание в функцию-декоратор всегда выглядит одинаково: 'название сторонней функции = функция декоратор (название сторонней функции)'.

CODE  
def decorator_def(func):
    def wrapper(*args, **kwargs):
        print('Старт')
        res = func(*args, **kwargs)
        print('Конец')
        return res

    return wrapper

@decorator_def
def some_def(param, param_2):
    print('Середина')
    return f"{param} {param_2}"

@decorator_def
def some_def_2(param, param_2):
    print('Середина 2')
    return f"{param} {param_2}"

result = some_def('...', 2)
result_2 = some_def(2, '...')
print(result, result_2)
RESULT
Старт
Середина
Конец
Старт
Середина
Конец
... 2 2 ...

Process finished with exit code 0

Такую запись заменяет значок @(название функции декоратора) написанный перед функцией, которую нужно декорировать. Вот теперь можно сказать, что мы написали полноценный декоратор и декорировали им наши функции. Также можно внутри одной программы создать несколько декораторов и обернуть какую-нибудь функцию в несколько декораторов. Теперь для закрепления разберем еще несколько более реальных примеров, в которых обертывание функции в декоратор будет иметь хоть какой-то смысл.

CODE                                                                    
def get_list(func):                              | def get_list(func):
    def result(*args, **kwargs):                 |     def result(*args, **kwargs):
        res_list = []                            |         res_list = []
        for item in func(*args, **kwargs):       |         for item in func(*args, **kwargs):
            res_list.append(item)                |             res_list.append(item)
        res = sorted(res_list)                   |         res = sorted(res_list)
        return res                               |         return res
                                                 |
    return result                                |     return result
                                                 |
                                                 |
@get_list                                        | @get_list
def show_list(s):                                | def show_list(s):
    return s                                     |     return s
                                                 |
                                                 |
my_s = map(int, input().split())                 | my_s = map(int, input().split())
print(show_list(my_s))                           | print(*show_list(my_s))
RESULT                                                              
1 -23 414 43 2 11 -99                            | 1 -23 414 43 2 11 -99
[-99, -23, 1, 2, 11, 43, 414]                    | -99 -23 1 2 11 43 414
                                                 |
Process finished with exit code 0                | Process finished with exit code 0

Например напишем декоратор, который сортирует строку переданных чисел по возрастанию. Функцию map() мы подробнее разберем позже, но говоря кратко, благодаря такой записи как на примере, можно передавать в переменную произвольное количество параметров через пробел. Внутри самого декоратора создадим функцию-обертку, внутри которой создадим пустой список, и внутрь этого списка будем добавлять все переменные переданные пользователем, сортировать этот список и возвращать его. Еще один момент, не относящийся к теме декораторов, с помощью * перед списком можно вывести его без квадратных скобок по краям, при этом данная коллекция все еще будет являться списком. Такой декоратор можно объявить один раз и использовать его к любой последовательности целых чисел.

CODE  
import time

def how_long(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print(f'Программа выполнена за {end_time - start_time}')
        return res

    return wrapper

@how_long
def main_def(a):
    res = 0
    for i in range(a + 1):
        res += i

    return res

@how_long
def some_def(lst):
    new_lst = []
    for i in lst:
        new_lst.append(i ** 2)

    return new_lst

result = main_def(10000 * 10000)
print(result)
result_2 = some_def([i for i in range(15)])
print(result_2)
RESULT 
Программа выполнена за 4.397265195846558
5000000050000000
Программа выполнена за 9.775161743164062e-06
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196]

Process finished with exit code 0

Или, к примеру, напишем вот такой декоратор, который будет засекать время исполнения программы. Снова классический скелет функции-декоратора, внутри которого с помощью модуля time засекаем начало и конец выполнения программы, а далее возвращаем пользователю разницу этих значений. Такой декоратор уже выглядит достаточно полезным.

CODE  
def decorator_func_with_arg(tag='h1'):
    def decorator_func(func):
        def wrapper(ex_str):
            res = func(f"<{tag}> {ex_str} </{tag}>")
            return res

        return wrapper

    return decorator_func

@decorator_func_with_arg(tag='p')
def main_def(ex):
    return ex

result = main_def('Python')
print(result)
RESULT 
<p> Python </p>

Process finished with exit code 0

В декораторы можно передавать параметры. Для этого поверх основной функции-декоратора напишем еще одну функцию, которая будет принимать какой-то параметр со значением по умолчанию. Такая запись позволяет создавать декораторы, в которые можно передать параметр и использовать его внутри функции-обертки. Например, в нашем случае сымитируем простановку html тэгов вокруг строки, по умолчанию выберем тэг h1, и добавим возможность выбирать тэг самостоятельно в качестве параметра декоратора.

И остался наверное последний момент в теме декораторов функций, который стоило бы обсудить.

CODE                                                                                   
def decorator_func_with_arg(tag='h1'):                   | def decorator_func_with_arg(tag='h1'):
    def decorator_func(func):                            |     def decorator_func(func):
        def wrapper(ex_str):                             |         def wrapper(ex_str):
            res = func(f"<{tag}> {ex_str} </{tag}>")     |             res = func(f"<{tag}> {ex_str} </{tag}>")
            return res                                   |             return res
                                                         |
        return wrapper                                   |         return wrapper
                                                         |
    return decorator_func                                |     return decorator_func
                                                         |
                                                         |
# @decorator_func_with_arg(tag='p')                      | @decorator_func_with_arg(tag='p')
def main_def(ex):                                        | def main_def(ex):
    """Обертывает строку в html тэг"""                   |     """Обертывает строку в html тэг"""
    return ex                                            |     return ex
                                                         |
                                                         |
print(main_def.__name__)                                 | print(main_def.__name__)
print(main_def.__doc__)                                  | print(main_def.__doc__)
RESULT                                                                                
main_def                                                 | wrapper
Обертывает строку в html тэг                             | None
                                                         |
Process finished with exit code 0                        | Process finished with exit code 0 

Хорошим тоном в написании декораторов считается сохранение системного названия декорируемой функции и ее описание, методы __name__ и __doc__ позволяют узнать эти параметры, что это за методы мы подробнее обсудим, когда перейдем к теме ООП, сейчас это не так важно. Так вот, когда функция декорирована ее имя и описание меняется на имя и описание функции-обертки декоратора, такого поведения, конечно, хочется избежать.

CODE                                                                                             
from functools import wraps                              | def decorator_func_with_arg(tag='h1'):
                                                         |     def decorator_func(func):
                                                         |         def wrapper(ex_str):
def decorator_func_with_arg(tag='h1'):                   |             res = func(f"<{tag}> {ex_str} </{tag}>")
    def decorator_func(func):                            |             return res
        @wraps(func)                                     |
        def wrapper(ex_str):                             |         wrapper.__name__ = func.__name__
            res = func(f"<{tag}> {ex_str} </{tag}>")     |         wrapper.__doc__ = func.__doc__
            return res                                   |
                                                         |         return wrapper
        return wrapper                                   |
                                                         |      return decorator_func
    return decorator_func                                |
                                                         |
                                                         | @decorator_func_with_arg(tag='p')
@decorator_func_with_arg(tag='p')                        | def main_def(ex):
def main_def(ex):                                        |     """Обертывает строку в html тэг"""
    """Обертывает строку в html тэг"""                   |     return ex
    return ex                                            |
                                                         |
                                                         | print(main_def.__name__)
print(main_def.__name__)                                 | print(main_def.__doc__)
print(main_def.__doc__)                                  |
RESULT                                                                                       
main_def                                                 | main_def                             
Обертывает строку в html тэг                             | Обертывает строку в html тэг         
                                                         |                                      
Process finished with exit code 0                        | Process finished with exit code 0   

Можно изменить это поведение в ручную, просто заменим имя и описание функции-обертки на имя и описание декорируемой функции, как это сделано в программе справа, но согласитесь, в каждом декораторе каждый раз писать две лишние строки выглядит как что-то, что уже должно быть как-то компактно автоматизировано. И действительно, во встроенном в python модуле functools есть декоратор @wraps, которым мы декорируем функцию-обертку и этот декоратор заменит две этих строки, сделав то же самое.

На этом можно закончить знакомство с декораторами, но, конечно, мы столкнемся с ними еще неоднократно.

lambda функции

lambda функции, другое название - анонимные функции, это так скажем одноразовые функции.

CODE  
def some_def(a, b, c):
    return a * b * c

res = some_def(5, 10, 15)
print(res)

result = lambda a, b, c: a * b * c

print(result(5, 10, 15), '\n')

def some_def_2(x):
    return x ** x

res_2 = some_def_2(5)
print(res_2)

result_2 = lambda x: x ** x

print(result_2(5))
RESULT  
750
750 

3125
3125

Process finished with exit code 0

Синтаксис lambda функций можно описать как:
lambda аргументы: действия, совершаемые над этими аргументами
lambda функции могут принимать один, несколько аргументов или ни одного. Использование анонимных функций подразумевает, что вы нигде ее не объявляете, у нее нет названия, а результат ее исполнения можно передать в какую-то переменную. lambda функции лаконичны и компактны, но внутри lambda функций можно выполнить только одно действие над аргументами, поэтому анонимными функциями можно заменить только простые однодейственные функции.

Рекурсивные функции

Функция в python может вызывать сама себя. Правда такой вызов не бесконечен, он ограничен примерно 1000 итераций. Поэтому, чтобы рекурсивная функция срабатывала у нее должен быть выход.

CODE  
def rec_def(n):
    if n <= 3:
        print('До')
        rec_def(n + 1)
        print('После')

rec_def(1)
RESULT  
До
До
До
После
После
После

Process finished with exit code 0

Посмотрим на такую рекурсивную функцию. Точкой выхода из нее является прекращение выполнения условия if. А как устроена сама рекурсия.

Работают рекурсии именно так. Как только мы доходим до рекурсивного вызова функции мы сразу, не доходя до второго print() возвращаемся к проверке условия, при этом программа запоминает на каком моменте это произошло. И как только условие не выполняется мы повторно проходимся по функциям, в которых это условие было выполнено и довыполняем эти условия. Поэтому сначала мы 3 раза видим 'До', когда n становится равно 4 условие не выполняется и мы идем к началу по этим трем успешно выполненным функциям, поэтому после 3 'До' мы видим 3 'После'.

CODE                                                         
def fact(n):                            | def fib(n):
    if n <= 0:                          |     if n == 1:
        return 1                        |         return 0
    else:                               |     if n == 2:
        return n * fact(n - 1)          |         return 1
                                        |     return fib(n - 1) + fib(n - 2)
                                        |
res = fact(5)                           |
print(res)                              | res = fib(5)
                                        | print(res)
RESULT                                                      
120                                     | 3
                                        |
Process finished with exit code 0       | Process finished with exit code 0      

Классическим примером для понимания работы рекурсии являются функции вычисления факториала числа и нахождения числа фибоначчи по его порядковому номеру последовательности. В данных примерах мы уменьшаем входное значение и выходом из программы будет являться условие для наименьшего значения n.

yield

Ключевое слово yield превратит функцию в генератор, то есть элементы такой функции можно будет обойти только один раз.

CODE     
def gen_func(lst):
    for x in lst:
        yield x

res = gen_func([1, 2, 3, 4, 5, 6])
print(res)
print(next(res))
print(next(res))
print(next(res))
print(next(res))
print(next(res))
print(next(res))
print(next(res))
RESULT   
Traceback (most recent call last):
  File "/home/tsarkoilya/kavo/PycharmProjects/forsite/python_learn/base_syntax/lesson_23.py", line 14, in <module>
    print(next(res))
StopIteration
<generator object gen_func at 0x7efd83719f20>
1
2
3
4
5
6

Process finished with exit code 1

Например, напишем функцию gen_func(), внутри которой будем поочередно обходить элементы списка. Список из шести элементов можно обойти шесть раз на седьмой мы получим ошибку StopIteration. Зачем нужны такие функции. Все достаточно очевидно - они занимают меньше памяти. В функциях-генераторах нам не требуется хранить весь список чисел, мы берем первое значение и на этом моменте выполнение функции замораживается и запоминает, на каком моменте мы остановились, пока мы не решим обратиться к следующему элементу списка.

Функция map()

Функция map() на самом деле возвращает итератор, по которому можно пробегаться функцией next(). Первым аргументов функция map() принимает функцию, которая последовательно применится к каждому значению второго аргумента, переданного в эту функцию.

CODE     
lst = [x for x in range(6)]
res = map(str, lst)
print(next(res))

for i in res:
    print(i)
    print(type(i))
RESULT   
0
1
<class 'str'>
2
<class 'str'>
3
<class 'str'>
4
<class 'str'>
5
<class 'str'>

Process finished with exit code 0

Например преобразуем каждый элемент списка к строке.

CODE     
x = list(map(int, input().split()))

res = map(abs, x)
for i in res:
    print(i, end=' ')
RESULT  
-10 5 6 7 -18 -23 -8 42
10 5 6 7 18 23 8 42
Process finished with exit code 0

Или такой пример, с помощью функции map() сначала получим произвольное количество переменных преобразованных к списку. А далее опять же с помощью функции map() возьмем модуль каждого числа из этого списка. Модуль в python можно взять с помощью функции abs().

Функция filter()

Функция filter() выводит только те значения итерируемого объекта, к которым применение функции вернет значение True.

CODE     
my_str = map(str, input().split())
res = filter(lambda x: len(x) < 5, my_str)
for i in res:
    print(i)
RESULT  
python c++ ruby html css
c++
ruby
html
css

Process finished with exit code 0

Например вот так благодаря функции filter() можно отсортировать список строк и оставить только те чья длина меньше пяти.

Функция zip()

последняя популярная функция для работы с итерируемыми объектами - функция zip().

CODE 
lst_1 = [x for x in range(7)]
lst_2 = [x for x in range(4)]

res = zip(lst_1, lst_2)
for i in res:
    print(i)
RESULT  
(0, 0)
(1, 1)
(2, 2)
(3, 3)

Process finished with exit code 0

Функция zip() объединяет несколько итерируемых последовательностей и преобразует их к кортежам. Заканчивается работы функции zip(), когда достигнут последний элемент самой короткой последовательности.

CODE
lst_1 = [x for x in range(7)]
lst_2 = [x for x in range(4)]
lst_3 = 'abcdefgh'

res = zip(lst_1, lst_2, lst_3)
for i, i1, i2 in res:
    print(i, i1, i2)
RESULT  
0 0 a
1 1 b
2 2 c
3 3 d

Process finished with exit code 0

Последовательностей может быть больше двух, а также кортежи сразу можно распаковать с помощью множественного присвоения.

Функция isinstance()

Ранее тип данных мы проверяли только благодаря функции type(), но есть еще одна функция, которая позволяет совершать такую проверку. Первым параметром в функцию isinstance() мы передаем тип данных, а вторым объект, тип данных которого мы хотим проверить.

CODE
my_lst = [1, 4, 'hello', 5.5, [100, 200], '50']

def lst_sum():
    res = 0
    for i in my_lst:
        if isinstance(i, int):
            res += i

    return res

result = lst_sum()
print(result)
RESULT  
5

Process finished with exit code 0

Напишем функцию, внутри которой будем поочередно пробегаться по всем элементам списка, с помощью функции isinstance() проверять является элемент целым числом, и если да, будем плюсовать его к переменной res и возвращать ее. Возвращает функция isinstance(), как несложно догадаться, True или False.

Функции all() и any()

Функции all() и any() возвращают либо True, либо False. Функция all() возвратит True, если все значения коллекции, к которой применена функция all(), возвращают True, в противном случае будет возвращен False. Функция any() работает также, но для возвращения True достаточно чтобы хотя бы один элемент коллекции приравнивался к True.

CODE
my_lst = [1, 4, 'hello', 5.5, [100, 200], '50']

print(all(my_lst))
print(any(my_lst))

my_lst_2 = [1, 0, 'hello', 5.5, [], '50']

print(all(my_lst_2))
print(any(my_lst_2))
RESULT  
True
True
False
True

Process finished with exit code 0

Обратите внимание, любая пустая коллекция, например, пустой список или пустой словарь, а также ноль, приравниваются к False.

Импорт и установка модулей

Когда мы говорили про декораторы в одном из примеров мы импортировали декоратор wraps из модуля functools. Модули, или библиотеки, в python это любой программный файл, содержащий какие-нибудь будь функции, классы или попросту переменные. Библиотек в python огромное множество, некоторые из них входят в стандартный набор библиотек и устанавливаются вместе с установкой python, а некоторые нужно устанавливать дополнительно.

Импортировать модули можно несколькими способами.
Первый import (название модуля)
Второй import (название модуля) as (свое название модуля)
Третий from (название модуля) import (список конкретных объектов этого модуля)
Чтобы обратиться к методу какого-то модуля импортированного целиком нужно написать название модуля и через точку необходимый метод этого модуля, а в тех случаях когда мы импортируем конкретные методы достаточно написать их без названия самого модуля. Функция dir() позволит посмотреть все методы модуля, но удобнее зайти внутрь этой библиотеки и почитать о ее методах подробнее. Для этого достаточно нажать на название библиотеки с зажатой клавишей ctrl.

Устанавливать библиотеки, не входящие в стандартный набор в windows можно командой pip install (название модуля), в linux это делается командой sudo apt-get install (название модуля). Я использую linux, но пишу в виртуальной среде поэтому также могу пользоваться командой pip. Можно устанавливать сразу несколько модулей, перечислив их через запятую. А вот импортировать модули в программу через запятую не рекомендуется, для каждого нового модуля новая команда import.

CODE
(lesson_29.1.py)                                 | (lesson_29for_import.py)                                                 
                                                 |
from lesson_29for_import import how_long         | import time
                                                 |
                                                 |
@how_long                                        | def how_long(func):
def main(lst):                                   |     def wrapper(*args, **kwargs):
    new_lst = []                                 |         start_time = time.time()
    for i in lst:                                |         res = func(*args, **kwargs)
        new_lst.append(i ** 2)                   |         end_time = time.time()
                                                 |         print(f'Программа выполнена за {end_time - start_time}')
    return sum(new_lst)                          |         return res
                                                 |
                                                 |     return wrapper
res = main([x for x in range(1000 * 10000)])     |
print(res)                                       |
RESULT (lesson_29.1.py)     
Программа выполнена за 3.604973793029785
333333283333335000000

Process finished with exit code 0

Конечно, ничего не мешает пользоваться не чужими библиотеками, а написать свой модуль и импортировать объекты из него. Например, возьмем декоратор для засекания выполнения времени программы, который мы писали ранее поместим его в новый файл и импортируем этот декоратор в нашу основную программу. Обратите внимание, модуль time не нужно импортировать для обеих программ, достаточно того, что он импортирован в той программе откуда мы забираем функцию, внутри которой использован данный модуль.

Работа с файлами

Часто придется работать со сторонними файлами, например форматов txt или json. В python для работы с файлами используется функция open().

CODE
ex = open(file="files/example.txt", encoding='utf-8')

print(ex.read())
ex.seek(0)
print(ex.read(10))
print(ex.tell())
ex.seek(0)
print(ex.readline())

ex.close()
RESULT
Красивое лучше уродливого.
Явное лучше неявного.
Простое лучше сложного.
Сложное лучше запутанного.
Развернутое лучше вложенного.
Разреженное лучше плотного.
Читаемость имеет значение.
Особые случаи не настолько особые, чтобы нарушать правила.
При этом практичность важнее безупречности.
Ошибки не должны замалчиваться.
Если не замалчиваются явно.
Встретив двусмысленность, отбрось искушение угадать.
Должен существовать один - и, желательно, только один – очевидный способ сделать что-то.
Хотя этот способ поначалу может быть и не очевиден, если вы не голландец.
Сейчас лучше, чем никогда.
Хотя никогда часто лучше, чем *прямо* сейчас.
Если реализацию сложно объяснить – идея точно плоха.
Если реализацию легко объяснить – возможно, идея хороша.
Пространства имен – отличная штука! Будем использовать их чаще!
Красивое л
19
Красивое лучше уродливого.


Process finished with exit code 0

Например, создадим в папке с нашим проектом еще одну папку с названием 'files', а внутри этой папки файл 'example.txt'. В файл 'example.txt' добавим дзен python - это свод правил из 20 пунктов, который можно увидеть по команде import this, я нашел русские эквиваленты этих правил и добавил их в 'example.txt'. Для того, чтобы прочитать этот файл воспользуемся функцией open(), первым параметром в кавычках напишем путь до нашего файла, пути могут быть абсолютными, т.е. начинающиеся с диска и проходящие все папки до файла и относительные, т.е. пути относительно того файла, в котором мы работаем. В данном примере воспользуемся относительным путем. Вторым параметром явно укажем кодировку, параметр encoding, используйте кодировку 'UTF-8', если в вашем файле присутствует кириллица. По умолчанию функция open() открывает файл на чтение.
Метод .read() выведет все содержимое файла и после того как файл будет прочтен, так скажем, каретка, останется на последнем символе, для того чтобы вернуть каретку в начало воспользуемся методом .seek() где в качестве параметра укажем к какому символу нужно вернуть каретку.
В метод .read() тоже можно передать параметр, это число будет означать сколько символов нужно прочитать, а метод .tell() расскажет на каком байте, именно байте, мы сейчас находимся, один кириллический символ равняется двум байтам.
Метод .readline() возвращает верхнюю строку, можно пройтись по файлу в цикле for и благодаря этому методу поочередно возвращать каждую строку.
Последний метод - .close() каждый файл нужно обязательно закрывать, особенно если он был открыт на запись, делается это для того, чтобы избежать возможную потерю данных.

CODE
with open(file="files/example.txt", encoding="utf-8") as ex:
    print(ex.read())
    ex.seek(0)
    print(ex.read(10))
    print(ex.tell())
    ex.seek(0)
    print(ex.readline())
RESULT
Красивое лучше уродливого.
Явное лучше неявного.
Простое лучше сложного.
Сложное лучше запутанного.
Развернутое лучше вложенного.
Разреженное лучше плотного.
Читаемость имеет значение.
Особые случаи не настолько особые, чтобы нарушать правила.
При этом практичность важнее безупречности.
Ошибки не должны замалчиваться.
Если не замалчиваются явно.
Встретив двусмысленность, отбрось искушение угадать.
Должен существовать один - и, желательно, только один – очевидный способ сделать что-то.
Хотя этот способ поначалу может быть и не очевиден, если вы не голландец.
Сейчас лучше, чем никогда.
Хотя никогда часто лучше, чем *прямо* сейчас.
Если реализацию сложно объяснить – идея точно плоха.
Если реализацию легко объяснить – возможно, идея хороша.
Пространства имен – отличная штука! Будем использовать их чаще!
Красивое л
19
Красивое лучше уродливого.


Process finished with exit code 0

Закрытие файла действительно важный момент. И для того, чтобы избежать забывание метода .close() существует менеджер контекста with и предпочтительней работать с файлами через него. Он автоматически закроет файл после всех манипуляций над ним.

CODE
with open(file="files/example.txt", encoding="utf-8", mode='w') as ex:
    ex.write('Еще строка')

with open(file="files/example.txt", encoding="utf-8", mode='r') as ex:
    print(ex.read())
RESULT
Еще строка

Process finished with exit code 0

У функции open() существует еще один параметр - mode. По умолчанию установлен в значение 'r', т.е. файл откроется на чтение, для открытия файла на запись заменим 'r' на 'w'. Если файл открыт на запись мы не сможем его прочитать, для прочтения нужно открыть файл еще раз с параметром mode в значении 'r'. Метод .write() запишет в файл переданную в этот метод строку. Обратите внимание, python дзен я не удалял из нашего файла, удалило его как раз открытие файла на запись, каждый раз, когда мы открываем файл на запись его прошлое содержимое удаляется.

CODE
with open(file="files/example.txt", encoding="utf-8", mode='w') as ex:
    ex.write('Еще строка')
    ex.write('И еще')
    ex.write('Еще одна')

with open(file="files/example.txt", encoding="utf-8", mode='r') as ex:
    print(ex.read())
RESULT
Еще строкаИ ещеЕще одна

Process finished with exit code 0

Нам необязательно создавать новый файл вручную для работы с ним. Мы можем создать новый файл прям внутри функции open, в параметре file напишем несуществующее название файла в режиме записи. Файл 'new.txt' с новым содержимым создан по указанному пути.

CODE
with open(file="files/new.txt", encoding="utf-8", mode='a') as ex:
    ex.write('Строка 4')

with open(file="files/new.txt", encoding="utf-8", mode='a+') as ex:
    ex.write('Строка 5')
    ex.seek(0)
    print(ex.read())
RESULT
Еще строкаИ ещеЕще однаСтрока 4Строка 5Строка 4Строка 5

Process finished with exit code 0

Каждый раз удалять содержимое это, конечно, не всегда удобно. Режим дозаписи называется 'a', если удалять содержимое не требуется, используйте этот режим. Но в режиме 'a' все еще нельзя читать содержимое. Режим и для дозаписи и для чтения называется 'a+', правда по умолчанию открытие файла в таком режиме установит каретку в конец файла, поэтому для прочтения всего содержимого следует сначала установить каретку в начало уже знакомым методом .seek().

Метод Описание
.read() Выводит все содержимое файла
.readline() Выводит содержимое файла построчно
.readlines() Выводит список всех строк файла
.next() Выводит следующую строку файла
.seek() Устанавливает каретку в переданную позицию
.tell() Показывает нынешнюю позицию каретки
.write() Запишет строку в файл
.writelines() Запишет последовательность строк в файл
.close() Закроет файл
Режим Описание
r Режим чтения, режим по умолчанию
w Режим записи
a Режим дозаписи
r+ Режим записи и чтения, каретка в начале
w+ Режим записи и чтения, каретка в начале, перезаписывает содержимое
a+ Режим дозаписи и чтения, каретка в конце
b Добавление b второй буквой к каждому режиму сделает то же, что и без нее, только в бинарном режиме
Что дальше?

На этом знакомство с базовым синтаксисом посчитаем законченным. Что делать дальше? Переходить к ОПП, но перед этим можно порешать задачки, например, на CodeWars или другом похожем сайте, там огромное количество задач распределенных по уровням сложности, опираясь на этот материал вы можете самостоятельно попробовать решать задачи, чем хорошо прокачаете свой навык владения типами данных и их методами. Даже если у вас не получится решить задачку самостоятельно вы всегда можете посмотреть решения других пользователей, из которых сможете подчеркнуть для себя интересные решения и подходы. Меня в свое время затянул CodeWars не на один вечер и время проведенное на этом сайте не было потрачено для меня впустую. Важно не забывать, что обучение программированию это всегда практика, вы можете провести десятки часов за просмотром видео уроков и прочтением статей и, конечно, без этого никуда, но самую большую пользу для своего скилла вы получите именно с практикой.

Для отправки комментария необходимо авторизоваться



Комментарии

Здесь пока ничего нет...