Kodomo

Пользователь

Введение

Адаптационная часть этого курса посвещена тому, чтобы дать вам представление о том, что такое программирование вообще (и о том, что это не страшно, не сложно, и заодно ещё иногда и прикольно). Делать это мы будем на примере языка Python.

wtf python?

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

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

python 3 vs python 2

С языком python связана одна печаль: сейчас широко распространены две версии языка – 2я и 3я. В глубине своих недров это два сильно разных языка, но на первый взгляд они кажутся почти одинаковыми. И, на наше счастье, в тех рамках, в которых мы его изучаем в адаптационной части курса, переключиться с python2 на python3 очень легко (нужно только запомнить, что в одном месте нужно ставить одну лишнюю пару скобочек), а вот в обратную сторону сложнее, там придётся отказываться от кучи удобных конструкций.

Пока что мы будем изучать язык python2.

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

Задание 1: установить себе интерпретатор языка python

Очень просто: нужно пойти на сайт http://python.org, и найти слева ссылочку windows installer. (NB: таких ссылочки там две, нужно угадать – используя те знания, которые у вас уже есть – какая из них вам годится).

В инсталляторе можно на все вопросы отвечать Next и не задумываться.

Замечание

Начиная с этого момента в тексте много фрагментов кода на питоне. При виде КАЖДОГО фрагмента кода вы обязаны взять его, вбить в питон, и посмотреть, что питон ответит. Текст написан из предположения, что вы так и делаете.

Иначе у вас будет прекрасное ощущение, что вы всё понимаете до тех пор, пока в какой-то момент оно не сменится ощущением, что десять страниц назад вы всё понимать перестали.

Один из девизов программирования: практика – критерий истины!

Калькулятор

Язык python настолько простой, что много из него вы уже знаете:

   1 print 2 * 2
   2 print 4 + 5 * (3 - 4)
   3 print 2 ** 8
   4 print abs(-3)

Эти команды очевидны и пояснений не требуют.

   1 print 5 / 2

Когда питон имеет дело с целыми числами, он считает, что и ответ вы хотите получить в целых числах, поэтому он отбрасывает дробную часть.

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

   1 print 5.0 / 2

Ещё одна операция с числами – остаток от деления. Для тех, кто забыл школьный курс (а он и вправду давно был, и эта часть никому не была нужна), напоминаю, что если мы скажем питону a / b * b, то он сначала поделит a на b, отбросит дробную часть, потом помножит получившееся число на b, и в результате получит число, которое может оказаться меньше, чем a. А вот то, чего ему недостаёт, чтобы стать обратно числом a, и есть остаток от деления. Например, про 10 / 3 питон нам скажет, что ответ 3. Но 3 * 3 будет 9, а не 10. Недостающая 1 – это остаток от деления 10 на 3. Пишется в питоне это так:

   1 print 10 % 3

Переменные

Иногда удобно запоминать промежуточные результаты. Для этого в питоне есть операция под названием "присвоение значения в переменную". Пишется она так:

   1 a = 7 * 9

Очень важно понять, что эта запись не имеет ничего общего со знаком равенства, который был в математике. В отличие от математики, это не утверждение о том, что да, эти два значения равны, а команда положить то, что справа, в переменную, которая слева. В отличие от математики, в питоне нельзя менять местами левую и правую стороны этой команды (если мы напишем 7 * 9 = a, то питон поймёт "положить в переменную 7 * 9 величину a... что? нет, вы чего-то противоестественного от меня хотите syntax error syntax error abort panic die!!!").

Второе важное замечание состоит вот в чём: на самом деле, эта команда значит "сделать так, чтобы в переменной a лежало значение 7 * 9". А если там что-то лежало до этого, выкинуть его (безвозвратно).

   1 a = 7 * 9
   2 print a
   3 a = 42
   4 print a

Как вы видите, для того, чтобы вынуть значение из переменной, нам нужно написать её имя. И в обратную сторону, всякий раз, когда мы видим просто какое-то имя переменной в тексте программы (справа от "=", если в этой строке есть "="), то это нужно читать так: "сделать то-то с тем, что сейчас лежит в переменной".

   1 b = a / 6
   2 print b + 3

Строки

Ещё мы в питоне можем работать с кусочками текста. Кусочек текста называется строкой ("string") и пишется в кавычках – одинарных или двойных.

   1 print "hello"
   2 a = "world"
   3 print a
   4 print "hello, " + a
   5 print " :) " * 10
   6 print "1"

Объекты

Итак, мы знаем, что в питоне мы можем в переменную класть строку или число. Общее название того, что мы можем положить в переменную – объект.

Мы можем заметить, что разные объекты ведут себя по-разному:

   1 print 1 + 2
   2 print "1" + "2"

Это значит, что это два объекта разного типа.

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

Например, у строк есть метод "replace", которому мы говорим, что (какое слово) на что в строке заменить:

   1 x = "hello"
   2 y = x.replace("l", "LL")
   3 print y

Мы можем написать и так:

   1 print "hello".replace("l", "LL")

