Python за ~~час~~ сколько-то.

EgorBron,13 min read

Для начала я хочу пожелать всем удачи как с экзаменом, так и подготовкой к нему.


Всего будет 16 секций с вопросами (не будут рассмотрены 11, 13, 14, 17, 21-24 вопросы). В каждой секции -- краткое объяснение, варианты ответа на вопрос, а затем длинная теория с кодом. К слову, код можно будет запустить прямо в браузере!

Важно: документ в процессе написания! Большая часть теории не готова.


Пара моментов по написанию кода:

  1. в Python есть понятие "блока кода", то есть когда операции принадлежат какой-то бόльшей конструкции (типа if, for, def); и важно, чтобы новый блок кода выделялся отступом в 4 пробела. Проще говоря: видишь двоеточие -- добавляй четыре пробела перед каждой строкой, которая принадлежит конструкции с двоеточием:
    for a in b:
        ...
    if a:
        if q:
            ...
        else:
            ...
    def z():
        ...
    class E:
        def __init__(self):
            ... # и так по нарастающей
  2. 2.

Поехали.

Содержимое
1. Введение: переменные, константы, типы данных, операторы, выражения, ввод-вывод.
2. Библиотека math.
3. Исключения (exceptions) в Python.
4. [условные] ветвления и циклы.
5. Строки: базовые операции, функции и методы
6. Списки: базовые операции, функции и методы. Генераторы списков. Вложенные списки.
7. Кортежи: базовые операции.
8. Множества: базовые операции. Генераторы множеств.
9. Словари: способы создания, базовые операции и методы.
10. Функции. Передача параметров в функцию. Область видимости.
11. не рассматривается Доп. возможности при работе с функциями.
12. Функциональные возможности: лямбда-функции.
13. не рассматривается, см. 18 вопрос Файлы.
14. не рассматривается Хранение структурированных данных в файлах.
15. Модули и пакеты.
16. Запуск программ. Установка сторонних модулей.
17. не рассматривается Регулярные выражения.
18. Работа с файлами и папками.
19. Работа с датой и временем.
20. Объектно-ориентированное программирование (ООП).
21. не рассматривается Библиотека Tkinter.
22. не рассматривается Работа с файлами .xls (таблицы Excel), .pdf (документы PDF) и .doc (документы Word).
23. не рассматривается Формат JSON.
24. не рассматривается Формат CSV.

Введение: переменные, константы, типы данных, операторы, выражения, ввод-вывод.

Коротко: переменные в Python создаются с помощью записи название переменной = значение. С помощью переменных можно хранить какое-либо значение. В Python нет констант -- все переменные можно изменить, но можно сделать "константу", записав название переменной большими буквами. В Python есть несколько типов данных, среди которых целочисленный (int), вещественный (float) и строковый (str). К переменным и типам данных можно применять операторы, например, математические -- сложение, вычитание и т. д. Несколько операторов образуют выражение. Выражение будет вычислено во время выполнения программы. Выражение можно присвоить переменной. С помощью print можно вывести какие-либо данные, а с помощью input -- ввести.

Другие варианты ответа

А теперь теория.

нажми сюда

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

Определяется переменная следующим образом:

название = значение
 
# примеры
num = 1
text = "текст"
pi = 3.14
truth = True

При этом:

  • можно будет изменить значение переменной на любое другое
  • нельзя объявить переменную без значения
  • можно присвоить переменной выражение (об этом далее)

Как таковых констант в Python нет, да и вряд ли они появятся. Любую переменную можно изменить. Вообще любую.

Однако бывает так, что константное значение сохранить надо. Для этого в Python принято такое соглашение: писать название "константы" заглавными буквами. Так что если видите переменную, название которой написано "капсом" -- лучше не изменять её.

PI = 3.14
ERROR = "ошибка"
# и т. д.

Тип данных задаёт набор операций, который можно провести над данными. Например, числовой тип данных позволит нам, скажем, провести сложение.

Основные (или же "примитивные") типы данных в Python:

