Лекция 0

Язык Python – высокоуровневый язык программирования общего назначения с динамической типизацией. Python появился в 1991 году, и его автором является Гвидо ван Россум, голландский математик. В нашем курсе мы будем рассматривать третью версию языка, появившуюся в 2008 году. Эту версию также сокращённо обозначают как Python3. На момент написания лекций Python находился на 5-7 позициях рейтингов распространённости языков программирования.

Особенности языка Python

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

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

    В последнее время при поддержке компании Google был разработан хороший компилятор Python, однако мы в своём курсе всё равно будем пользоваться стандартным интерпретатором от команды разработчиков, возглавляемой ван Россумом.

  2. Автоматическое управление памятью. Память под объекты в Python выделяется автоматически при их создании и автоматически освобождается, когда объекты перестают быть доступными. Это однозначно упрощает написание программ, так как не нужно заботиться о выделении правильного количества памяти и отлавливать утечки памяти, возможные в языках, в которых освобождать память нужно вручную.

    Однако, такой подход приводит к тратам на поддержание счётчика ссылок на каждый объект в актуальном состоянии, ведь его нужно обновлять при добавлении или удалении ссылок на объект. Когда интерпретатору необходимо выделить память под очередной объект и памяти не хватает, то он останавливает выполнение программы и запускает процедуру сборки мусора. Эта процедура проходится по всем объектам в программе и удаляет все объекты с нулевыми счётчиками ссылок. Если освобождённой таким образом памяти всё ещё недостаточно, интерпретатор запросит у операционной системы ещё один участок памяти. Затем выполнение программы продолжается.

  3. В языке Python отсутствуют указатели, а при доступе к элементам массивов всегда происходит проверка на выход за границу массива. Следствием этого является практически полная невозможность ошибок работы с памятью. В языке C в случае ошибки при работе с памятью происходит либо Segmentation Fault, либо порча памяти, и строку, в которой происходит ошибка при выполнении программы, пришлось бы искать самостоятельно. В Python3 у нас будет информации о типе ошибки, краткое её описание, имя файла с исходным кодом и номер строки, в которой произошла ошибка.

    Traceback (most recent call last):
      File "ex.py", line 2, in <module>
        print(a[10])
    IndexError: list index out of range
    
  4. Ссылочная семантика. В Python каждая переменная является ссылкой на место в памяти, где содержится значение. Такая схема работы языка отличается от языка C, где каждая переменная является значением с жёстко выделенным количеством байт и типом.

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

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

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

    int a = 1;
    double b = 3.14;
    

    В данном случае для переменной a выделялась ячейка памяти размером sizeof(int) байт, и компилятор вставлял в выполняемый файл программы инструкции для работы с этой ячейкой только как числом типа int. Тип закреплён за переменной статически ещё во время компиляции.

    В языке Python каждая переменная является ссылкой на объект, и информация о типе объекта содержится в этом объекте:

    a = 1
    print(a, type(a))  # 1 int
    a = 3.14
    print(a, type(a))  # 3.14 float
    a = "abc"
    print(a, type(a))  # abc str
    

    В показанном примере переменная a легко меняет тип с int на float, а затем с float на str, строковый тип.

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

  6. Поддержка объектно-ориентированного программирования.

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

  7. Плата за удобство – низкая производительность.

    Так как следствием динамической типизации является необходимость проверять типы объектов перед каждой операцией с ними, а применение сборщика мусора заставляет регистрировать объекты в реестре сборщика при их создании, а так же периодически запускать сборку мусора для удаления неиспользуемых объектов, очевидно, производительность Python будет меньше, чем у компилируемого языка типа C или C++. В чисто вычислительных научных или инженерных задачах скорость выполнения одних и тех же операций может отличаться в десятки раз, поэтому вычислительные программы редко пишутся на Python. Однако есть возможность использовать для таких программ специальные библиотеки, написанные на C или C++, и ускорять с их помощью программы на Python. Кроме того, для написания программ, которые не требуют больших вычислений, бывает выгодно пожертвовать производительностью выполнения и получить взамен увеличение производительности труда программиста, которая выше в случае использования Python.

  1. Обширная стандартная библиотека и множество сторонних библиотек.

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

Переменные

Переменные создаются при первом присваивании.

a = 1
b = 'hello'