В питоне есть специальная функция help, которая скажет про объект, какие у него есть методы, и что они делают (если авторы объекта утрудились об этом позаботиться):

   1 help(1)

Одно важное исключение про функцию help состоит в том, что хелпы про строки нужно узнавать про слово str (иначе питон пытается использовать текст строки как название темы, про которую вы хотите прочитать хелп):

   1 help(str)

Пока что только этот хелп и содержит интересные нам знания. Из него мы можем узать, что у строк есть большая гора полезных методов capitalize, который делает большой первую букву строки, а остальные маленькими, upper и lower, которые делают все буквы строки большими или маленькими, strip, которая отрезает лишние пробелы по краям, и большое море всего полезного... (Мы ещё не один раз будем смотреть пристальнее, что питон умеет делать со строками).

Замечание для бесстрашных и любопытных. На самом деле, когда мы пишем a + b, питон, прежде, чем что-либо об этой строке подумать, превращает её в a.__add__(b). Отсюда в хелпах так много функций с подчёркиваниями в именах. И отсюда и следствие, что на самом деле, вся разница поведения объектов – это разница того, какие у них есть методы и что они делают.

Слайсы

Из строк можно вырезать кусочки:

   1 x = "abcdef"
   2 print x[3]

Это операция взять 4-ую букву из строки. Называется "индексация".

Важное замечание: в питоне нумерация букв в строке идёт с нуля, а в русском языка с единицы. В этом месте может возникать путаница, поэтому я буду всегда придерживаться очень строгой терминологии: взять n-ую букву – это по-русски, а взять букву с номером n или букву с индексом n – это по-питонски. То есть четвёртая буква – это буква с индексом 3. А прибавлять или вычитать единичку в этом случае вам придётся в уме.

Ещё можно из строки взять несколько букв:

   1 x = "abcdef"
   2 print x[1:4]

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

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

Предположим, мы хотим в строку, между 3-ей и 4-ой буквой воткнуть "-". В питоне готовой операции для этого нет (что, к слову, печально). Поэтому нам придётся взять всё до 4-й буквы исключительно, приклеить "-", и приклеить то, что идёт с 4-й буквы включительно. И в такой формулировке это оказывается очень просто:

   1 x = "abcdef"
   2 print x[0:3] + "-" + x[3:len(x)]

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

Дабы мы над этим не задумывались, в питоне есть полезное сокащение: если мы какое-нибудь из чисел вокруг двоеточия не указываем, то для питона это значит что мы хотели в эту сторону пойти до конца:

   1 x = "abcdef"
   2 print x[:3] + "-" + x[3:]

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

   1 print x[:3] # первые 3 буквы

Ещё одна полезная особенность питона – в нём буквы можно в строке считать не только с левого края, но и с правого. Крайная правая буква обозначается индексом минус один, вторая минус два, и т.д:

   1 print x[-1] # последняя буква строки

Модули

Если посмотреть на сайт питона в раздел Library reference (который обозначен очень точной пометкой "keep this under your pillow"), то мы обнаружим, что питон умеет работать с неимоверной кучей чего – начиная математики и скачивания веб-страниц до работы со звуковыми файлами и базами данных. (И ещё больше возможностей есть в библиотеках, которые созданы другими авторами и распространяются отдельно).

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

Прежде, чем пользоваться модулем, мы должны питону сказать, что мы хотим им пользоваться. Например, есть в питоне модуль "math":

   1 import math

На самом деле, после того, как мы проимпротировали модуль, модуль представляется как объект, лежащий в переменной. Ииииии – мы можем узнать, какие у этого объекта есть методы!!!

   1 help(math)

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

   1 print math.sqrt(4)

Подготовка к домашнему заданию

У нас будет задание сделать кусочек простейшей (но тем не менее забавной – особенно, если сделать это своими руками) текстовой анимации. Хочется сделать программу, которая показывает букву o, бесконечно бегающую от одной "|", до другой.

Мы умеем редактировать строки и мы умеем выводить их на экран. Мы не умеем делать так, чтобы это происходило бесконечно.

В следующий раз мы разберёмся, как и почему она работает, а сейчас мы выучим волшебную конструкцию:

   1 while True:
   2     print "hello"

Эта программа бесконечно печатает слово "hello". Конструкция while True говорит питону, что следующий за ней сдвинутый направо кусочек кода нужно повторять до бесконечности. То, что print сдвинут направо, играет существенную роль – иначе питон не знает, к чему относится while True, а к чему нет. (Умный текстовый редактор IDLE сделает в этом месте отступ за вас, и вам не нужно будет об этом беспокоиться).

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

Если мы напишем:

   1 while True:
   2     print "|"
   3     print "/"
   4     print "-"
   5     print "\\"

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

\

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

   1 print "O'Brian"

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

   1 print 'said: "hey!"'