типобозначениечто может хранить
целочисленныйintцелые числа (1, 42, 1024)
вещественныйfloatдробные числа (0.5, 3.14, 999.0
булев (логический)boolдве логических константы (True, False)
строковыйstrлюбой текст (aaa, lorem ipsum)
комплексныйcomplexкомплексные числа с мнимой единицей (1j, 4j, 100j)

(текущий тип данных у переменной или значения можно узнать с помощью функции type)

Между этими типами можно проводить конверсию или же приведение к другому типу. Проще говоря -- можно сделать из текста число, и наоборот:

a = 42
print(type(a)) # int
 
b = "58"
print(type(b)) # str
 
b = int(b)
c = a + b
print(c) # 100
 
d = "num: " + str(c)
print(d) # num: 100

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

# a -- операнд, b -- тоже операнд, а + -- оператор
a + b

Рассмотрим самые базовые из операторов:

оператороперациятип данныхпример
+сложениечисла1 + 2
-вычитаниечисла3 - 1
*умножениечисла2 * 2
/деление с дробной частьючисла10 / 5
%получение остаткачисла9 % 4
//деление без остаткачисла7 // 2
**возведение в степеньчисла10**2
+конкатенация (сложение строк)строки"a" + 'b' + "c"
-повторение строкистроки"abc" * 10
%форматирование строкистроки"text: %s" % text
\\экранирование (вставка спец. символа)строки"\n\t tab and new line\\oh"
f, {}f-строка (форматирование)строкиf'text: {text}'
()вызов функциифункцииfunc()
.получение свойства или вызов методалюбойsomething.prop

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

a = f"equation: {PI * radius + (round(h) / 2)} \t = \t {10 + var}"

Ну и, наконец, самое самое. Ввод и вывод.

Вывести какое-либо значение можно с помощью функции print:

a = 28
 
print("hola!")
print(42)
print(a)
print(print()) # 0_o

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

print("the text: ", end = "")
print(text)

Если же нам нужно ввести данные, то тут поможет функция input:

i = input()

Нужно "подсказать" пользователю, а что ему нужно ввести? Передаём строку в input:

e = input("Введите Е: ")

По умолчанию input возвращает строковый тип данных. Если нужно число, то надо сделать приведение:

a = int(input("Введите A: "))

Библиотека math.

Коротко: в библиотеке math есть куча математических функций, например, квадратный корень, синус, модуль числа, и так далее. Чтобы добавить эту библиотеку, надо написать import math, а затем обращаться к её функциям через точку. К примеру, для вычисления квадратного корня можно написать math.sqrt(), а как аргумент передать число.

Другие варианты ответа

А теперь теория.

нажми сюда

math -- одна из множества стандартных библиотек. Её предназначение -- дать погромистам базовые математические функции.

Для использования math необходимо добавить (импортировать) её:

import math

И далее обращаться к её функциям через точку:

math.pi

Список самых используемых функций из math:

функцияпример # выводчто делает
pow(num, num)math.pow(2, 10) # 1024возводит число в степень (аналог. 2**10)
sqrt(num)math.sqrt(4) # 2извлекает квадратный корень (аналог. 4**0.5)
floor(float)math.floor(2.5) # 2округление до ближайшего наименьшего целого (до "пола")
ceil(float)math.ceil(2.5) # 3округление до ближайшего наибольшего целого (до "потолка")
cos(num), sin, tan, acos, asin, atanmath.sin(math.pi) # 1.0тригонометрические функции: косинус, синус и т. д.
log(num, num)math.log(9, 3) # 2логарифм числа по основанию
abs(num)abs(-10) # 10модуль числа (функция не из math)
round(float)round(3.3) # 3банковское округление (функция не из math)
pimath.piчисло пи (π\pi)
emath.eчисло Эйлера (ee)

Исключения (exceptions) в Python.

от себя: а накой чорт они идут третьим вопросом?

Коротко: исключениями в Python называются ошибки. Когда возникает такая ошибка (ещё говорят "поднялось исключение"), то программа прекращает своё выполнение (если эта ошибка не была обработана). Примеры исключений: деление на ноль, использование несуществующего индекса у списка, использование несуществующей переменной. Исключение можно обработать с помощью конструкции try/except/finally.

Другие варианты ответа

А теперь теория.

нажми сюда

В процессе работы программы на Python может возникнуть куча ошибок. Они делятся на два типа: ошибки в синтаксисе (т. е. сам код написан криво/неверно) и ошибки выполнения. Последние как раз и называются исключениями (a. k. a. exceptions).

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

  1. Не удалось преобразовать тип данных
    a = "один два три"
    b = int(a) # !!! TypeError
  2. Деление на ноль
    a = 0
    b = 5 / a # !!! ZeroDivisionError
  3. Переменная не объявлена
    b = a + 2 # !!! NameError
  4. Индекс за пределами списка
    a = [1, 2, 3]
    b = a[10] # !!! IndexError
  5. Несуществующее свойство
    a = "один два три"
    b = a.escaped   # !!! AttributeError
    c = a.escaped() # !!! AttributeError
  6. Несуществующий ключ у словаря
    a = {"one": 1, "two": 2}
    b = a["three"] # !!! KeyError
  7. Слишком глубокая рекурсия
    def a():
      a() # работает бесконечно
    b = a() # !!! RecursionError
  8. ...и так далее...

Исключений можно избежать двумя способами.

Первый -- не писать такой код, что может поднять исключение.

Но бывают такие ситуации, когда "угадать" входные данные будет трудно или вовсе невозможно.

Для этого есть второй способ -- конструкция try/except/finally.

Всё просто:

  1. помещаем в блок try код, в котором есть риск возникновения исключения
    try:
       a = get_a()
       b = 10 / a
  2. в блоках except (которых может быть сколько угодно) обрабатываем ошибки
    except ZeroDivisionError: # обработает деление на ноль
       print("Деление на ноль")
    except ArithmeticError as e: # обработает любую ошибку этого типа + выведет её
       print("Ошибка вычислений: ", e)
    except: # обработает все остальные ошибки
       print("Неизвестная ошибка")
  3. в блоке finally обрабатываем код, который выполнится после try и всех exceptов, даже если ошибок не было
    finally:
       print("Завершаем работу")
       free_a(a)

Полный код:

try:
   a = get_a()
   b = 10 / a
except ZeroDivisionError: # обработает деление на ноль
    print("Деление на ноль")
except ArithmeticError as e: # обработает любую ошибку этого типа + выведет её
    print("Ошибка вычислений: ", e)
except: # обработает все остальные ошибки
    print("Неизвестная ошибка")
finally:
   print("Завершаем работу")
   free_a(a)

Также можно самому поднять исключение с помощью оператора raise:

raise Exception("Моя ошибка!")

[условные] ветвления и циклы.

Коротко: в Python можно делать ветвление, чтобы какой-то код выполнялся при определённых условиях. Скажем, если число больше другого числа. Для этого используется конструкция if/else. В блоке if пишется условие, затем в теле блока пишутся операторы, которые должны быть выполнены при истинности условия, а в теле else пишутся операторы, которые выполнятся, если условие ложно. Можно добавить блоки elif, которые позволят проверить несколько условий. Блоки else и elif необязательны.

#TBD

Объектно-ориентированное программирование (ООП).

Коротко: объектно-ориентированное программирование -- это такой подход к написанию программы, в котором всё строится вокруг классов и объектов. Класс в данном случае -- некоторое подобие чертежа для какой-то сущности -- например, машины, животного, фигуры и так далее. Каждый класс обладает какими либо свойствами (=переменными) и методами (=функциями). Объект же -- конкретный пример класса, например, в случае с классом "машина" это может быть какая-либо конкретная машина. Класс определяется ключевым словом class, Для определения свойства класса нужно задать его в конструкторе. У классов и объектов можно определить свойства и методы. Свойства -- это какие-либо переменные, привязанные к объекту. Например, длина фигуры или цвет машины. А методы -- это функции, которые выполняют действия над этим объектом. Например, поиск площади или исполнение "поездки". Свойства определяются в функции-конструкторе (def __init__(self):), а методы -- в блоке класса. Классы проявляют несколько общих для них свойств: возможность наследоваться (то есть перенимать свойства и методы от "родительского" класса), инкапсулировать какие-то свойства/методы (то есть намеренно скрывать их от "чужого" кода, чтобы этот класс не сломался) и выполнять методы полиморфно (то есть одинаковый метод у двух классов может использоваться одинаково).

А теперь теория.

нажми сюда

Итак. ООП ака Объектно-Ориентированное Программирование пришло на смену функциональному и структурному. Зачем, а главное нафига? Ну, есть несколько причин:

  1. рано или поздно появится нужда описывать объекты из реального мира -- с конкретными свойствами и/или действиями
    • к примеру -- машина -- для простоты у неё есть марка, цвет и несколько действий
  2. было бы неплохо определять какой-то базовый объект, а от него делать подобие иерархии
    • та же машина может быть "потомком" транспортного средства, забирая от него действие передвижения; а может быть обобщением для всех машин, которые могут различаться, например, устройством двигателя
  3. и чтобы ещё действия от "родителя" объекта можно было выполнять и над потомком
    • машина же может перемещаться, как и любое другое транспортное средство
  4. и чтобы можно было скрыть какие-то особенные для этого объекта свойства от других объектов
    • для всё той же машины: мы не сможем заглянуть и покопаться в двигателе, пока та едет

И вот мы как бы и подошли к главным элементам ООП -- классам, объектам, свойствам и методам.

Класс есть описание какого-то объекта. Будь то реальная сущность, или воображаемая кучка из данных. Чтобы создать класс в Python, мы используем ключевое слово class.

Далеко не отходя от темы с машинами: есть класс Car ("машина"), который будет использоваться для описания машины.

И вот как мы бы его определили:

class Car:
    ... # здесь что-то будет

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

car = Car()

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

Возникает вопрос: а где можно определить (т. е. создать/описать) свойства нашего объекта?

Для этого у классов существует магический метод (да, он так и называется) -- конструктор. Именно он и вызывается, когда создаётся экземпляр класса. Мы можем воспользоваться конструктором, чтобы передать какие-то данные в объект класса.

Конструктор определяется как функция с названием __init__, и первым её аргументом всегда идёт self -- это и есть создаваемый объект, который мы будем настраивать в конструкторе.

Пример для машины:

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color
 
# ...
 
car = Car("Fahrzeug", "чёрный")
 
print(car.color) # чёрный

В этом примере мы создали класс, конструктор которого получает марку и цвет машины. Затем мы задаём одноимённые свойства в объекте.

Конструктор -- это метод. А что такое метод?

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

Определим для машины метод drive, который будет как бы "перемещать" машину.

class Car:
   def __init__(self, brand, color):
      self.brand = brand
      self.color = color
   
   def drive(self):
       print(f"{self.brand} едет")
 
car = Car("Fahrzeug", "чёрный")
 
car.drive()
 
# к слову, аналогично такой записи:
Car.drive(car)

Немного усложним наш класс машины, добавив в него новое свойство -- топливо.

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color
        self.fuel = 5
    def drive(self, speed = 1):
       if self.fuel <= 0:
          print("недостаточно топлива")
       else:
          print(f"{self.brand} едет")
          self.fuel -= speed
 
car = Car("Fahrzeug", "чёрный")
 
car.drive()
 
# немного разгоним машину
car.drive(5)
 
# уже не выйдет поехать
car.drive()
 
# пополним топливо...
car.fuel = 10
 
# и снова в путь
car.drive()
 

С описанием класса разобрались. Что же насчёт второго пункта -- создания иерархии из объектов.

Вообще, это называется наследованием, и это -- один из трёх принципов ООП.

В наследовании мы можем создать класс на основе уже существующего. А потом использовать его свойста или методы. Или определить свои. Или переписать уже существующие. Тут уже на свой вкус.

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

Например, добавим нашей машине родительский класс -- транспортное средство. И создадим ещё одного наследника для ТС -- самолёт.

class Vehicle:
    brand = "неизвестное"
    def drive(self):
        print(f"{self.brand} перемещается")
 
class Car(Vehicle):
   def __init__(self, brand, color):
      super().__init__()
      self.brand = brand
      self.color = color
   def drive(self):
      print(f"{self.brand} едет")
 
class Airplane(Vehicle):
    def __init__(self, brand):
        self.brand = brand
    def drive(self):
        print(f"{self.brand} летит")
 
car = Car("Fahrzeug", "чёрный")
plane = Airplane("Paperplane")
 
car.drive()
print(plane.brand)

Как мы видим, в классе Vehicle определено свойство brand для марки ТС и метод drive для "перемещения" ТС. Наследники Vehicle переопределяют их под себя: машина Car едет, а самолёт Airplane летит.ц

В __init__ мы видим какую-то функцию super. Она представляет собой родительский класс.

Также замечу, что оба класса имеют общий метод drive. Это значит, что мы можем использовать данный метод в каком-то другом коде, не зная, а с каким именно классом мы сейчас работаем:

def drive_if(surface, vehicle):
   if surface == "воздух":
      print("поднимаем ТС на воздух")
   elif surface == "земля":
      print("разгоняем ТС")
   vehicle.drive()
 
car = Car("Fahrzeug", "чёрный")
drive_if("земля", car)

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

Вернёмся к варианту класса машины, в котором мы добавили свойство fuel (топливо).

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

Его смысл так и значится: скрытие элементов класса для обеспечения безопасности.

В Python скрыть метод или свойство можно двумя способами:

  • написав перед названием одно нижнее подчёркивание (field -> _field), но обратиться к нему извне всё ещё будет возможно (cls._field)
  • написав два нижних подчёркивания (field -> __field), и тогда поле точно скроется (но вариант cls._НазваниеКласса__field не отменяется)

Пример для топлива:

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color
        self.__fuel = 5
    def refuel(self, amount):
        if amount > 5:
            print("слишком много!")
        else:
            print("заправляемся на ", amount)
            self.__fuel += amount
    def drive(self, speed = 1):
        if self.__fuel <= 0:
           print("недостаточно топлива")
        else:
           print(f"{self.brand} едет")
           self.__fuel -= speed
 
car = Car("Fahrzeug", "чёрный")
car.drive(5) # "Fahrzeug едет" * 5
car.drive() # недостаточно топлива
# car.__fuel = 5 # ! ошибка: такого свойства нет
car.refuel(10) # слишком много!
car.refuel(5) # заправляемся на 5
car.drive() # Fahrzeug едет

© Egor Bron.

Треугольник - хороший повод задуматься. Просто задуматься.