Если переменная не создана, но используется в выражении, интерпретатор выдаст ошибку NameError. Например, при выполнении кода

print(no_such_variable)

интерпретатор выдаст ошибку

Traceback (most recent call last):
  File "test.py", line 1, in <module>
    print(no_such_variable)
NameError: name 'no_such_variable' is not defined

Обратите внимание, что Python при каждой ошибке пишет её тип и краткое её описание, из которого можно попытаться понять, как её устранить. Так же, указывается имя файла и номер строки, в которой произошла ошибка.

Целые числа

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

a = 1000000000000000000000000000000000000000000
b = 2 * (a + 1)
print(b)

Операторы +, -, * работают как их математические аналоги.

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

10 / 10  => 1.0
1 / 2    => 0.5

Для деления без остатка в Python3 существует оператор //

10 // 3  => 3
-10 // 3 => -4

Обратите внимание, что в Python3 округление идёт вниз, к минус бесконечности. Благодаря этому остаток от деления всегда неотрицателен:

10 % 3  => 1
-10 % 3 => 2

Оператор ** отвечает за возведение в степень:

2 ** 8    => 256

Например, можно воспользоваться им для взятия квадратного корня (степени 0.5):

2 ** 0.5  => 1.4142135623730951

Чтобы ввести число с клавиатуры, нужно написать код вида

x = int(input())

Функция input считает ввод пользователя вплоть до перевода строки, и вернёт результат в виде строки. Со строками мы разберёмся немного позднее, а пока достаточно лишь знать, что если в строке записано число, то можно привести его к типу int с помощью одноимённой функции.

Если нужно ввести несколько чисел, то надо просто разместить подряд несколько таких строк:

x = int(input())
y = int(input())

При этом каждое число нужно вводить на отдельной строке.

Числа с плавающей точкой

Числа с плавающей точкой в Python почти не отличаются от таковых в C, за исключением того, что тип данных называется float и соответствует числу двойной точности, то есть типу double в C. Такие числа могут содержать значения примерно от \(-10^{308}\) до \(+10^{308}\), а минимальное положительное число составляет приблизительно \(10^{-324}\). Чисел одинарной точности в Python нет.

p = 3.14
G = 6.67408e-11   # Гравитационная постоянная.
# Число после буквы e -- показатель десятичной степени.
# Комментарии в Python начинаются с символа #,
# и не учитываются при работе программы.

Операторы +, -, *, / ведут себя аналогично операторам языка C. Если хотя бы один из операторов – это число с плавающей точкой, то типом результата тоже будет float.

Для деления без остатка применяется оператор //, для остатка от деления %:

10 // 3.0   => 3.0
-10 // 3.0   => -4.0
10 % 3.0   => 1.0
-10 % 3.0   => 2.0

Так же работает оператор возведения в степень:

27.0 ** (1 / 3)   => 3.0

Числа с плавающей точкой вводятся с помощью функций input и float:

x = float(input())

Оператор if

Короткая версия оператора if выглядит следующим образом.

_images/if_1branch.png

Обратите внимание, что вложенность инструкций в оператор if определяет не тот или иной вид скобок, а расположение на увеличенном уровне отступов. Таким образом, в Python3 скобки нужны не только для красоты и читаемости кода, но необходимы для корректной работы программы и определяют её логическую структуру. Сообществом Python принято использовать для отступа 4 пробела. Редактор IDLE автоматически делает отступ в 4 пробела при нажатии на клавишу Tab.

Пример работы оператора:

x = int(input())
if x > 0:
    print("Positive number")

Такой код выведет сообщение “Positive number”, если введённое число больше нуля, если же оно равно нулю или меньше нуля, то не выведет ничего.

Ещё один пример (обратите внимание, что инструкция print(“Finish”) находится на том же уровне отступов, что и if, а значит, не сложена в if и будет выполняться в обоих случаях):

x = int(input())
if x > 0:
    print("Positive")
    print("Number")
print("Finish")

При вводе положительного числа программа выведет:

Positive
Number
Finish

При вводе нуля или отрицательного числа:

Finish

Полная версия if выглядит следующим образом:

_images/if_2branch.png

Наличие нижней строчке на рисунке с “кодом вне if” не обязательно, она дана проста для понимания того, куда переходит управление из оператора if.

Пример кода:

x = int(input())
if x > 0:
    print("Positive number")
else:
    print("Negative number or zero")