С' 2016

1. Hello world!

Как правило, первая программа которую пишут на языке - это "Hello world!". Ниже приведен полный код этой программы на языке Python 3.

In [1]:
print("Hello world!")
Hello world!

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

Первый тип данных, с которым мы познакомимся, будет строка (str). "Hello world!" является строкой.

Строки в python можно задавать также с помощью одинарных кавычек:

In [2]:
print('Hello world!')
Hello world!
In [3]:
print(10)
print(2 + 2)
print(11, 11)
print("Hi!", 42, 12.5)
10
4
11 11
Hi! 42 12.5

Какие ещё типы данных, помимо строки, есть в коде выше?

Целое число (int), дробное число (float).

Язык программирования python является интерпретируемым языком, в отличие от Pascal или C/C++, являющихся компилируемыми.
В компилируемых языках написанный код переводится с помощью специальной программы, называемой компилятором, в другой код (процесс перевода --- компиляция), более понятный машине, а затем уже исполняется этот новый код. В интерпретируемых языках написанный код никуда не переводится, а выполняется построчно с помощью специальной программы, называемой интерпретатором (в нашем случае, это python3). Увидели строчку --- выполнили.
Как следствие этого, если вы писали до этого на компилируемых языках, то при попытке компиляции вам сразу выдавались все ошибки в коде, если они были. И программа не запускалась до тех пор, пока вы не устраните все ошибки. В интепретируемых языках вы увидите ошибку только в тот момент, когда дойдете до нее. То есть часть кода может выполнится успешно, и выполнение остановится, как только где-нибудь вы допустили ошибку.

2. Переменные

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

In [4]:
a = 5
b = 7 + 3
print("a + b =", a + b)
a + b = 15
In [5]:
c = a + b
print(' "c =" ', c)        # что будет напечатано?
 "c ="  15

В этом куске кода выше есть как минимум одна новая вещь: комментарий, он начинается с символа # и продолжается до конца строки.

In [6]:
some_string = "string variable"
print("some_string =", some_string)   

"""
сами строки 
не выводятся с кавычками
"""
some_string = string variable
Out[6]:
'\nсами строки \nне выводятся с кавычками\n'

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

Как вы заметили, в отличие от С/С++ и Pascal, мы при объявлении переменной не задавали ее тип. Все очень просто: в Python у переменной нет типа, подобное свойство языка называют динамической (утиной) типизацией. Определение типа переменной основано на свойствах переменной.

Если это выглядит как утка, плавает как утка и крякает как утка, то это возможно и есть утка.

Пример ниже демонстрирует это свойство чуть-чуть подробнее.

In [7]:
a = 5
print(a)    # python сам определяет, что это целое число (int)
a = "a is string now!"
print(a)    # теперь python понимает, что это строка (str)
5
a is string now!

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

In [8]:
a = 5
b = "5"
print(a + b)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-ce5c646256b0> in <module>()
      1 a = 5
      2 b = "5"
----> 3 print(a + b)

TypeError: unsupported operand type(s) for +: 'int' and 'str'

Будет ошибка, так как python не позвоялет складывать целые числа и строки с помощью +.

3.Арифметические операции

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

Список основных бинарных (требующих 2-ух переменных) арифметических действий, которые вам понадобятся:

  • Сложение: +
  • Вычитание: -
  • Умножение: *
  • Вещественное деление: /
  • Целочисленное деление (округление производится вниз): //
  • Взятие остатка (работает с действительными числами тоже): %
  • Возведение в степень: **