Ровно за этим и нужно два типа кавычек в питоне. А вот если мы напечатать всю эту фразу целиком, у нас возникают проблемы. И чтобы мы всё же могли это сделать, нам в помощь дали волшебный символ \, который внутри строки воспринимается не как буква, а как команда не интерпретировать то, что за ним идёт, как закрывающую кавычку строки. Пользуясь им, мы можем написать:

   1 print 'O\'Brian said: "hey!"'

Немного менее симпатично, зато работает!

Но тогда следующий вопрос: а как нам получить в строке именно букву \? Ответ: для этого её нужно защитить с помощью \. Вот и получается: print "\\". (И если мы хотим вывести на экран два обратных слэша, то это нужно будет сказать print "\\\\" – каждый из них нам придётся защищать по-отдельности. Да, это страшно. Но мы научимся с этим в питоне бороться, да и требуется обратный слэш в жизни нам редко).

Теперь у нас возникла ещё одна проблема: для нашей задачи нам нужно после того, как мы написали строку, вернуть курсор в начало строки, но не опускать его вниз (что делает print). Для этого есть специальный символ "\r". Например:

   1 print "Hello\rworld!"

Мы увидим только слово world! потому, что мы напечатаем Hello, вернём курсор в начало строки и напечатаем поверх него слово "world!".

sys.stdout.write

Одна печаль, что print нам всё равно насильно опускает курсор на следующую строку, и даже если мы напишем:

   1 while True:
   2     print "|\r"
   3     print "/\r"
   4     print "-\r"
   5     print "\\\r"

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

   1 import sys
   2 while True:
   3     sys.stdout.write("|\r")
   4     sys.stdout.write("/\r")
   5     sys.stdout.write("-\r")
   6     sys.stdout.write("\\\r")

Важное техническое замечание: похоже, что IDLE неправильно трактует символ "\r" (негодяи!!!). Поэтому нам нужно будет запускать программу по-другому: нужно найти то, куда мы её сохранили, и, если мы её сохранили с расширением ".py", то просто дважды щёлкнуть по ней мышкой. (А если нет, то сохранить как положено).

(Увы, пока что нам придётся без пояснений писать вот эту конструёвину с двумя точками, которая вроде и похожа на метод, а вроде и нет. Хитрые люди могут попробовать посмотреть хелпы про sys и про sys.stdout, и чего-нибудь для себя понять. Особенно, те из них, которые общались с языками C или C++).

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

Ещё раз про присваивание

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

   1 y = 1
   2 y = y + 1

Здесь нам нужно вспомнить, что = – это операция "посчитать то, что от меня справа и положить в переменную, которая от меня слева". То есть питон сначала смотрит на правую чась выражения, и вычисляет её. В данном случае наша строка перепишется в y = 2 (с учётом, что до этого в y лежала 1), которая у нас уже не вызывает никакого удивления. Если мы эту команду повторим ещё раз, то теперь она будет значить y = 3. И т.д. То есть на самом деле эта команда читается так: положить в y число, на единицу большее, чем то, что лежало в y до сих пор. Или, ещё проще: увеличить значение y на единицу.

Если вдобавок вспомнить, что операция "остаток от деления на N" всегда возвращает число от 0 до N+1, то мы можем получить операцию: увеличить y на единицу, если y меньше N, или положить в y 0:

   1 n = 5
   2 y = 1
   3 while True:
   4     y = (y + 1) % n
   5     print y

Теперь мы можем, например, сделать, чтобы наша вертящаяся палочка, прихрамывала:

   1 import sys
   2 s = "|||||||/-\\\\\\"
   3 y = 1
   4 while True:
   5     y = (y + 1) % len(s)
   6     sys.stdout.write(s[y] + "\r")

А отсюда – если вспомнить наши упражнения про то, как запихать "-" в середину строки, уже недалеко и до буковки, которая бегает палочками :)

Бонус

А если мы хотим сделать так, чтобы наша анимация шла с какой-то относительно понятной скоростью, то в неё нужно добавить задержки. За то, чтобы воткнуть в программу задержку, в питоне отвечает функция time.sleep (которая обитает в модуле time).

Правда, к этому бонусу прилагается заморочка. Перед ним нужно втыкать sys.stdout.flush(), иначе всё будет работать не так, как хочется. Дело вот в чём: питон старается экономить операции выведения текста на экран потому, что это самая медленная операция относительно всех остальных. Поэтому когда мы его просим чего-нибудь написать, он это задерживает у себя в памяти до тех пор, пока не случится одно из двух: мы его попросили написать что-то с переносом строки или суммарно мы его уже попросили навыводить текста больше, чем некоторое количество. Питон не признаёт "\r" за перенос строки в этом смысле, поэтому он будет ждать, пока ему наберётся длинное полотно, чтобы вывести всё одним залпом. Вообще говоря, почти всегда такое поведение очень правильное и бережёт всем много сил и мозгов, но вот ровно в нашем случае оно нам мешает. И вот как раз для таких ситуаций есть sys.stdout.flush(), который приказывает питону вывалить на экран всё, что мы ему начали говорить, пусть даже питону кажется, что мы говорить ещё не закончили.

Литература