Основы программирования в Python

Алла Тамбовцева, НИУ ВШЭ

Данный ноутбук основан на лекции Щурова И.В., курс «Программирование на языке Python для сбора и анализа данных» (НИУ ВШЭ).

Python как калькулятор, переменные и типы переменных

Python как калькулятор

Привычные арифметические действия (сложение, вычитание, умножение, деление) в Python выглядят так же, как и в обычных калькуляторах:

In [1]:
1 + 2 # сложение
Out[1]:
3
In [2]:
2 * 4 - 9 # умножение и вычитание
Out[2]:
-1
In [3]:
23 / 2 # деление
Out[3]:
11.5

Однако с делением все не так просто: Python 3 всегда будет выдавать результат в виде числа с плавающей точкой (float), даже тогда, когда ожидается целочисленный ответ. Например:

In [4]:
6 / 2 # не 3
Out[4]:
3.0

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

In [5]:
6 // 2 # теперь 3
Out[5]:
3

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

In [6]:
6 // 4 # от 1.5 осталось 1
Out[6]:
1

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

Примечание: для того, чтобы сравнивать исполнение одного и того же кода в Python 3 и Python 2 совсем необязательно устанавливать обе версии на компьютер. Можно воспользоваться онлайн-компиляторами (например, http://rextester.com: выбирать Python или Python 3) и запускать код прямо в браузере. Речь идет, конечно, о небольших фрагментах кода. Полноценно работать в них, не устанавливая Python, будет неудобно. Но для нескольких строк для сравнения как раз.

Что еще можно делать с числами? Возводить в степень и извлекать из них корень. При расчетах на калькуляторе и в R для возведения числа в степень мы обычно используем символ ^. Попробуем!

In [7]:
2 ^ 3 # ой
Out[7]:
1

Получилось что-то неожиданное. В Python оператор ^ используется для побитного сложения по модулю два. Для возведения числа в степень потребуется ** (кстати, в R тоже для возведения в степень можно использовать двойную звездочку):

In [8]:
2 ** 3 # как нужно
Out[8]:
8

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

In [9]:
sqrt(9) # не получается!
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-9-baf0a9e4b790> in <module>()
----> 1 sqrt(9) # не получается!

NameError: name 'sqrt' is not defined

Python пишет, что не знает, что такое sqrt. В каких случаях Python может такое писать? Например, если мы опечатались в названии функции (Python не понимает, что мы от него хотим) или если мы пытаемся обратиться к функции, которая не является базовой (Python не знает, откуда ее брать). В нашем случае у нас вторая проблема. Функция для вычисления квадратного корня из числа хранится в специальном модуле math. Этот модуль стандартный, дополнительно устанавливать его не нужно. Но для того, чтобы воспользоваться этой функцией, нужно сначала импортировать модуль, а потом вызвать из него функцию sqrt (для тех, кто работал в R -- процедура, аналогичная library().

In [10]:
import math # импортируем модуль math
In [11]:
math.sqrt(9) # теперь все работает
Out[11]:
3.0

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

In [12]:
from math import sqrt
sqrt(16) # так тоже работает
Out[12]:
4.0

В math есть много полезных функций для вычислений. Чтобы посмотреть, какие функции там есть, после импортирования всего модуля через import math можно набрать math. и нажать на Tab (табуляция, кнопка над Caps Lock). Помимо квадратного корня этот модуль поможет вычислить логарифм (натуральный и не только), синус, косинус и так далее.

In [13]:
math.log(2) # натуральный логарифм
Out[13]:
0.6931471805599453
In [14]:
math.log10(100) # десятичный логарифм (логарифм по основанию 10)
Out[14]:
2.0
In [15]:
math.sin(0) # синус
Out[15]:
0.0

А еще из math можно импортировать константы $\pi$ и $e$:

In [16]:
from math import pi, exp # можно сразу несколько - перечислить через запятую
In [17]:
pi
Out[17]:
3.141592653589793
In [18]:
exp(1)
Out[18]:
2.718281828459045

С чем еще можно столкнуться, выполняя вычисления в Python? С такими вещами:

In [19]:
1 / 18 ** 25
Out[19]:
4.1513310942010236e-32

Результат выше – компьютерная форма экспоненциальной записи числа. Возможно, тот, кто считал что-то на научных или инженерных калькуляторах, уже сталкивался с такой записью. Здесь e-32 – это $10^{-32}$, а вся запись означает $4.1513310942010236 \cdot 10^{-32}$, то есть примерно $4.15 \cdot 10^{-32}$. Если бы число было очень большим, e стояло бы в положительной степени.

Такая компьютерная форма записи числа отчасти помогает понять, почему дробные числа называются числами с плавающей точкой (float). Возьмем число попроще, например, $12.34$. Его можно записать как $12.34$, как $1.234 \cdot 10$, как $123.4 \cdot 10^{-1}$, $1234 \cdot 10^{-2}$ и так далее. Точка, отделяющая дробную часть от целой, будет "плавать", однако само число при этом меняться не будет, будут меняться только множители ‒ разные степени десятки.

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

In [20]:
round(12.6) # округлим до целого
Out[20]:
13
In [21]:
round(12.53, 1) # округлим до первого знака после запятой
Out[21]:
12.5

С другой стороны, могут возникнуть странности:

In [22]:
round(2.50) # не 3
Out[22]:
2
In [23]:
round(3.525, 2) # не 3.53
Out[23]:
3.52

Эти странности связаны с тем, что число, которое мы видим (например, 3.525), не совпадает с тем, которое хранится в компьютере, потому что оно при сохранении преобразовывается и превращается из точного 3.525 в такое:

In [24]:
from decimal import Decimal
Decimal(3.525)
Out[24]:
Decimal('3.524999999999999911182158029987476766109466552734375')

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

Переменные

Переменные в программировании похожи на переменные в математике. Кроме того, их можно рассматривать как хранилища значений – «коробки», в которые мы что-то кладем. Python, в отличие от некоторых языков программирования (C, C++, Java), сам распознает что мы «кладем в коробку»: число, целое число, текст, список чисел... Поэтому при создании переменной нам не нужно указывать ее тип.

In [25]:
x = 2
y = 3
In [26]:
print(x)
print(y)
2
3

Значения переменных мы можем обновлять – изменить значение и сохранить в переменную с тем же названием.

In [27]:
x = x + 1 # возьмем значение x, увеличим на 1 и сохраним изменения в переменной x
In [28]:
y = y * 2 # возьмем значение y, увеличим в 2 раза и сохраним изменения
In [29]:
print(x, y)
3 6

Рассмотрим такую задачу. Пришла весна и решили мы заняться бегом по такой схеме: каждый день мы пробегаем столько, сколько в сумме за два предыдущих дня. При этом первые два дня мы морально готовимся: топчемся на месте и символически проходим по одному метру (полшага назад и полшага вперед). Если мы будем записывать все пройденные нами расстояния в ряд, мы получим последовательность из чисел Фибоначчи. Давайте напишем код, который будет считать, сколько метров мы будем пробегать в следующий день.

Сначала создадим переменные, в которые сохраним данные по первым двум дням.

In [30]:
b = 1 # день 1 - готовимся бегать, "бежим" 1 метр 
i = 1 # номер дня, когда начинаем бегать
bnext = 1 # день 2 - готовимся бегать, "бежим" 1 метр
i = i + 1 # перешли ко второму дню, увеличили i на 1
In [31]:
res = b + bnext # в следующий день пробегаем столько же, сколько за два предыдущих
i = i + 1 # перешли к следующему дню, увеличили i на 1
b = bnext   # значение b нам уже не нужно, сдвигаемся к следующему дню - записываем bnext
bnext = res   # запомнили полученное значение res
print(i, bnext) # выводим на экран номер дня и расстояние, которое нужно пробежать
3 2

Теперь можно прогонять предыдущую ячейку много раз (через Ctrl + Enter) и получать результат по каждому дню. Например, на 20 день мы будем пробегать уже нормальное расстояние — 6765 метров, почти 7 километров. Конечно, прогонять одну и ту ячейку много раз неудобно и странно, но о том, как считать числа Фибоначчи более рационально, мы поговорим, когда будем разбирать циклы.

Важно: если бы не разбили наш код на части (на две ячейки), ничего бы при повторном запуске ячейки не произошло — переменным b, bnext и i заново присваивались бы значения 1, и движения вперед бы не происходило.

Типы переменных и преобразование типов

Типы переменных (не путать с data types):

  • числовой c плавающей точкой (float)
  • целочисленный (integer)
  • строковый или текстовый (string)
  • логический (boolean): только два значения True и False

Примечание: в R вместо float был numeric, вместо stringcharacter, вместо booleanlogical.

Посмотрим, как определить тип переменной:

In [32]:
x = 2.34
type(x)
Out[32]:
float
In [33]:
y = 2
type(y)
Out[33]:
int
In [34]:
r = 'hello'
type(r)
Out[34]:
str
In [36]:
l = True
type(l)
Out[36]:
bool

Иногда требуется преобразовать тип переменной, например, из числа с плавающей точкой сделать целое число. Зачем это бывает нужно? Для удобства и для более корректной выдачи результатов. Например, у нас есть база данных по респондентам, в которой указан их год рождения, и мы хотим добавить столбец с возрастом респондентов (числом полных лет). Из-за того, что кто-то ввел год в виде 1993.0, возраст при вычислениях тоже получится числом с плавающей точкой — 25.0. Так как мы знаем, что возраст всегда будет целым, чтобы дробная часть не смущала, можно привести все значения к целочисленному типу.

In [37]:
int(25.0) # int - от integer
Out[37]:
25

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

In [38]:
float(23)
Out[38]:
23.0
In [39]:
str(2) # str - от string
Out[39]:
'2'
In [40]:
int("23")
Out[40]:
23

Однако, если преобразование невозможно, Python выдаст ошибку (а точнее, исключение):

In [41]:
float('23,56')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-41-b3dd92f20a26> in <module>()
----> 1 float('23,56')

ValueError: could not convert string to float: '23,56'

Так как запятая не воспринимается как десятичный разделитель в Python (в качестве разделителя используется точка), превратить строку '23,56' в число не получится, нужно будет сначала заменить запятую на точку. Как работать со строками, мы обсудим позже, но если интересно, можно сделать следующее: создать любую строковую переменную, а потом после ее названия поставить точку и нажать Tab. Так же, как и в случае с модулем math, выпадет список всех возможных методов, которые можно применять к строке.

In [42]:
words = "political science"
In [43]:
words.capitalize() # например, метод capitalize
Out[43]:
'Political science'
In [44]:
help(words.capitalize) # информация по нему
Help on built-in function capitalize:

capitalize(...) method of builtins.str instance
    S.capitalize() -> str
    
    Return a capitalized version of S, i.e. make the first character
    have upper case and the rest lower case.