In [9]:
a = 1
b = 2
print(a + b)
3
In [10]:
a = 2.0
b = 1
print(a + b)
3.0
In [11]:
a = 4
b = 5
print(a - b)
-1
In [12]:
a = 3.0
b = 4
print(a - b)
-1.0
In [13]:
a = 2
b = 4
print(a * b)
8
In [14]:
a = 2.1
b = 4
print(a * b)
8.4
In [15]:
a = -2.1
b = 4
print(a * b)
-8.4
In [16]:
a = 10
b = 2
print(a // b)
5
In [17]:
a = 10
b = 3
print(a // b)
3
In [18]:
a = -10
b = 3
print(a // b)
-4

Стоит заметить, что по математическим правилам x = y $\cdot$ b + r, где r --- остаток при делении числа x на y и r $\ge$ 0. Отсюда следует, что y = x // b. В примере выше: -10 = -4 $\cdot$ 3 + 2

In [19]:
a = 10
b = 3
print(a / b)
3.3333333333333335
In [20]:
a = 10
b = 2
print(a / b)
5.0

Даже если r = 0, то результат всё равно будет вещественным числом (float)

In [21]:
a = 10
b = 2
print(a % b)
0
In [22]:
a = 10
b = 3
print(a % b)
1
In [23]:
a = 10
b = 3.5
print(a % b)
3.0
In [24]:
a = -10
b = 3
print(a % b)
2

Здесь как раз видно, что r = 2

In [25]:
a = 2
b = 2
print(a ** b)
4
In [26]:
a = -2
b = 2
print(a ** b)
4
In [27]:
a = 2
b = -2
print(a ** b)
0.25
In [28]:
a = 2.5
b = 2
print(a ** b)
6.25
In [29]:
a = 4
b = 0.5
print(a ** b)
2.0

Кто не знает: $\sqrt{a} = a^{\tfrac{1}{2}} = a^{0.5}$

In [30]:
a = -4
b = 0.5
print(a ** b)
(1.2246467991473532e-16+2j)

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

Здесь мы также увидели некоторое особенное число 1.2246467991473532e-16. Что же это такое?
Запись <число>e<степень> = <число> $\cdot 10^{\text{<степень>}}$

Чему же равно 1.2246467991473532e-16?

In [31]:
a = -4.2
b = -0.5
print(a ** b)
(2.9878322515602243e-17-0.4879500364742666j)

Так же существует специальный операторы позволяющие сократить записи вида: a = a + 5. Все эти операторы записываются как "оператор=", ниже несколько примеров.

In [32]:
a = 1
a += 2     # эквивалент а = а + 2
print(a)
3
In [33]:
a = 3
b = 2
a *= b     # эквивалент а = а * b
print(a)
6
In [34]:
a = 3
b = 2
a **= b    # эквивалент а = а ** b
print(a)
9

Отдельно обратим внинамние на то, как пишутся арифметические опреации. Вокруг любой арифметической операции ставится по одному пробелу. Например, x+y --- неправильно, а x + y --- правильно. Это часть соглашения о стиле написания кода на python PEP8.

4.Операторы if..elif..else

Думаю, всем понятно, как работает оператор if, ниже несколько примеров, чтобы разобраться с синтаксисом python.

In [35]:
if 5 < 3:
    print("Правда")
else:
    print("Ложь")
Ложь

После условия в if или после else ставится двоеточие :. Это часть синтаксиса python. Все, что будет выполняться после if должно идти с отступом 4 пробела (или один tab, но нельзя их смешивать! используйте пробелы или настройте в среде конвертацию таба в пробелы) относительно предыдущей строки. Аналогично с else. Это демонстрирует пример ниже.

In [36]:
if 10 > 5:
    print("1")
    print("2")
    if 5 < 4:
        print("3")
        print("4")
    else:
        print("5")
else:
    print("6")
        
1
2
5

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

In [37]:
if 6 > 5
    print("Пропущено двоеточие!")
  File "<ipython-input-37-615a4bd4ed19>", line 1
    if 6 > 5
            ^
SyntaxError: invalid syntax
In [38]:
if 6 > 5:
print("Пропущен отступ!")
  File "<ipython-input-38-641a99981a4e>", line 2
    print("Пропущен отступ!")
        ^
IndentationError: expected an indented block

Некоторые среды программирования, такие как, Wing Ide, в котором мы будем работать, умеют автоматически расставлять отступы там, где это необходимо.

In [39]:
if 9 * 7 ** 2 > 8 * 5 ** 2:
    print("Правда")
else:
    print("Ложь")
Правда
In [40]:
if True:
    print("Ветка if")
else:
    print("Ветка else")
Ветка if
In [41]:
if False:
    print("Ветка if")
else:
    print("Ветка else")
  
Ветка else

В python существуют 2 логических типа: True и False. Такие типы нызвают булевскими (bool).

Конструкция elif позволяет рассматривать множественные случаи без вложенных if ... else.

In [42]:
if False:
    print("Ветка if")
elif True:
    print("Ветка elif")
else:
    print("Ветка else")
    
Ветка elif

Что будет напечатано выше?

Понятно, что после if может стоять не только значение True или False. Посмотрим, что делает Python, если после if стоит нечто более странное?

Одни типы данных могут приводиться к другим, если это возможно (если python позволяет). Такое преобразование называется приведением типов, или, на слэнге, прикастовыванием (от англ. cast --- приведение).

0 --> False

In [43]:
if 0:                     
    print("Ветка if")
else:
    print("Ветка else")
Ветка else

Любое другое число (в том числе и нецелое), отличное от 0 --> True

In [44]:
if 5:
    print("Ветка if")
else:
    print("Ветка else")
Ветка if
In [45]:
if 3.5:
    print("Ветка if")
else:
    print("Ветка else")
Ветка if
In [46]:
if 0.0:
    print("Ветка if")
else:
    print("Ветка else")
Ветка else
In [47]:
if -1:
    print("Ветка if")
else:
    print("Ветка else")
Ветка if

Пустая строка "" --> False

In [48]:
if "":
    print("Ветка if")
else:
    print("Ветка else")
Ветка else

Непустая строка --> True

In [49]:
if "abc":
    print("Ветка if")
else:
    print("Ветка else")
Ветка if
In [50]:
if "''":
    print("Ветка if")
else:
    print("Ветка else")
Ветка if

Посмотрим, как устроены логические операции и операции сравнения в Python.

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

  • Равенство: ==
  • Неравенство: !=
  • Меньше: <
  • Меньше либо равно: <=
  • Больше: >
  • Больше либо равно: >=
  • Логическое или: or
  • Логическое и: and
  • Логическое отрицание: not

Рассмотрим несколько примеров.

In [51]:
print(5 > 2)
True
In [52]:
print(not 5 > 2)
False
In [53]:
a = 5
b = 2
print(a > b and a >= b)
True
In [54]:
a = 5
b = 2
print(True and a > 2)
True
In [55]:
a = 5
b = 2
print(False or b >= 2)
True
In [56]:
a = 5
b = 2
print(b == a)
False
In [57]:
a = 5
b = 2
print(b != a)
True

А вот так писать плохо и бессмысленно:

In [58]:
bool_var = True
if bool_var == True:
    print("ай-ай")

if bool_var:
    print("Так хорошо")
    
bool_var = False

if not bool_var:
    print("И так тоже")
ай-ай
Так хорошо
И так тоже

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

In [59]:
year = 2015

if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
    print("YES")
else:
    print("NO")
NO

Единственная проблема у кода выше - это невозможность считать год с клавиатуры, как можно заметить, мы задали его в явном виде. Для решения этой проблемы нам понадобится функция input, она считывает строчку (до перевода строки) из stdin (стандартного потока ввода, который обычно связан с клавиатурой). Также нам понадобится функция int, она преобразует число записанное в виде строки в число. Далее, на примере среды Wing, мы подробнее разберем, откуда все считывается и куда выводится.

In [60]:
year = int(input())

if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
    print("YES")
else:
    print("NO")

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-60-e8038eaed948> in <module>()
----> 1 year = int(input())
      2 
      3 if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
      4     print("YES")
      5 else:

ValueError: invalid literal for int() with base 10: ''

5.Цикл while

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

In [61]:
i = 0
while i < 5:
    i += 1
    print(i)
1
2
3
4
5

Естественно, после while может стоять любое условие.

Также есть оператор break, он заканчивает выполнение цикла по достижению break.

In [62]:
i = 0
while True:
    print("before", i)
    if i == 2:
        break
    print("after", i)
    
    i += 1
before 0
after 0
before 1
after 1
before 2

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

In [63]:
n = int(input())

cp, i = 1, 0
while n >= cp:
    cp *= 2
    i += 1
    
print("cp = %d, i = %d" % (cp, i))  # это более удобный способ форматирования строк, ниже несколько комментариев про него

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-63-c284d85b2af7> in <module>()
----> 1 n = int(input())
      2 
      3 cp, i = 1, 0
      4 while n >= cp:
      5     cp *= 2

ValueError: invalid literal for int() with base 10: ''

В задаче выше мы воспользовались несколькими новыми вещами.

Во-первых, конструкция вида a, b = c, d , называется параллельным присваиванием. В этом случае переменной a будет присвоено значение c, а переменной b --- значение d. В некоторых ситуациях это бывает очень удобно.

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

In [64]:
print("I'am %d years old." % 19)
I'am 19 years old.

%d указывает на то, что на месте %d должно стоять число, идущее после % (в данном случае это 19).
В примере со степенью надо было подставить 2 числа, для этого мы их объединили в скобки. Если написать без них, то будет ошибка, так как ожидается, что должно подставить 2 значения, а дано только одно. Более точное объяснение скобкам будет ниже.

In [65]:
print("2 * 2 = %d" % (2 * 2)) # самый простой пример просто вставляющий целое число в нужное место
2 * 2 = 4

Форматирование строк --- мощный и гибкий инструмент. Можно задавать различные параметры, например, для подставляемых чисел. Например, в следующем примере, конструкция %02d означает: подставить число, если оно меньше двух знаков, то заполнить пространство перед ним нулями так, чтобы суммарно получилось два знака.

In [66]:
print("%02d:%02d" % (15, 2))  # немного форматирования самого числа
15:02

6. List и tuple

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

In [67]:
a = [1, 2, "Hi"]        # мы создали список и присвоили переменной а этот список
print(a[0], a[1], a[2]) # так обращаться к элементам списка, элементы индексируются с нуля

b = list()              # таким образом можно создать пустой список
c = []                  # пустой список также можно создать и таким способом
1 2 Hi

В Python есть отрицательная индексация. $-1$ элемент это последний элемент, $-2$ элемент это предпоследний элемент списка и так далее. После примеров ниже должно стать совсем понятно.

In [68]:
a = [1, 2, "Hi"] 
print(a[-2]) 
2
In [69]:
b = ["a", 1, 2, 3, 4, "b", -10, [1, 2, 3], 3.4, "!!!"] # элементом списка может быть список
print(b[-3])
[1, 2, 3]
In [70]:
a = [1, 2, "Hi"] 
a[1] = 4 # можно изменить отдельный элемент
print(a) # можно вывести весь список
[1, 4, 'Hi']

Бывает такое, что нам нужна только некоторая часть списка, например все элементы с $5$ по $10$, или все элементы с четными индексами. Подобное можно сделать с помощью срезов.

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

  • список[первый элемент:следующий после последнего элемента]
  • список[первый элемент:] в этом случае последний это последний элемент списка
  • список[:следующий после последнего элемента] в этом случае нулевой элемент это первый элемент списка
  • список[первый элемент:следующий после последнего элемента:шаг] шаг - промежуток между соседними элементами, которые нужно взять, по умолчанию шаг равен 1

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

In [71]:
print([1, 2, 3, 4, 5, 6, 7, 8][:5])
[1, 2, 3, 4, 5]
In [72]:
print([1, 2, 3, 4, 5, 6, 7, 8][1:7:2])
[2, 4, 6]
In [73]:
print([1, 2, 3, 4, 5, 6, 7, 8][:0])
[]
In [74]:
print([1, 2, 3, 4, 5, 6, 7, 8][::2])
[1, 3, 5, 7]
In [75]:
print([1, 2, 3, 4, 5, 6, 7, 8][::-1])
[8, 7, 6, 5, 4, 3, 2, 1]
In [76]:
print([1, 2, 3, 4, 5, 6, 7, 8][2::-1])
[3, 2, 1]

Кстати, как вы думаете, что будет выведено ниже?

In [77]:
if []:
    print("Ветка if")
else:
    print("Ветка else")
Ветка else
In [78]:
if not []:
     print("Ветка if")
else:
    print("Ветка else")
Ветка if
In [79]:
if [2, 3, 4]:
    print("Ветка if")
else:
    print("Ветка else")
Ветка if

В списках все точно так же, как и со строками: пустой список [] --> False, непустой -->True.
Что будет выведено ниже?

In [80]:
if [[]]:
    print("Ветка if")
else:
    print("Ветка else")
Ветка if

Несколько необычных примеров:

In [81]:
print("abc" and [1, 2, 34])
[1, 2, 34]
In [82]:
print("abc" < [1, 2])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-82-4be796a5ac22> in <module>()
----> 1 print("abc" < [1, 2])

TypeError: unorderable types: str() < list()
In [83]:
print("abc" == [1, 2])
False
In [84]:
print("abc" != [1, 2])
True

Давайте немного подробнее поговорим о присваивании списков, с этим в Python есть несколько сложностей.

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

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

In [85]:
a = [1, 2, 3, 4] # а - ссылка на лист, каждый элемент листа это ссылка на элементы 1, 2, 3, 4
b = a  # b - ссылка теперь на тот же лист
c = "abc"
d = 5

print("id(a) = %d, id(b) = %d" % (id(a), id(b))) # в Python у каждого объекта есть свой id
                                                 # это некоторое уникальное число сопоставленное объекту

a[0] = -1
print("b =", b)
id(a) = 140059766526664, id(b) = 140059766526664
b = [-1, 2, 3, 4]

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

In [86]:
l = [1, 2, -1, 3, 2, -2, 1, 5, 7, 3]
print(min(l))
-2
In [87]:
# не стоит думать, что min можно брать только от list или tuple, конечно, существует min от двух элементов
print(min(12, 10))
10
In [88]:
print(sum([2, 3, 11, 1])) # эта функция суммирует все числа в list или tuple
17
In [89]:
l = [1, 2, 3]
l.append(4) # метод append добавляет элемент в конец списка
print(l)
l.pop()     # метод pop удаляет из списка последний элемент
print(l)
[1, 2, 3, 4]
[1, 2, 3]
In [90]:
l = [1, 2, -1, 3, 2, -2, 1, 5, 7, 3]
print(l.index(3))   # заметьте, что в списке l у нас две тройки и index возвращает индекс первой из них
3
In [91]:
l = [1, 2, -1, 3, 2, -2, 1, 5, 7, 3]
print(l.index(115)) # если элемента нет в списке, то произойдет ошибка выполнения
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-91-d045d99a485d> in <module>()
      1 l = [1, 2, -1, 3, 2, -2, 1, 5, 7, 3]
----> 2 print(l.index(115)) # если элемента нет в списке, то произойдет ошибка выполнения

ValueError: 115 is not in list
In [92]:
l = [1, 2, 4, 5]
l.insert(2, 3) # метод insert(idx, el) вставляет элемент el на место idx. 
print(l)
[1, 2, 3, 4, 5]

Так же есть удобный метод sort, он сортирует список по невозрастанию.

In [93]:
a = [3, 2, 4, 1, 2]
a.sort()
print(a)
[1, 2, 2, 3, 4]

Еще одна приятная функция это reverse, она, как следует из названия, переворачивает список.

In [94]:
a = [1, 4, 2, 5, 2]
a.reverse()
print(a)
[2, 5, 2, 4, 1]

А как еще можно перевернуть список?

In [95]:
a = [1, 4, 2, 5, 2]
a = a[::-1]
print(a)
[2, 5, 2, 4, 1]

Списки также можно складывать друг с другом:

In [96]:
a = [1, 2, 3]
b = [4, 5, 6]
c = a + b
print(c)
[1, 2, 3, 4, 5, 6]

У списка можно узнать его длину:

In [97]:
a = [1, 3, 6]
print(len(a))
3

Tuple почти ничем не отличает от list, только он неизменяемый. То есть нельзя взять и изменить один элемент. Во всем остальном они будут идентичны.

In [98]:
a = (1, 2, "Hi")         # tuple записывается в круглых скобках
print(a[0], a[1], a[2])

b = tuple()              # изменения по сравнению с аналогичным куском кода для списка минимальны
с = (3,)
1 2 Hi
In [99]:
a = (1, 2, "Hi")
a[1] = 4                 # так делать нельзя, о чем интерпретатор нам скажет
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-99-9ab68226910a> in <module>()
      1 a = (1, 2, "Hi")
----> 2 a[1] = 4                 # так делать нельзя, о чем интерпретатор нам скажет

TypeError: 'tuple' object does not support item assignment
In [100]:
a = (1, 2, "Hi")
a.sort()                 # аналогично нельзя, ведь sort хотел бы изменить a
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-100-9741c5bb045d> in <module>()
      1 a = (1, 2, "Hi")
----> 2 a.sort()                 # аналогично нельзя, ведь sort хотел бы изменить a

AttributeError: 'tuple' object has no attribute 'sort'

Tuple так же умеет работать с срезами.

Выше мы встречались с такой конструкцией как параллельное присваивание. Давайте ее немного обобщим.

Раньше у нас параллельное присваивание выглядело так:

a, b = c, d

Но! Если мы вместо двух значений c и d поставим один tuple или list содержащий столько же элементов, сколько и переменных в левой части равенства, то Python проделает аналогичное параллельное присваивание.

In [101]:
a, b = (1, 2)           # самый простой случай, когда мы присваиваем в явном виде заданный tuple 
print(a, b)
1 2
In [102]:
a, b, c = [2, "abs", 3] # то же самое, но со списком
print(a, b, c)
2 abs 3
In [103]:
tup = (1, 2, 3)
a, b, c = tup           # чуть менее тривиальная запись того же самого
print(a, b, c)
1 2 3

Впереди нас ждет еще один кусочек про параллельное присваивание, а пока остановимся.

7.Цикл for

Цикл for имеет в Python свою специфику.

В нашем случае цикл имеет один из двух видов:

  • for переменная in list/tuple
  • for переменная in range(start, end, step)

Рассмотрим примеры.

In [104]:
for i in (2, 3, 5):
    print(i)
2
3
5
In [105]:
t = [3, "ads", 4]
for i in t:
    print(i)
3
ads
4
In [106]:
for i in range(5):
    print(i)
0
1
2
3
4
In [107]:
for i in range(2, 5):
    print(i)
2
3
4
In [108]:
for i in range(2, 10, 3):
    print(i)
2
5
8

Стоить заметить, что поведение параметров range (некоторый объект) очень похоже на поведение параметров у срезов.

Примеры ниже либо сводят с ума либо показывают, что range во многом похож на list! Но чтобы работали эти примеры нужен Python 3.3 или более новой версии.

In [109]:
r = range(5)

print(r[2], r[-1])
2 4

Аналогично циклу while для цикла for есть оператор break.

In [110]:
for i in range(10):
    print("before", i)
    if i == 2:
        break
    print("after", i)
before 0
after 0
before 1
after 1
before 2

В Python у цикла for есть возможность написать ветку else. Это будет работать так: если цикл был завершен при помощи break, то ветка else выполнена не будет, в противном случае, после выполнения цикла она будет выполнена.

Достаточно странная возможность, чтобы понять как это можно использовать, решим задачу. Дан список, нужно проверить встречается ли в нем $-1$, если встречается, вывести "YES", иначе "NO". (Мы еще не умеем считывать целиком список, поэтому пока зададим его в коде программы.)

In [111]:
l = [1, 2, 3, 4, 5, 10, 1, 5, -1, 10, 12]

for i in l:
    if i == -1:
        print("YES")
        break
else:
    print("NO")
YES
In [112]:
l = [1, 2, 3, 4, 5, 10, 1, 5, 10, 12]

for i in l:
    if i == -1:
        print("YES")
        break
else:
    print("NO")
NO

Такая реализация иногда заметно проще, чем заводить отдельную переменную для флага или писать if...else.

8.Строки

Мы уже раньше много раз встречались со строками, задавая их, как правило в явном виде в кавычках. Даже много чего делали с ними: выводили, даже чуть-чуть форматировали. Давайте подробнее обсудим, что можно делать со строками средствами Python.

In [113]:
a = "abc"
b = "edf"
print(a)           # просто вывести
print(a * 2)       # строки можно естественным образом умножать на целое число
print(a + b)       # строки можно складывать
print(a[0], a[-1]) # можно обращаться к элементам строки
print(len(a))      # возвращает длину строки
abc
abcabc
abcedf
a c
3
In [114]:
a = "abc"
a[0] = 'f'         # строки неизменяемые как и tuple
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-114-f83efea5f5bd> in <module>()
      1 a = "abc"
----> 2 a[0] = 'f'         # строки неизменяемые как и tuple

TypeError: 'str' object does not support item assignment
In [115]:
a = 124            # число
b = str(a)         # можно число преобразовать в строку
print(a, type(a))
print(b, type(b))
124 <class 'int'>
124 <class 'str'>

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

In [116]:
a = int("1234")
print(a, type(a))  # помните строчку int(input()), там нет никакой магии, мы просто преобразовывали строку к числу
1234 <class 'int'>
In [117]:
a = input()
print(a, type(a))  # функция input просто считывает строчку до перевода строки, все очень просто
 <class 'str'>

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

In [118]:
a = "long string"  # у нас есть строка, мы хотим уметь ее изменять
print(a)

l = list(a)        # сделаем из строки список символов
print(l)

l[2] = "!"         # изменим один элемент списка
print(l) 
long string
['l', 'o', 'n', 'g', ' ', 's', 't', 'r', 'i', 'n', 'g']
['l', 'o', '!', 'g', ' ', 's', 't', 'r', 'i', 'n', 'g']

В предыдущем кусочке мы научились преобразовывать строку к списку символов и изменять один символ. Теперь нам нужно научиться из списка символов делать одну большую строку.

Для этого в Python у строки есть метод join.

s.join(l) - вернет строку, в которой все элементы списка $l$ записаны через разделитель(строку) $s$.

In [119]:
print("#".join(["1", "2", "3"])) # запишет "1" "2" "3" через разделительную строку "#" 
1#2#3
In [120]:
print("".join(["1", "2", "3"]))  # запишет "1" "2" "3" через пустую разделительную строку 
123

Способом выше как раз и делается, что нам нужно. Давайте теперь целиком напишем программу, которая меняет в считанной строке второй элемент на символ "!".

In [121]:
s = input()
l = list(s)
l[1] = "!" # первый символ имеет индекс 0, второй символ имеет индекс 1
s = "".join(l)
print(s)

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-121-84439fb6de49> in <module>()
      1 s = input()
      2 l = list(s)
----> 3 l[1] = "!" # первый символ имеет индекс 0, второй символ имеет индекс 1
      4 s = "".join(l)
      5 print(s)

IndexError: list assignment index out of range

Также пока мы не умеем выводить список строк через пробел, это так же делается с помощью метода join.

Как можно по-другому изменить третий символ в строке?

In [122]:
s = input()
s = s[:2] + "!" + s[4:]
print(s)
!

Есть метод, противоположный методу join, --- это split, он разбивает строку по разделяющей строке и возвращает список строк.

In [123]:
print("1#2#3".split("#"))          # разобьет строку по символу "#"
['1', '2', '3']
In [124]:
print("1 2 3        fasd".split()) # split без параметров разбивает строку по пробельным символам
['1', '2', '3', 'fasd']
In [125]:
print("1abc2abcd3".split("abc"))   # строка-разделитель может стоять более чем из одной буквы
['1', '2', 'd3']

9.Генераторы списков

Иногда возникает необходимость создать список с определенными значениями элементов. Например, предподсчитать квадраты чисел или получить список вида [1, 2, 3, 4, 5, 6, ...], или даже просто получить список заполненный нулями.

Самый простой способ это с помощью метода append добавлять в конец списка нужные элементы, например как сделано в коде ниже.

In [126]:
l = list()

for i in range(10):
    l.append(i ** 2)
    
print(l)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

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

In [127]:
l = [i ** 2 for i in range(10)]
print(l)   # этот код делает то же самое, что и код выше
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Как можно попытаться запомнить конструкцию:

имя_списка = [то_что_будет добавляться в конец for переменная in что-то]

In [128]:
l = [0 for i in range(10)]  # список длины 10 из нулей
print(l)

l1 = [0] * 10  # списки можно как и строки умножать на число, но этот способ имеет свои особенности, лучше генератор
print(l1)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
In [129]:
l = [2 ** i for i in range(10)] # список длины 10 из степеней двоек
print(l)
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

На самом деле генераторы умеют "сокращать" еще более сложную запись. Посмотрим на пример ниже.

In [130]:
l = list()

for i in range(10):
    if i % 2 == 1:
        l.append(i ** 2)
    
print(l) # в l квадраты нечетных чисел
[1, 9, 25, 49, 81]
In [131]:
l = [i ** 2 for i in range(10) if i % 2 == 1]
print(l)
[1, 9, 25, 49, 81]

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

Также можно делать со списками и строками:

In [132]:
a = [i for i in [1, 2, 3, 4, 5, 6] if i % 2 == 0]
print(a)
[2, 4, 6]
In [133]:
b = [c for c in "abcdef"]
print(b)
['a', 'b', 'c', 'd', 'e', 'f']

10.Считывание

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

In [134]:
a, b = [int(i) for i in input().split()] # так считываются 2 целых числа из одной строки
print(a, type(a))
print(b, type(b))

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-134-f933ebf0b143> in <module>()
----> 1 a, b = [int(i) for i in input().split()] # так считываются 2 целых числа из одной строки
      2 print(a, type(a))
      3 print(b, type(b))

ValueError: need more than 0 values to unpack

Что происходит при выполнении этого кода:

  1. Вызывается функция input и считывается одно строка.
  2. Для считанной строки вызывается метод split и разбивает ее по пробелам. Наши числа как раз и были записаны через пробел.
  3. Метод split вернули список строк, после этого цикл в генераторе списка начинает работать и перебирать элементы.
  4. Перед попадание в итоговый список ко всем элементам применяется функция int.
  5. Происходит параллельное присваивание и переменной a присваивается значение первого числа в строк, переменной b значение второго числа.

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

In [135]:
s = input()

l = s.split()
print(l) # посмотрим, что получилось

ans = list()
for el in l:
    ans.append(int(el))
print(el)

a, b = ans

print(a, type(a))
print(b, type(b))
[]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-135-c414dc5d1a35> in <module>()
      7 for el in l:
      8     ans.append(int(el))
----> 9 print(el)
     10 
     11 a, b = ans

NameError: name 'el' is not defined

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

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

In [136]:
l = [int(i) for i in input().split()]
print(min(l))

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-136-db2fc79c334a> in <module>()
      1 l = [int(i) for i in input().split()]
----> 2 print(min(l))

ValueError: min() arg is an empty sequence

На самом деле все то же самое можно было написать в одну строчку!

In [137]:
print(min([int(i) for i in input().split()]))

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-137-c38255cde25b> in <module>()
----> 1 print(min([int(i) for i in input().split()]))

ValueError: min() arg is an empty sequence

Если вы хотите убрать вокруг строки лишние пробельные символы (что вполне может подаваться на входе), то вам понадобится функция .strip(), применяемая к строке. Рассмотрим пример:

In [138]:
s = "   sdf kjk   sd   "
print(s.split()) # split без параметров умеет разбивать по нескольким пробелам
print(s.split(' '))
s = s.strip()
print(s)
print(s.split(' '))
['sdf', 'kjk', 'sd']
['', '', '', 'sdf', 'kjk', '', '', 'sd', '', '', '']
sdf kjk   sd
['sdf', 'kjk', '', '', 'sd']

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

In [139]:
a = [int(i) for i in input().strip().split()] # в данном случае strip можно и не писать, так как split без параметров
print(a)
a.sort()
print(a)
for i in a:
    print(i)
[]
[]

Другой вариант написания:

In [140]:
a = [int(i) for i in input().split()]
a.sort()
a = [str(i) for i in a]
print(a)
print(' '.join(a))
[]

Метод join требует на вход список строк, именно для этого мы и воспользовались генератором. То есть генератор здесь только для того, чтобы преобразовать список чисел в список строк. Посмотрите, это действительно так.

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

На самом деле нам хватает уже того, что мы умеем, разве что иногда хочется выводить числа не через пробел или не делать перевод строки после каждого вызова print. Это делается с помощью параметров sep и end. sep это строка, которую print вставляет между двумя элементами для вывода, по умолчанию пробел, а end это строка, которая записывает в конце после вызова print, по умолчанию это '\n', то есть перевод строки.

Посмотрим на несколько примеров.

In [141]:
print(12, 12, sep=":", end="")
print(45, 23)
12:1245 23
In [142]:
print(1, 2, 3, sep=", ", end="!")     # sep и end могут быть не односимвольными
1, 2, 3!

Заметим, что по стилю PEP8 после запятой ставится ровно один проблел, а перед --- ничего не ставится.

11. Import

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

Например, нам хочется поделить число $n$ на некоторое $k$ и округлить его вверх. То есть, например, 2.5 округлить до 3.

In [143]:
from math import ceil
n, k = [int(i) for i in input().split()]
print(n / k)
print(ceil(n / k))

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-143-c780f5dedf4a> in <module>()
      1 from math import ceil
----> 2 n, k = [int(i) for i in input().split()]
      3 print(n / k)
      4 print(ceil(n / k))

ValueError: need more than 0 values to unpack

Здесь мы использовали модуль math и импортировали (взяли, получили) из него только функцию ceil.
Импортирование модулей всегда пишется в начале программы. Еще различные варианты написания:

In [144]:
import math
print(math.ceil(5 / 3))
2

Здесь мы вынужденны писать <имя модуля>.<название функции>
А вот так писать совсем плохо:

In [145]:
from math import *

Такая запись означает: импортируй из math все, что там есть . Чем это плохо?

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

Приведем еще примеры модулей и функций из них:

In [146]:
from math import sqrt
from math import floor

from random import randrange
from random import choice

print(sqrt(4), sqrt(9))     # квадратный корень из неотрицательного числа
print(4 / 3, floor(4 / 3))  # округление вниз

print(randrange(2, 10, 2))  # рандомноe число из последовательности (begin, end, step)
print(choice([4, 8, 2, 7])) # рандомное число из переданной последовательности
print(choice("asd"))
print(choice((-3, -7, -2)))
2.0 3.0
1.3333333333333333 1
8
7
a
-3
In [147]:
from math import sqrt

print(sqrt(-1))             # заметим, что здесь будет ошибка, так как sqrt работает только для неотрицательных чисел
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-147-e37050558a93> in <module>()
      1 from math import sqrt
      2 
----> 3 print(sqrt(-1))             # заметим, что здесь будет ошибка, так как sqrt работает только для неотрицательных чисел

ValueError: math domain error

12. И еще о PEP8

  • Не делайте строки длиннее, чем 79 символов. Если строка слишком большая, то разделяейте её с помощью заключения выражения в скобки:
In [148]:
a = ("sdfjsdjfksjflksjdklfjsf" + "sjdlfjksfgehrgldfoidg" + "sfsfd" + 
     "werwerkwer" + "jlsfdsdlf")
print(a)
sdfjsdjfksjflksjdklfjsfsjdlfjksfgehrgldfoidgsfsfdwerwerkwerjlsfdsdlf
  • В случае разрыва строки около бинарного оператора, оставляйте оператор на предыдущей строке:
In [149]:
var = (a * b + 
       5 * c)          # правильно
var = (a * b 
      + 5 * c)         # неправильно
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-149-e51def7138b1> in <module>()
----> 1 var = (a * b + 
      2        5 * c)          # правильно
      3 var = (a * b 
      4       + 5 * c)         # неправильно

TypeError: can't multiply sequence by non-int of type 'list'
  • Пишите import каждого модуля на отдельной строке.
  • Правильно расставляйте пробелы в таких ситуациях:
In [150]:
a = [ 1,3,  5 ,6 ]     # неправильно, пробел после запятой ровно 1, перед запятыми пробелы не нужны. Отступы после [ и
                       # и перед ] тоже не нужны
a = [1, 3, 5, 6]       # правильно
print(a[1])            # правильно
print(a[ 1 ])          # неправильно, пробелы около после [ и перед ] не нужны
print(a [1])           # неправильно, пробел перед [  не нужен
print (a[1])           # неправильно, пробел перед (. Как и для любого вызова функции

if a[1] == 1:          # правильно
    print("1")

if a[1] == 1 :         # неправильно, пробелы пробел перед : не нужен
    print("2")
3
3
3
3
  • Не используйте символы l, I, O как имена переменных. В некоторых шрифтах они похожи на цифры.
  • Нельзя использовать однобуквенные (кроме счетчиков для циклов), бессмысленные названия для имен переменных (если только в условии задачи не сказано явно, что задается, например, переменная n). Имена переменных должны состоять только из маленьких латинских букв. Несколько слов в имени переменной разделяются_символами_подчеркивания.
In [153]:
var1 = 5
var2 = 6
var3 = 9
# что такое var?
# за что она отвечает?

name = "Vasya"
people_count = 23
  • Имена констант должны состоять только из заглавных букв. Несколь- ко слов так же разделяются символами подчеркивания.
In [154]:
NAME = "Andrew"
SCHOOL_NAME = "LKSH"

Кажется, на этом хватит:)

In [ ]: