Prostoy Python
Prostoy Python
«солон»
Рик Гаско
Простой
Pythonпросто с нуля
Серия «Программирование»
Рик Гаско
Простой
Python
просто с нуля
Москва
СОЛОН-Пресс
2019
УДК 681.3
ББК 32.973-18
К 63
Рик Гаско
Простой Python просто с нуля. — М.: СОЛОН-Прссс, 2019.—
256 с.: ил. (Серия «Программирование»)
ISBN 978-5-91359-334-4
ООО «СОЛОН-Пресс»
115487. г. Москва,
пр-т Андропова, дом 38, помещение № 8, комната № 2.
Формат 60*88/16. Объем 16 п. л. Тираж 100 зкз.
4
Списки - короче, длиннее...................................................................... .93
Списки плюс циклы. Начало. И философия....................................... .96
Списки плюс циклы. Стандартные ситуации..................................... 101
Стандартные ситуации. Чуть сложнее.................................................. 104
Вложенные списки. Или многомерные, как вам больше нравится .. НО
Срезы. Всякие странности и экзотичности.......................................... 116
Кортежи - что такое и зачем. Очень коротко..................................... 118
Ещё раз квадратное уравнение.............................................................. 119
Глава шестая, короткая. Строки.......................................................... 122
Повторение пройденного и чем строки похожи на списки............... 122
Чем строки не похожи на списки.......................................................... 124
Методы строк........................................................................................... 125
Строки экранированные и неформатированные и кое-что ещё....... 127
Глава седьмая. Функции 130
Напоминание - что такое функция.................................................... 130
Функции. Сделать программу понятнее............................................... 131
Функции. Когда приходится повторять.............................................. 132
Функции. Когда у них есть параметры................................................. 133
Функции. О параметрах подробнее...................................................... 135
Настоящие функции................................................................................ 139
Ещё о функциях....................................................................................... 143
И ещё о функциях. Всякое не очень обязательное............................. 146
Скучное - глобальные и локальные переменные............................... 150
Функции с функциями............................................................................ 157
Функция, у которой много параметров................................................ 162
И опять. Квадратное уравнение............................................................ 163
Глава восьмая, короткая. Модули. Коротко..................................... 167
Постановка задачи........... ................... .................................................... 167
Решение задачи........................................................................................ 168
Что ещё важно знать............................................................................... 170
Наше любимое квадратное уравнение.................................................. 173
Глава девятая. Файлы............... 179
Что такое файл, вообще.......................................................................... 179
Шаг первый. Текстовые файлы. Теория............................................... 180
Шаг второй. Текстовые файлы. Запись................................................ 181
Шаг третий. Текстовые файлы. Чтение................................................ 183
Шаг четвёртый. Запись объектов в файл. Только для Питона......... 188
Глава десятая. Файлы бинарные 191
Запись и чтение бинарного файла.............. .......................................... 191
5
Как записать и прочитать строку............................................................ 196
Учебная задача........................................................................................... 199
Обобщаем и систематизируем............................................................... 201
Глава одиннадцатая. Графика............................................................... 204
Подготовительные упражнения. Параметры по именам.................... 204
Начинаем рисовать................................................................................... 205
Линии со смыслом.................................................................................... 207
Круги и прочие эллипсы.......................................................................... 215
Текст........................................................................................................... 221
Прямоугольники и многоугольники. В том числе и без углов......... 224
Картинки.................................................................................................... 227
Если хочется странного. Библиотека PIL..............................................232
Приложение Л. Консоль........ 233
Приложение В. Другие числа ......................................................... 23S
Приложение С. Можно ли сделать ЕХЕ-файл?------ ......--------------- 242
Приложение 1). Философия Питона....................................... 243
Приложение Е. Все системы счисления на трёх страницах........... 245
Приложение F. Всё о битах и байтах................................................... 248
Приложение G. Обмен данными с другими программами
через файлы............................................................................................. 252
6
Вступление
О чём эта книга?
Объяснение в лирической форме
Попа! мужик под паровоз. Вылечили его. Лег он дома спать, а жена на
кухне чайник поставила. Чайник вскипел — и давай свистеть. Мужик
вскакивает и топтать чайник, топтать!
Жена офигела:
— Ты чего, Федя?
— Убивать их надо, пока маленькие!!! С Народное
7
Ещё я написал хорошую книгу по языку программирования Паскаль. Это
был учебник не только языка, но и программирования, с самого начала, для
тех, кто не знает вообще ничего, ни о Паскале, ни о Программировании.
Разумеется, у меня возникла мысль предложить Дорогому Издателю™ эту
же замечательную книгу, только заменив программный код на Паскале на
программный код на Питоне. Но нет, оказалось, что я на такую халтуру не
способен, и книга сама собой стала писаться с самого начала, заново, и
совсем другая.
Кое-что о Питоне
8
очень уважаемые имена. Процесс шёл вполне предсказуемо. Я скачивал
собственно программную часть - компилятор, оболочку,
интегрированную среду, библиотеки и что там ещё бывает. В комплекте
шли какие-то инструкции по применению и встроенная справка и
контекстная помощь или как оно теперь называется. Потом, само собой, я
искал учебники и руководства, для начинающих и для продвинутых.
# исторический момент
Летом 1942 года по просьбе американцев Советский Союз передал им для
изучения танк Т-34. Его испытывали на Абердинском полигоне. Танк этот
на тот момент был, безусловно, лучшим танком в мире. Однако были
нюансы. Производили танк тогда только на двух заводах, один из которых
- Харьковский, к тому моменту давно уже был захвачен немцами, теперь -
украинцами. Второй завод, Сталинградский, ещё работал, но под
немецкими бомбами. Производство танков стремительно перемешалось в
Сибирь. Качество было вполне ожидаемым и предсказуемым.
Хотя у американцев на тот момент своих танков почти и не было,
советский танк был подвержен самому критическому разбору. Что-то
похвалили, что-то поругали. Но вот воздушный фильтр, заявили
американцы, наверняка проектировал вредитель!
А вы что думаете, зря этих вредителей в 37-м году пачками расстреливали?
# конец Исторического момента
9
фильтр на Т-34. Хотя, меня внезапно посетила внезапная мысль, может они
и не вредители? Кстати, поищите в Яндексе по словам анекдот может
она телеграмму не получила? Может, они нс вредители, а им просто денег
не платили? Может, они просто работали бесплатно? Это я не про Т-34, это
про бесплатные языки программирования. Обдумайте.
10
Что, кроме этой книги, почитать?
Само собой разумеется, даже если эта, моя, книга - лучшая в мире книга по
Питону, надо обязательно прочесть что-то ещё. Любая, даже самая лучшая
книга, даёт взгляд - или проекцию - только с одной стороны. 11еплохо бы
посмотреть и с другой и составить объёмную картину. Так что дальше
несколько книг для рекомендованного прочтения. Точнее, несколько
авторов, потому что названия у всех книг сходные до полной
неразличимости. Ещё одно обшее у авторов - они повторяются. Не в
смысле пишут одно и то же в разных книгах. В смысле поворяют одну и ту
же мысль на протяжении книги несколько раз немного разными словами,
чтобы лучше дошло до читателя. Можно, я тоже так буду?
Так вот, я очень советую прочесть его книгу Изучаем Питон (Learning
Python). Эго самое лучшее введение в язык, хотя местами я бы
подсократил. Кроме того, Лутц всё время сравнивает Питон с другими
языками, причём всегда в пользу Питона. Как-то это неспортивно.
Впрочем, лично мне это не обидно, потому что обычно сравнивает он с
C++, не с Дельфи. Л C++ я не люблю. Кстати, кто бы объяснил, почему на
обложке его книги нс змея, а крыса?
11
Ещё один автор - Сузи Роман Аврсвич. ООП изложено хорошо. Всё
остальное тоже хорошо, но только ни разу не для начинающих. По стилю
напоминает справочник. Справочники я уважаю, но начинать знакомство с
предметом лучше с чего-то написанного доступнее.
Питон уже где-то рядом. Если хотите писать реальные программы под
реальный Windows, то это читать обязательно.
12
Величайший программист всех времён и народов, кроме шуток. Объектно
Ориентированное Программирование он не очень любил. В смысле,
совсем его не любил.
Нет, ничего не надо! Эта книга именно для тех, кто программировать не
умеет, от слова вообще. Как я уже упомянул - сам не похвалишь, никто не
похвалит - я уже написал очень хороший учебник по Паскалю. Так вот,
ваше знакомство с этой моей замечательной книгой не предполагается,
точно так же и нс предполагается знакомство с программированием
вообще.
Если вас интересует Питон сам по себе, то на этом всё. Если вы думаете о
связи вашей программы с другими программами - не важно, через файлы,
по сети или непосредственным обменом данных через память - то надо
чётко понимать, как переменные устроены внутри. Если оставаться внутри
Питона, то это знание лишнее, а если вы хотите в дальнейшем выйти за его
пределы, то знание является обязательным, Лишнее я вас учить не
заставляю, прочитайте два приложения к этой книге - о байтах и о
переменных.
13
Осознанный выбор,
нли
Информированное согласие
Весь смысл в том, чтобы вы чётко понимали, что вас ждёт в будущем. Тут
у нас в программировании препараты из вас делать не будут, но тем не
менее. Если вы хотите программировать на Питоне, то должны понимать,
действительно ли вы хотите программировать на Питоне. Очень неплохо
будет, если сначала вы бросите взгляд на Питон и другие языки и выберете
тот, который вам больше нравится. Этим мы сейчас и займёмся.
14
значение, только если надо отсортировать десятки тысяч чисел, да и то не
один раз. Зато она простая и понятная.
Rem заполнение
a(l) = 10
a(2) - 22
a(3) = 11
a(4) - 50
a(5) - 30
Rem сортировка
For i - 0 To n - 1
For j « 0 To n - 1-1
If a(j + 1) < a(j) Then
t ■ a(j)
a(j) • a(j + 1)
a(j ♦ 1) -
End If
Next j
Next i
15
2. Школьный алгоритмический язык. Придуман в 1980-е. Автор Андрей
Ершов, русский. Использована реализация в среде Кумир.
алг Сортировка
нач
. цел таб а(1:5)
. лог ок
. цел tmp
. цел I
. I заполнение таблицы
. all):-10
. а[2]:-22
. а(Э):-11
. а[4]:-50
. а[5]:»30
. | сортировка
. ок:-яет
, иц пока не ок
. . ок:=да
. . иц для i от 1 до 5-1
. . . если a[il > a(i+l]
.... то
................... tmp:=a[i)
................... all):-a(i*l]
................... a(i+l):=tmp
................... ok:яет
. . . все
. . «ц
. ku
КОМ
♦ отсортировать
ок ■ False
16
4. C++. Придуман - вопрос философский - 1972-1983. Авторы - вопрос
философский. Или Денис Ритчи и Кен Томпсон, американцы. Или Бьёрн
Страуструп, датчанин. Использован Turbo C++ 3.0.
int а 15);
int ok,tmp;
// заполняем массив
all] - 10; а(2] - 22; al3) - 11; а(4] - 50; а[5) - 30;
// сортируем
ок - 0;
while (!ок)
I
for (int i - 1; i < 5; i++)
(
ok = 1;
if (alii > a[i+ll)
(
tmp = a [ i ];
ali) - a(i»l];
ali+1) = tmp;
ok - 0;
]
]
)
const
N - 5;
var
a s array|1..N) of Integer;
ok : boolean;
tmp : integer;
i : integer;
17
a(1*1)
end;
•nd;
until ok;
Несколько почему
18
что в реальном программировании, в том, которое за деньги, всё не так.
Если вы в совершенстве выучили один способ, зачем учить другой? Но
если вы ещё не выучили ни одного, лучше попробовать изучить все.
# почему
Почему некоторый текст написан вот именно так, как написан вот этот. То
есть в этих вот решёточках? В Питоне решётками выделяются
комментарии, то есть такой текст, который на выполнение программы
никак не влияет, а пишется только для самого программиста. При
исполнении программы он игнорируется.
Вот и тут то же самое. Вы смело можете пропустить всё, что внутри
решёток, на понимание дальнейшего пропуск никак не повлияет. А для
чего это написано? Для расширения вашего кругозора и открытия перед
вами сияющих перспектив.
# конец Почему
Никакой игры слов, здесь речь действительно пойдёт о том, чего в моей
книге нет, но что я считаю действительно важным и заслуживающим
внимания.
19
Другая тема - итераторы, генераторы и прочие чрезвычайно полезные
возможности, резко сокращающие длину программы. Но тут такое дело -
сокращать не значит упрощать.
И ещё такое понятие, как исключения. В том или другом виде и под
разными названиями они существуют в большинстве языков
программирования с незапамятных времён. Это очень полезная концепция
и достаточно понятная. Но только я их не люблю и никогда ими не
пользуюсь. Ну не нравятся они мне, нс нравятся.
20
Глава первая. Начало
Где скачать и как установить
Сейчас на рабочем столе у нас должна быть вот такая иконка с вот такой
подписью.
21
и*
ЖХ.Е (lython
вШ)
program Nothing;
begin
end.
void main ()
t
)
22
Питон в этой части достиг полной гармонии с природой. Программа,
делающая ничего, сама по себе является ничем. Но сначала даже в Питоне
мы должны проделать несколько несложных манипуляций. Выбираем в
главном меню пункт File\New File. Получаем новое окно, тоже не очень
впечатляющее:
It Unfilled
ПГ
На что обратить внимание? На то, что вверху окна появилось имя файла в
совокупности с именем каталога. Каталог именно тот, в котором
установлен Питон. Пусть пока так и будет. Расширение у файла *.ру. Это
надо запомнить. Теперь возвращаемся к вопросу о том, как выглядит в
Питоне пустая программа. Л никак. Пустая, она и есть пустая. Другими
словами - пустая, ничего нс делающая программа, текста не имеет. Или
можно сказать и так - если у нас есть совершенно пустой файл, то в нём
содержится она, совершенно пустая программа, уже готовая к
употреблению. Считайте, что дальше эта секретная программа напечатана
- белыми буквами на белом фоне:
23
2.7.14 Slwll
He E4l -W Окид Otfix-s Mrdw h*
Python 2.7.14 (vS.7.14: 9U7193Sed, Sep 16 2017, 20:17:30) I
tel)] on Win32
Type ’copyright*, "credits’ or ’license () " for nore inform.
»>
RBSTART: C:\Python27\Nothing.oy
»> | 1
Встреча с ветераном:
-Дедушка, расскажите о Ленине.
- Захожу я в общественный туалет, а там Ленин. Он дела! то же, что и
мы, но как просто, как человечно! © простой человечный анекдот
Pass
24
Поговорим о комментариях. Комментарии - это, как легко догадаться,
когда что-то комментируют. То есть написали вы программный код и
боитесь, что через неделю и не вспомните, о чём там речь шла. И вы
оставляете пометку на память. С точки зрения языка, компьютера, эффект
от комментариев тот же, как если бы их нс было. То есть перед
выполнением программы все комментарии просто выбрасываются.
Другими словами - комментарии не для компьютера, комментарии для
вас. Примеры.
25
Программа, которая что-то выводит
Как результат, будет выведена в окне Оболочки вот зга самая строка, та,
которая в кавычках. Если быть точным, будет выведена та строка, что мы
набрали в кавычках, но выведена она будет без кавычек. Можно
усложнить программный код, совсем немного:
Иногда бывает надо отделить одну часть вывода от другой, чтобы легче
было читать. Звёздочка десять в данном случае просто выводит строку
десять раз подряд.
# старческое разъяснение
Обе английские строки, эта и выше, из Альбова The Wall группы Pink Floyd
От 1979 года. Простые люди, вроде меня, считают этот альбом лучшим
альбомом всех времён и народов. Эстеты-извращснцы нс согласны и
предпочитают Dark Side of the Moon тех же авторов.
26
# конец Разъяснения
Что произойдёт при попытке просто и без затей вынести такую строку:
А почему? А потому, что Питон воспринимает, как строку, только ’we don',
после чего следует непонятный хвост и какая-то совсем ни к чему ещё одна
кавычка. Я обещал нс сравнивать Питон с другими языками, но иногда
можно отступить от своих правил. В традиционных языках вместо одной
кавычки в строке пишут две, вот так: 'We don"t need по education'.
Выводится в таком случае только одна кавычка. Питон против такого кода
возражать не будет, но отреагирует по-другому:
27
Однако! Внутри одинарных скобок совершенно свободно могут
употребляться двойные, а внутри двойных - одинарные. То есть:
print "'а'Ь'"
а'Ь
print 'abed','efgh'
print 'abed'
print 'efgh'
abed efgh
abed
efgh
print 'abed',
print 'efgh'
abed efgh
Да. весь секрет в этой самой занятой в конце первого оператора. Да, лично
мне это нс нравится.
28
print 'abed' + ' efgh'
abcdefgh
29
Нам предлагают
Tkinter
(module)
При выборе первого пункта мы попадем во встроенную справку и, пока
будем бродить по ней, неожиданностей нс будет. Впрочем, и узнаем мы
оттуда не слишком много. Если мы выберем вторую строку, то нам
предложат богатейшую информацию по Tkinter - но вся она будет уже
on-line.
30
Глава вторая. Переменные
Переменные вообще.
И нелые в частности
Dima = О
х - 3
print х
II о простоте Питона
Когда я говорю, что переменная возникла из ниоткуда, я имею в виду
следующее - в отличие от традиционных языков, нам не надо производить
с именем этой переменной какие-то предварительные манипуляции. Нам
не надо объявлять переменную - нам не надо уточнять, что в этой
переменной будет храниться именно число.
В питоновской переменной может храниться что угодно - число, строка,
список. Причём по ходу выполнения программы может меняться не только
значение переменной, что нормально и понятно, но и тип этого значения.
Разумеется, такой подход очень упрощает и ускоряет процесс внесения
ошибок в программу.
II конец Простоты
31
print X
3
X - 3
У - 2
Z = X ♦ у
print ’z ■ 1, z
Результат:
z • 5
х = 2
у - 3
z = х ~ у
print 'г - г
z - -1
х - 2
у - з
Z - х • у
print 'z - ', г
г - 6
х - 2
у = 3
г - х *• у
32
print ' z = z
z « 8
х * 23
У - 5
г • х / у
ost “ X % у
print 'z » ', z
print ’ost • ost
z • 4
ost “ 3
Косая черта делит нацело. Знак процента даёт остаток. Теперь решим не
очень сложную задачу, которая, хотя и кажется оторванной от жизни,
программистам довольно часто встречается. Есть трёхзначное число. Надо
разобрать его на цифры. То сел. на входе 321, на выходе 3,2.1. К решению
будем подползать мелкими шагами - сначала решим задачу попроще.
Пусть у нас на входе всего-навсего двузначное число. Разберём его на
цифры. Задача настолько проста, что сразу приведу решение:
х - 32
cl - X « 10
с2 ■ х / 10
print c2,cl
Пора ещё раз напомнить, что у переменной обязательно есть имя. Имя не
обязательно состоит из одной буквы, например - x,y,z. Как вы только что
увидели, имя может состоять из букв и цифр. Если совсем аккуратно - имя
может состоять из букв, цифр и знака подчёркивания, при условии, что
первым символом не будет цифра. Примеры правильных имён - х, у 123,
a_plus_b, _pi_na_tri. Примеры неправильных имён я приводить нс буду, вы
сами справитесь. Очень и очень важная особенность изучаемого нами
языка - большие и маленькие буквы отличаются и не одно и то же. То есть
х и X совсем разные переменные. Как легко догадаться, это служит
33
неисчерпаемым источником ошибок. Впрочем, ошибки эти легко
вылавливаются.
х - 321
cl = х % 10
с2 - (х / 10) % 10
СЗ - (х / 10) / 10
print c3,c2,cl
сЗ = х / 100
34
Однако первый вариант обладает неоспоримым преимуществом с точки
зрения математики - мы не решали задачу заново, а свели её к
предыдущей, уже решённой. Если есть возможность, то поступайте так
всегда. На самом деле, это совет не из области математики, это совет из
области программирования. Ещё одна деталь, на которую следует
обратить внимание, - скобки. Вроде бы всё понятно, но иногда в
математике, кроме обычных круглых скобок, используют и квадратные, и
фигурные. В других языках программирования при попытке их
применения у вас немедленно возникнут серьёзные проблемы - вам
сообщат, что вы использовали недопустимый символ. В Питоне всё будет
гораздо хуже. Он всё сжуёт, но результат, скорее всего, будет не совсем
тем или совсем не тем, что вы хотели. Дело в том, что в Питоне вполне
допустимы во многих случаях и другие скобки, кроме круглых. Поэтому
запомните - в вычислениях и в выражениях используйте только круглые
скобки.
А теперь об очень важном, что мы незаметно пропустили. Пока это для вас
нс очень важно, но там, потом, в реальной про1раммистской жизни
вырастет до гигантских, монстрообразных размеров. Мы предположили и
приняли на веру, что числа наши будут именно двухзначными или
трёхзначными. Если они такими не окажутся, то программа наша выдаст
неверный результат. А они такими не окажутся, просто по закону подлости
- он, закон этот, беспощадно работает. Поэтому в реальной, а не учебной
жизни, все данные на входе надо проверять.
Кстати, вопрос - вот есть у нас очень простая формула р = р . Это, если
Переменные дробные.
Или, по-программистски, плавающие
35
Сейчас будет длинное математическое вступление. Вы можете его
пропустить и перейти сразу к практической части, ничего не потеряете.
В чистой математике сначала рассматривают только целые числа. Не то
чтобы была ещё и какая-то грязная математика, так принято выражаться,
ничего личного. 11росто есть ещё и прикладная математика. Далее понятие
числа распространяют на рациональные числа, то есть числа, которые
можно представить в виде натуральной дроби, например
1 3 127 5 I
—- = — . Последнюю дробь можно сократить, но это
3 13 19 125 25
совершенно не важно. Потом в дело вступают иррациональные числа.
Иррациональное число - число, как ни странно, то, которое нс
рациональное. Иначе говоря - нс представимое в виде натуральной дроби,
например V2. Почему именно корень из двух? Потому что ещё древние
греки под командованием Пифагора доказали иррациональность этого
числа. И немедленно, от радости, принесли в жертву сто быков. Тут для
меня остаются нерешёнными некоторые вопросы, как то - чему
радоваться? - чем быки виноваты? - «по с мясом сделали? Далее надо
понимать один несложный момент. Вот есть у нас банка мёда, и добавили к
ней, скажем так, ложку дегтя. Вы знаете, что такое дёготь? Путаетесь в
показаниях? Это народная русская пословица. Ну пусть не дёгтя. Пусть
известно чего. Мёд уже не мёд. А если к ведру меда? Л если к бочке мёда?
Я понимаю, что в реальности никто не заметит, но пословица утверждает
иное.
# Философский вопрос
Есть куча песка. Мы отнимаем из неё одну песчинку. Осталась ли куча
песка кучей песка? Мы продолжаем отнимать из кучи по одной песчинке.
Когда куча перестанет быть кучей?
Как ни странно, вопрос этот был задан в мохнатые семидесятые в журнале
Техника - Молодёжи, и там же был дан на него вполне разумный и
обоснованный ответ.
Обдумайте.
# конец Философского вопроса
36
иррациональным, хотя всё в нём, кроме корня из двух, - рациональнее
некуда.
х - 7
У = 2
z = х/у
print 'z = ', z
z - 3
х - 7.0
у - 2
z = х/у
print 'z = ', z
z = 3.5
i - 2.3 + 1.2
print ' z - ', г
г - 3.5
Вычитание:
z = 1.234 - 3.99
print ' z = z
37
z = -2.756
z - з •• 2.5
print 'z “ ', z
z - г - 15.5894572681
х - 1
z = sin(x)
print ' z = ', z
х ■ 1
z • math.sln(x) I
print 'z = z
38
г - 0.841470984808
а - 2г ь - 3
print 'а = ',а, ' b - ',Ь
c,d - a,b
print 'с - ',с,1 d » ',d
а - 2 b - 3
с - 2 d - 3
Здесь мы видим целых два новшества. В первой строке сразу два оператора
присваивания, разделённые точкой с запятой, - так можно. Но это именно
два отдельных оператора присваивания. А дальше мы присваиваем в
одном операторе значения двум переменным сразу - тем двум, что слева от
равенства значения, тех двух, что справа. Никакой пользы, кроме вреда и
путаницы, я от этого не вижу, разве что в следующей ситуации:
а,Ь - Ь,а
print 'а - ',а, ' Ь - ',Ь
а - 3 Ь - 2
Строки
а - 'abed'
39
b - 'etgh'
s = a + b
print 's ” ', s
s = abcdefgh
а - ’abc 1
s = а * 3
print 'а - ', а
Ввод
Вывод уже был, теперь ввод- С глубокой печалью должен признать, что с
вводом данных не всё в Питоне хорошо и не всё в Питоне удобно. В
Паскале и других традиционных языках это реализовано проще. Но - а
куда ты денешься с подводной лодки © Анекдот. Кроме шуток - другой
системы ввода у меня для вас нет.
40
Что мы видим? Видим вот такую схему:
41
# конец Вставки
х ■ inttrawinputt'х ■“ '>)
у - int(raw input!’у - *>)
z • х“у
print 1Z - ', г
х - 2
у - 10
z - 1024
import math
print 'S = S
R - 2.5
S - 19.6349540849
Легко заметить, что место функции int заняла функция float, переводящая
строку в число с плавающей точкой. Ещё раз обращаю внимание на строку
42
inport math, позволяющую совершенно бесплатно использовать константу
я.
Игра случая
43
возможных ответов. Бывают собственно вероятностные задачи, где
требуется определить вероятность некоторого события. Чаще мы имеем
какое-то измеренное значение, на точность измерения которого влияет
много разных случайных помех. Надо определить диапазон, в котором на
самом деле наше значение находится.
import random
xl - random.randint(0,5)
х2 = random.randint(0,5)
хЗ " random.randint(0,5)
х4 = random.randint(0,5)
х5 = random.randint(0,5)
print xl,x2,x3,x4,x5
0 15 2 1
44
числами в Питоне устроена много лучше, проще и логичнее, чем в других
языках.
import random
х = random.randint(10, 99)
у ■ random.randint(10, 99)
print x, y,
raw input ()
print 'result is ', x«y
81 + 22 = ?
result is 103
import random
xl - random.random()
х2 « random.random()
print xl, x2
0.726732416853 0.967583829512
45
Однажды к товарищу Сталину пришёл высокопоставленный работник
Центрального Комитета, отвечавший за работу с творческой
интеллигенцией. Он стал жаловаться на писателей - пьют, уводят другу
друга жён, потом бьют морды и пишут доносы.
— Других писателей у меня для вас нет. - прервал его Сталин. -
работайте с тем, что есть. © Быль
import random
xl - random.random О
х_10 И - xl ♦ 10
х_0_100 = xl • 100
х_100_300 - х!*200 ♦ 100
print х_10_11
print х_0_100
print х_100_300
10.7360001241
73.600012407
247.200024814
46
Глава третья. Условный оператор
Что это такое
До сих пор наши небольшие программы выполнялись несколько
предсказуемо - от начала до конца. Если в программе было десять строк,
выполнение программы начиналось с первой строки и заканчивалось на
десятой. Такие программы просты и понятны, но, к сожалению, мало
полезны. Настоящие про!раммы выполняются заранее непредсказуемым
способом. Вот сейчас, мало-помалу, мы и начнём писать настоящие
программы.
if х > 0:
print <'x positive')
47
В каждой уважающей себя фирме есть документ, описывающий
стандартный вид программного кода, то есть сколько пробелов надо
вставить в следующей строке после условного оператора. Тех, кто не
подчиняется, наказывают - это только в хороших фирмах, разумеется. Но
всё это только для того, чтобы прО1раммный код легко читался и выглядел
красиво. Некрасивый самолёт не полетит, некрасивый код работает
известно через что. В Питоне всё очень по-другому - некрасивый код нс
заработает.
if х > о
then
begin
у:=х*х;
z:-y - 1;
if z div 2-0
then begin
ShowMessage('Всё пропало!'»;
end;
end;
Так вот, в Питоне так нельзя. Если вы испортите код вот таким образом
if х > 0:
print ('х positive')
48
it <УСЛОВИЕ»:
«ПРОБЕЛЫХДЕЛАТЬ ЧТО-ТО»
print x
x - 3
x positive
4
it х > 0:
print (*х positive')
х - х +1
49
отрицательная. Это потому, что теперь арифметический оператор
находится на одном уровне с условным, и выполняется независимо от него.
Потренируйтесь - поглядите на этот текст, и ответьте, чему будет равен X:
X - 1
if х > 0:
х = х + 2
х - х + 3
х - х + 4
if х > 9:
х - х - 10
print X
Ответ: ноль.
if х > 0:
if х > 10:
print •!''m here!'
if х > 0:
if х < 0:
if х >- 0:
if х <- 0:
if x — 0:
if x <> 0:
pass
50
равенства обозначает - никогда не догадаетесь - больше или равно.
Предпоследнее условие, два знака равенства подряд - всего лишь проверка
на равенство. Дело в том, что в Питоне символ просто равенства уже занят
под оператор присваивания. А последнее условие, знаки меньше и больше,
вместе обозначают не равно. Справа нс обязательно должно быть
конкретное число, можно и так:
if х о у:
print 'не равно!'
if х**2/а»*2 + у»*2/Ь**2 — 1:
if х » 13 — 0:
print 'Ok'
if х > 0:
if х > 10:
print "I'm here!"
if х > 0:
if х < 10:
print 'I"m here!'
51
I или так
if (х < 1) or (х > 9) :
Задумайтесь над тем, что любое сложное условие можно записать в разной
форме - как минимум в двух. Запись может меняться, а смысл остаётся
прежним. Пример двух равносильных условных операторов :
х - 1
Ok
Ok
if х > 1:
52
Пришлось думать. А можно и так, немного громоздко, но без напряжения
головного мозга.
Ещё проще, вот или налево. или направо - тут уже точно надо выбирать
что-то одно. Математическое или работает совсем по-другому. Оно
означает - или налево, или направо, или и то и другое вместе. То есть и в
ЗАГС, и к прокурору одновременно. Как страшно жить математикам.
Обдумайте. Пробудите свое воображение.
if х > 0:
print ('x positive'
if x > 0:
print ('x positive')
if x <- 0:
print ('x NOT positive')
53
Если вам кажется, что это слишком примитивно, то я с вами согласен.
Зачем вторая проверка? Вариантов всего два - или положительное, или не
положительное. Мы проверили число на положительность. Зачем ещё раз
проверять его на отрицательность, точнее нс положительность? Ведь если
первый ответ был да, то второй ответ будет нет, и наоборот. Встречаем
расширенную форму условного оператора.
if х > 0:
print (*х positive’)
•lee:
print ('x NOT positive')
if <УСЛОВИЕ>:
«ПРОБЕЛЫХДЕЛАТЬ ЧТО-ТО
Вариантов может быть нс три, как у нас. а больше - семь, восемь. Почему
нс больше? Потому что, если их больше, пора применять другие средства,
а не условный оператор, сколь угодно продвинутый. Об этом мы
поговорим позже. Проверка условия может быть весьма затратной по
времени, но это редко и нс главное. Главное то, что такой код-с многим))
условными операторами на одном уровне - очень способствует
размножению ошибок. Теперь вариант решения с привлечением новых
средств языка:
if х > 0:
print (’х positive')
•lif x < 0:
54
print ('x negative')
else:
print ('zero')
import random
х = random.randint<10,99)
у “ random.randint<10,99)
print x, ' + y,
z = intlrattinput ())
rightZ = x + у
if z =“ rightZ:
print 'you are Ok'
•Ise:
print 'something''з wrong'
print 'sum is ', rightZ
В общем и целом, всё должно быть понятно. Задумайтесь над тем, как
небольшое изменение алгоритма работы программы заметно меняет сё
текст.
55
Не очень сложное задание. Два
Есть целое число, это номер года нашей эры - например, 1937. Напишите
условный оператор, который определяет, является ли год високосным. Для
тех, которые думают, что високосный год - тот, который делится на
четыре, объясняю.
То есть:
1899 нет
1900 нет
1902 нет
1903 нет
1904 да
2000 да
Вот ещё одна, очень хорошая, годная задача на целые числа и условный
оператор. Кроме того, неплохо бы и подумать о спасении души. Задача
называется - вычисление даты Пасхи. Поскольку у нас тут сплошная,
извините, толерантность, то вспомним, что Пасхи бывают разные -
православные, католические и, прошу прощения, еврейские. Это важно.
Ещё тридцать лет назад, когда в книгах по программированию например
многотомнике Кнута - ставилась эта задача, в описании алгоритма
обязательно уточнялся важный нюанс.
56
Алгоритм есть в Википедии, но там он настолько подробно расписан на
чём-то, очень похожем на Паскаль, что даже неинтересно. Этот вариант я
взял с сайга mooseum.ru, у них ещё почему-то лось на заставке. Вот
алгоритм:
Далее понятно:
Для перевода иа новый стиль дату, как известно, нужно сдвинуть вперед на
13 дней в 20-м и 21-м веках.
© mooseum.ru
Я однажды условия этого задания уже написал. Эго было в моём учебнике
по Паскалю. Это моя первая книга и, с точки зрения Дорогого Редактора™,
лучшая. Потому что эта книга лучше всех продаётся. Сейчас я не буду
выдумывать ничего нового. Если вы ту книгу нс чи тали, то вам абсолютно
всё равно, что там было написано, а если читали - то вам будет интересно
сравнить Паскаль и Питон.
57
случай - корень уравнения, это такое число, при котором уравнение
обращается в равенство, то есть левая часть становится равной нулю.
Проще квадратного уравнения может быть только линейное, это такое, в
котором даже второй степени икса нет:
Зх-15 = О
2х2 + 5х + 2 = 0
58
Вот она, эта волшебная формула, хотя древние вавилоняне записывали её
клинописью на глиняных табличках:
— h±^Jb2 -4ас
хи=-------- 02а
--------- •
Быстро программируем.
А - 2; В - 5; С - 2
d = В**2 - 4*А*С
print А,В,С
print 'xl ■ xl, ' х2 = ', х2
2 5 2
xl - -0.5 х2 - -2.0
59
Продолжаем разговор. А что будет, если а = 0 ? Хочется немедленно
объявить, что корней нет, потому что на ноль делить нельзя. Это
неправильный ответ - это всего лишь означает, что наша формула нс
совсем подходит. Другими словами - это проблема формулы, а не
уравнения. Уравнение оказывается не квадратным, а линейным. То есть
уравнение ах1 + />х + с = 0 при х = 0 немедленно превращается в
—с
уравнение Ьх + с - 0 и мы легко находим его единственный корень х = —.
Ь
Третий абзац и третий случай. А что будет, если а = 0 и Ь-0? Что будет,
если, говоря языком формул, с = 0? Это зависит от того, чему равно с.
Если с* 0, то корней нет. Если равно нулю, то корнем нашего уравнения
является любое число, то есть - абсолютно любое.
2 5 2 Х| = -2 Х2=-0.5
2 4 2 Хи = -1
2 1 2 корней нет
0 3 6 Х|Д--2
0 0 3 корней нет
ООО тождество
if А о 0:
xl - 0; х2 - 0
• lif В о 0:
xl - 0; х2 - 0
xl - 0,- х2 - О
60
К чему эти нелепые присваивания иксам нулей? Сначала я хотел написать
в этих местах разумный комментарий и этим ограничиться, однако
оказалось, что синтаксис Питона этого не позволяет. Комментарий - это
как бы абсолютное ничто, а внутри условного оператора обязательно
должно что-то быть. Далее на вид незначительный, а по сути весьма
существенный вопрос - а как наша программа предоставит внешнему
миру результаты своей работы? Если бы она была функцией, то вернула
бы корни уравнения как список. Но мы, напоминаю, пока ничего не знаем
ни о функциях, ни о списках. Так что пока - корни будут просто
выводиться на экран. Приступим.
Как сообщить пользователю, что корней два, или один, или вообще нет?
Мы вернёмся к этому вопросу позже, обогащённые новыми знаниями, но
пока, на этом уровне знаний, просто введём текстовую переменную,
которая будет содержать ответ и которую можно будет вывести на экран.
И первая версия нашей программы приобретает вот такой вид,
незаконченный и не вполне полезный. Если вам стало скучно это читать,
то сейчас я учу вас не языку Питон. Я учу вас программировать вообще.
Всё сказанное относится к любому языку программирования.
if А О 0:
I квадратное уравнение, дискриминант вычисляем
xl - 0; х2 - О
elif В О 0:
t линейное уравнений
xl - 0; х2 - О
•Isa:
• особый случай
xl - 0; х2 - 0
61
А почему коэффициенты объявлены как переменные большими буквами, а
выводятся на экран как маленькие? А это чтобы напомнить вам, мои
маленькие друзья, что большие и маленькие буковки в Питоне это совсем
разные буковки.
if dis > 0:
xl = <-B+dis**0.5)/(2‘A);
х2 - (-B-dis*’0.5)/(2*А);
result = 'два корня'
• lif dis =- 0:
xl - (-В)/(2‘А); х2 - xl
result = 'один корень'
else: // дискрнминлнт меньше нуля - корней нет
xl - 0; х2 - 0
result “ 'корней нет’
xl - <-B+dis‘*0.5)/(2*А|;
xl - <-B-dls**0.5)/(2‘А)I
62
Теперь второй случай, случай линейного уравнения. Здесь всё просто. Или
кажется простым,
♦ линейное уравнение
х1 = -С/0; х2 = Х1
result = 'один корень'
А - 0; В = Ь/ С - 2
result « 'восОше ничего не понятно'
if А о 0:
t квадратное уравнение, дискриминант вычисляем
I это мы уже запрограммировали
•Ilf в о 0:
f линейное уравнение
х1 = -С/В; х2 = х1
а = 0 Ь = 5 с = 2
один корень
Х1 - -1 х2 » -1
63
Переменная в Питоне может иметь любой тип и менять его много-много
раз. Это удобно. Неудобно то, что вы должны тип переменной всё равно
иметь в голове и отслеживать, как он повлияет на выполнение вашей
программы. В нашем случае мы должны с самого начала задать исходные
данные немного по другому.
А - 0; В - 5.0; С - 2.0
а - О Ь - 5.0 с - 2.0
один корень
х1 - -0.4 х2 - -0.4
# особый случай
if С = 0:
х! - 0; х2 - О
result - 'тождество'
х1 - 0; х2 - 0
result = 'корней нет'
print result
if result -- 'два корня':
print xl,x2
elif result -- 'один корень':
print xl
else:
64
Чем плох этот вариант? Плох он гем, что в случае изменения текста одного
из сообщений вся схема рушится. Немного позже мы попытаемся это
исправить, а пока пусть будет то, что есть. А вы попытайтесь из этих
отдельных блоков собрать законченную и работоспособную программу.
65
Глава четвёртая. Циклы
Вступление и о главном.
Введение в цикл Гог
Если вам надо вырыть яму для вышеупомянутого строения, вряд ли вам
поможет шагающий экскаватор, лучше нанять группу лиц какой-то
национальности с лопатами. Если вам непонятно, о чём это я вообще, то
сейчас мы займёмся циклами. Что такое цикл? В простейшем случае - это
когда мы что-то одинаковое делаем несколько раз, причём абсолютно
одинаковое, вот гак, например:
print ’Stop!'
print 'Stop!'
print 'Stop!'
print 'Stop!'
print 'Stop!'
Stop!
Slop!
Stop!
Stop!
Stop!
Как-то скучно писать одну строку в программе пять раз, чтобы пять раз
вывести один и тот же текст. Чтобы избавиться от этой печальной
необходимости, циклы и выдуманы. Цикл, в простейшем варианте,
повторяет одно и то же. Пишем цикл:
for i in xrange(l,5):
print 'Stop!'
66
Всё понятно? Что, ничего вообще не понятно? Начинаем разбираться. Со
второй строкой более-менее ясно - это то, что мы хотим повторять. Первая
строка кончается двоеточием, а вначале второй строки сами собой
появились четыре пробела - точь-в-точь, как в условном операторе. И это
не случайно, логика н механизм работы те же самые.
Stop!
Stop!
Stop!
Stop!
Есть переменная цикла I, и, что важно, и я вас уверяю, внутри цикла она
всё время изменяется. Говоря по-другому, есть понятие шаг цикла, это
значит, что переменная цикла изменилась. То есть сначала i = I, а потом
1 = 2. Эго и еегь шаг цикла. Все эти рассуждения ведут к одному - а не
попробовать ли нам вывести переменную цикла I внутри этого самого
цикла? Выводим.
for i In xrangeil,5):
print 'i - ', i
i - 1
i - 2
i - 3
i = 4
67
Итак, если мы явно и чётко пишем (1,5) - цикли выполняется только от
единицы до четырёх, то есть - для последнего числа он нс выполняется.
Это надо запомнить.
Нам пока не надо всё - нам нужно самое простое применение цикла, для
многократного выполнения совершенно одинаковых или очень похожих
действий. Чтобы продвинуться дальше, зададим себе вопрос - что
означает загадочное, отсутствующее в американском языке слово xrange?
Вместо xrange можно написать и просто range, по-английски - диапазон,
так уже понятнее. То есть rangc(l,5) означает числа в диапазоне от 1 до 5.
но! Последнее число в диапазон нс входит, вы ведь уже запомнили это.
правда?
68
Если вам хочется, можно всегда писать просто range и не задумываться ни
о чём. хгапдв делает в точности то же самое, но при этом экономит память.
Если вы закажете цикл от единицы до миллиона, то, скорее всего, для его
выполнения будет затребовано несколько мегабайт памяти - это если
использовать просто range. А еСЛИ хгапде - ТО ИСТ.
Конечно, для наших игрушечных программ это совсем неважно.
# конец Примечания
start ■ 1
stop = 2
for i in xrange!5,l):
print •i • •, i
А ничего не будет . Цикл выполнится ровно ноль раз. Причина в том, что по
умолчанию, если не задано иного, переменная цикла увеличивается на
единицу на каждом шаге цикла. Или, говоря иначе, шаг приращения цикла
равен плюс единице. Эго даёт нам некоторую свободу маневра - если
можно плюс один, наверное, можно и минус один? Совершенно
правильно, можно и минус один, вот так:
i - 5
i - 4
i - 3
i = 2
69
tor i in xranga -10,-1,+4):
print 'i = ', i
Сколько раз и для каких значений переменной цикла I будег выполнен этот
цикл? Знак плюс здесь исключительно из педагогических соображений, он
ничего не меняет. Правильный ответ:
1 - -ю
1 - -6
i = -2
for i in xxange(5) :
print i, ’* 2 - i‘*2
0*2- 0
1*2= 1
2 * 2 - 4
3*2- 9
4*2= 16
1
2
3
4
for 1 in xranga(5,1,-1):
70
print i
5
4
3
2
1 1 1
2 4 8
3 9 27
10 100 1000
for 1 in «range11,11):
print i,
print i**2,
print i**3
1 1 1
2 4 8
3 9 27
10 100 1000
71
неизменная величина. Как посчитать сумму первых N членов прогрессии?
Очень просто. = -+а-&п. Однако мы сделаем вид. что этой формулы
<=| 2
не знаем. Посчитаем сумму тупо и в лоб - а потом проверим по этой самой
формуле. План работ такой:
ввести N
посчитать сумму через цикл
вывести
посчитать сумму по формуле
вывести
сравнить
sumlter • О
for 1 in mangaI1,N+1):
sumlter = sumlter + i
N - 4
10
if sumlter -- sumForm:
print 'Ok'
•Isa:
print 'very, very bad!’
N - 3
sumlnter = 6
sumForm • 6
Ok
72
Всё хорошо, для числа три. Упростим задачу до невозможности -
посчитаем сумму для двух членов.
N = 2
sum’nter = 3
и uniform - 2
very, very bad!
N - 2
sumlnter - 3
sumForm = 3.0
Ok
Это ещё одна стандартная задача, с той только разницей, что простого пути
для её решения до сих пор нс придумано, только непосредственно
вычислять. О чём речь? Сумму арифметической последовательности
можно вычислить непосредственно. Если для вас это скучно и раздражает,
можно вычислить по короткой формуле. Следующую задачу можно
решить только в лоб, короткой формулы нет. Задача называется расчёт
факториала. Что такое факториал? А!=1х2хЗх...хА - очень просто. И
программа очень простая:
73
N ■ in t<ra«r_ input: 'N - '))
F - 1
for i in xrange<2,N+l):
F - F * i
print N,*! - F
N - 6
6 ! - 720
74
памятник этому великому писателю. Зато его очень любят голуби,
памятник, я имею в виду. Па нём всегда сидят голуби. Самый главный
голубь сидит у писателя на голове. У него - у писателя - есть роман про
нехороших людей - «Господа Головлёвы». Самый плохой из нехороших -
Порфирий. Когда он родился, то получил в подарок 100 (сто) рублей, но
маменька заныкала и растратила. Порфирию сейчас пятьдесят. Он
напрягся и высчитал, что если бы маменька положила сто рублей в
ломбард (или банк) под проценты, то за пятьдесят лег они превратились бы
в восемьсот. Перельман спрашивает, сколько процентов годовых тогда
платили такие учреждения. Юридическая и финансовая стороны понятны.
Как легко заметить, за три года, кроме трёх рублей, набежали ещё и
копейки. Ясно, что если начислять будут не 1%, а больше, лишних денег
набежит больше. Хотя, конечно, денег лишних не бывает. Как мы этой
программой будем пользоваться? Очень просто - запустим для 1%,
посмотрим на результат, наверняка будет мало. Запустим для 10% -
наверняка будет много. Так, постепенно, и подберем правильный ответ.
75
Всё должно быть ясно. Количество бессмысленно прожитых лет вводится
как целое, потому что оно обязано быть целым - ведь оно управляет
циклом. Диапазон цикла, разумеется, от 1 до numYears«i.
sumAfter - sumBefore
1» 164.43
10% 11739.08
2% 269.16
3% 438.39
4% 710.67
5% 1146.74
Ещё в этом разделе будет довольно много рассуждений о том, как должна
быть организована программа. Много, разумеется, только для того уровня
знания языка, который я успел до вас донести.
76
Языки программирования без переменных редко, но случаются. Лично я
сходу вспомнил LISP, на котором немного программировал и APL,
которого не видел даже издалека. Да и то, переменных не было только в
исходном, расово чистом Лиспе, а потом что-то подобное всё-таки
завелось. В APL, переменные как бы были, но специфические. Так вот, во
всех традиционных языках программирования переменные есть. Питон -
язык нетрадиционный, но переменная в нём тоже есть. В чём подвох?
Если иметь в виду только то, что мы уже знаем о Питоне, то картина имеет
некоторое сходство. Я честно рассказал о целых, плавающих и строковых
переменных. И вам показалось, что они есть. Я вас обманул - их нет! Да, то
есть нет, нет в Питоне переменных каких-то там типов. Есть просто
переменные, без типа. Что это значит? Вот очень простой, несложный
прщраммный код:
integer - 44
single = 3.14
string - ‘Мама мыла раму*
integer - 44
77
single - 3.14
string = 'Мама мыла раму'
print Integer
print string
ЗапрО1раммирусм.
al - float(rawinputt'al = '))
bl - float (raw inputCbl - '))
a2 - float(rawinput('a2 - '))
b2 = float(rawinputt'b2 - ’))
SI = (al*bl)/2
S2 = (a2*b2)/2
78
Проверьте - программа считает правильно, по формуле. Есть переменные,
которые мы вводим, - и так далее. Есть переменная, которую мы
выводим, - V. А ещё есть переменные S, и S,. Это то, что обычно
называют промежуточные или временные переменные. Называют с
некоторым неодобрением. Во многих книгах по программированию
повторяется разный по форме, но одинаковый по содержанию совет -
избегайте промежуточных переменных] Мне кажется, советчики не
вполне правы. В нашей небольшой программе две промежуточные
переменные по два раза каждая. Написать вместо них непосредственно
выражения - можно, но это замусорит текст. Прочитать и, главное, понять
его, код, будет трудно и сделает его длиннее и сложнее. Так что пусть
будут вспомогательные переменные.
79
Однако сосредоточимся и не будем отвлекаться. Каждому флажку на
экране в программе соответствует логическая переменная. То есть вы их
всё-такн вводите, но нс так, как обычные числовые переменные. Вывод
таких переменных на экран или печать, опять-таки, никогда не
производится непосредственно. Что я имею в виду? Если у нас есть целая
или плавающая переменная, значение которой равно трём, то на экране мы
увидим 3 или 3.0, или, в самом плохом случае, что-то вроде 3.0000079.
Логические переменные никогда не выводятся в том виде, как они есть, -
говоря по-другому, конечному пользователю их значения безразличны.
Поясню, чтобы меня правильно поняли. Никакому нормальному и даже
ненормальному пользователю неинтересны надписи на экране True или
False. Но вполне ЯСНЫ тексты Всё в порядке ИЛИ Я согласен ИЛИ Денег нет.
yes ■ True
no - False
print yes, no
True False
а » 5; Ь - 2
plus = а > Ь
if plus:
print 'а > b’
а > b
80
логическое выражение, которое можно присвоить логической переменной.
Обдумайте. Теперь случай позаковыристее. Чуть раньше я просил вас
закодировать условный оператор для проверки високосного года.
Закодировали? Врёте, наверное. Если таки да, то вот вам возможность
проверить правильность вашего результата:
if leap:
print 'leap year'
else:
print 'not leap'
Циклы. Особенности
81
цикл по числу до предела
цикл по делителю до предела
если число делится на делитель без остатка, значит оно не простое
если число простое, то вывести
howMany ”10
2
3
5
7
82
# очевидное замечание
Впрочем, очевидное оно на то и очевидное, что очевидно не всегда и не для
всех. Переменная I определена только внутри внешнего никла, обращаться
к ней снаружи этого цикла нельзя. Переменная К определена только во
внутреннем цикле. Поскольку внутренний цикл по чистой случайности
находится внутри внешнего, в нём - внутреннем цикле, можно обращаться
и к I и к К.
tf конец Очевидного замечания
83
Правильно, будет выведено первое простое число и на этом всё
закончится. Есть ли в этом смысл? Никакого, мы и так знаем, что первое
простое число - 2. А вот если бы мы вводили не диапазон, в пределах
которого ищем простые числа, а число, начиная с которого искать
наименьшее простое число, то смысл был бы и вполне математически
корректный. Измените программу для этой цели. Когда программа
заработает, добавьте снаружи условный оператор, чтобы программа по
выбору работала в любом из двух режимов - до числа и от числа. Не
жалуйтесь! Тяжела и неказиста жизнь простого программиста ©
Народное.
i = 1 1.0
i - 2 1.41421356237
i - 3 1.73205080757
i - 4 2.0
i - 5 2.2360679775
i = 1 1.0
i - 3 1.73205080757
i - 5 2.2360679775
84
Довольно-таки сложная задача
123456789
I 123456789
2 2 4 6 8 10 12 14 16 18
3 3 6 9 12 15 18 21 24 27
4 4 8 12 16 20 24 28 32 36
5 5 10 15 20 25 30 35 40 45
6 6 12 18 24 30 36 42 48 54
7 7 14 21 28 35 42 49 54 63
8 8 16 24 32 40 48 56 64 72
9 9 18 27 36 45 54 63 72 81
for 1 in xrange11,11):
S - Str (i) .rjust(3) + ' '
for k in xrange(1,11):
s = s + »tr(i*k) . rjust (3) +
print s
3 - ’'
for i in xrange (1,11):
з - s ♦ ’ ' +etr(l) .rjust (3)
print s
85
print
for i in хгапде(1,11):
s “ »tr(i) . r just (3) + ' '
for k in xxange(1,11) :
s - s ♦ eCr(i*k) .rjust(3> ♦ ' '
print s
Другие циклы
В Питоне есть ещё один, другой цикл. Сначала напишем его так, как
написать его можно, но смысла никакого в этом нет - зато всё понятно.
i - 1
while i < 5:
print i
i - 1 ♦ 1
1
2
3
4
for i in xrange(l,5):
print i
86
Самая частая ситуация применения цикла while - активное и
непредсказуемое участие пользователя в работе программы. Пример -
любая игрушка. Мы ведь не знаем заранее, когда шрок нажмёт на клавишу
или когда всё игроку надоест, и он захочет выйти из игры. Но до этой
категории задач нам ещё далеко, выберем что-нибудь попроще.
Что-нибудь попроще будет из области высшей математики, конкретно -
математического анализа. Там таких задач сколько угодно.
доказали, что сумма этого ряда равна -. Теперь, чтобы узнать, чему же
4
равно л, остался сущий пустяк - сложить несколько членов ряда. Ну там,
десять, сто, тысячу. Скорее, миллион, если мы хотим получить хоть
немного точный результат. Кстати, это называется формула Лейбница.
Для наших целей мы возьмём ряд. изобретение которого приписывается
много кому. А если говорят много кому - значит, никому вообще. Вот он:
/г = 112(1 - -—- +---- у-------- у + ...). Говорят, этот ряд гораздо лучше - вот
V 3x3 5x3“ 7x3
и проверим.
Очень важное понятие - формула общего члена ряда. Эго уже очень
третьего?
А куда вы летите, я пока не знаю © Брат-2
II конец краткого курса.
87
Теперь надо решить две очевидно выделяемые подзадачи. Первая
подзадача - как записать формулу общего члена. Задача, повторюсь, по
форме математическая, а по существу - чисто программистская. Вторая
подзадача - по какому признаку завершить цикл. Несмотря на слово цикл в
вопросе, эта задача математическая и довольно сложная. Для того чтобы
решить, на каком шаге остановиться при суммировании ряда, математики
вывели формулы, узнать значения которых часто сложнее, чем сосчитать
сам ряд. Из всех возможностей мы выберем самую математически
ненадёжную. Зато она легко программируется. Наше условие
записывается так: ап < е. Другими словами, очередной, последний член
ряда меньше какой-то заранее заданной величины. Её, эту величину, тоже
придётся ввести.
Общий член, мне кажется, выглядит вот так:а =----- -—г. Как я до
(2л-1)хЗ”'
этого додумался? Нет, я нс подглядывал и не списывал. Нет, не потому, что
я слишком умный, это потому, что я очень давно программирую. Обычная
работа для программиста - посмотреть на последовательность, выявить
закономерность и вывести формулу. Причём это нс только в
математических задачах. Теперь о проверке условия. Оно должно
выглядеть где-то так while Ап < eps: Переменная называется eps ПОТОМу»
что та смешная закорючка в формуле по-гречески называется эпсилон.
Маленькая проблема, ап мы ещё нс вычислили, оно будет вычислено
только внутри цикла. В нашем случае мы с первого взгляда знаем, что
первый член ряда равен единице, но так бывает не всегда. Часто
приходится присваивать проверяемой переменной какое-то искусственное
значение, к примеру, заведомо очень большое. Продолжаем
программировать, однако.
Ошибки нужны только для того, чтобы на них учиться. Я написал вот
такой текст и получил бесконечно длинный список чисел, бесконечно
долго бегущий по экрану.
An - 1
s - о
n = 1
88
S - S ♦ An
print S
Что делать в таком случае? Нет, понятно, что надо немедленно найти
ошибку. Но ещё раньше надо остановить бесконечно долгое выполнение
программы. А для этого надо в том окне, которое Оболочка, не Редактор,
выбрать пункт меню <Shell>\<Interrupt Execution Ctrl+O - и всё
немедленно остановится. Можно проще - нажать совместно клавиши
<Ctrl> и <С>. Так в чем же ошибка? Ошибок много, н не все из них глупые.
An - 1.0
3 - 0.0
89
П = 1
cps = 0.001
90
Глава пятая. Списки
Списки - что это такое
а - [11,12,13,14,15]
print а
а(0| - и
•<П1 - 12
.1(2] = 13
>13) - 14
а(4] - 15
91
если мы попросим вывести элемент вне этого диапазона? Например,
одиннадцатый. Ничего хорошего, проверьте и убедитесь. Ещё - то. что
после имени списка в квадратных скобках, называется индекс списка. А
теперь совсем несложный код:
п « 1
print а(2*п+1]
14
а - [11,12,13,14,15]
print а
а = [-3,87,54566545]
print а
# страшная правда
А что случилось с первым списком, после того, как ему присвоили новые
значения? Куда он делся?
Он умер. Его значения всё ещё лежат в черноте оперативной памяти, но на
них не указывает ни одно имя. И совсем скоро-скоро к нему придёт
92
страшный Фредди Крюгер Сборщик Мусора и сметёт жалобно скулящие
бантики в совочек и утопит нафиг. Честное слово, вот так оно всё в
чёрном-чёрном компьютере и работает.
It коней Страшной правды
а - (11,12,13,14,151
print а
а[0] = 101
а(1] - а(2] + а[3) ♦ 1
print а
а - (11,12,13,14,15)
print а
93
который мы добавляем в список. Самый первый возникающий вопрос - а
если нам надо добавить не в конец, а в середину? Легко.
а - [1,2,3]
print а
a.Insert! 2, -1)
print а
(1, 2, 3)
(1, 2, -1, 3]
а - (1,2,3]
print а
del а(1)
print а
(1, 2, 3]
(1, 3]
а - (111,222,333)
print а
а.remove(222)
print а
94
Теперь, когда мы попросили удалить элемент и указали в качестве
параметра 222, это число обозначает не номер элемента в списке, а его
значение. Вам это надо? Иногда да, это бывает надо, но чаще - нет.
Ещё две незамысловатые, но полезные операции. Проще не объяснять, а
показать.
а - (1,23,11,77)
print а
a. reversed
print а
а - (1,23,11,77)
print а
a.sort О
print а
а - (1,23,11,77)
print а
а.sort(reverse-True)
print a
95
a - (1,23, 11,77, 11)
print 'index - ', a.index(11)
index = 2
а - [1,23,11,77,11)
print 'count - ’, a.count(11)
count - 2
а = (1,23,11,77,11)
print 'len • ', len (а)
len • 5
а - (1,23,11,77)
96
for 1 in хгапдо(0,4):
print i,'. ', a [i]
0 . 1
1 . 23
2 . 11
3 . 77
а - [1,23,11,77]
for i in xrang*<0,len(a)):
print i,’. ', a(i]
0 . 1
1 . 23
2 . 11
3 . 77
Здесь мы должны тщательно обдумать то, что видим. Нет, в целом всё
понятно. Лично мне сначала казалось как-то непонятно - ведь цикл
останавливается перед вторым параметром хгапдв, цикл для него не
выполняется. Тем не менее, мы просто пишем ien<a>, как собственно и
подсказывает нам здравый смысл - и всё работает идеально. Потом я
вспомнил, что начинаем цикл мы с нуля, гак что одна странность
компенсирует другую. Извините, что запутанно и расплывчато - это я
показываю, как непросто давалось мне постижение Питона.
97
обязательно напишу. Но когда переменная уже существует, для неё
откуда-то берётся значение. Потом это значение обрабатывается,
возможно, результат получает га же самая переменная, возможно,
исходной переменной наследует другая. Например, ввели радиус и
присвоили одной переменной, сосчитали длину окружности и присвоили
переменной номер два. А потом переменную куда-то отправили - на экран,
на печать, в файл или, как теперь модно говорить - в облако. В любом
случае, куда-то отправили - если результат никому нс нужен, то кому
вообще нужна эта программа?
98
Так бывает не всегда. Иногда назначение программы заключается в том,
чтобы принимать данные, приходящие из внешнего мира. Есть датчик, он
десять раз в секунду снимает какие-то отсчёты - температуру воздуха и
частоту бета-распада. Всё это пишется в файл, разумеется, а попутно-
в список, и с этим списком параллельно тоже что-то происходит. Его
элементы сортируются, пересчитываются, вводятся и выводятся.
Но сначала просто создадим список с нуля, ведь пустого списка у нас ещё
нс было.
а = (1
а. append(10)
a.append<20)
b - 11
for i in xrange(J, 11):
b.append(i,,2>
print a
print b
HO, 20)
|0, 1, 4, 9, 16, 2b, 36, 49, 64, 81, 100)
11устой список очень прост и очевиден: [). Две квадратные скобки, внутри
которых пустота. Первый пример - добавление элементов в список
поштучно. Второй - в цикле. В первом случае добавить ещё сотню
элементов вызовет некоторую усталость. Во втором - пустяк, главное
шать формулу, по которой эти элементы вычислять.
99
for । in xiang»! 0, howMany):
what « int(rawinput(' what = '))
a.append(what)
print a
How Many? = 3
what - 11
what - 12
what - 13
(11, 12, 13)
Задайте себе вопрос - а что, если где-то наш клиент вместо целого числа
введёт дробное, что будет? Ничего хорошего нс будет, разумеется. А как с
этим бороться? Пока никак, но мы к этому непременно вернёмся.
inport random
num - 5
а - I]
for 1 in xiang»{0, num):
what = random.randint(1,20)
a.append(what)
print a
100
Списки плюс циклы.
Стандартные ситуации
Этот раздел будет скучным. Или нет? В любом случае, этот раздел не даст
никакой возможности для творчества. Вам нс придётся думать,
размышлять, фантазировать. Вам надо только запоминать. Есть типовые
задачи - у них есть типовые решения. Не надо ничего выдумывать - просто
сделайте так, как все и всё уже делали до вас! Если вам кажется, что я
излишне эмоционален, извините. Я встречал очень большое количество
программистов, которые категорически не хотели делать, как надо. Они
изобретали свой собственный велосипед, подводный, с зонтиком и на
вёслах. Так вот, те задачи, которые я предлагаю дальше, решаются именно
1ак и только так, и, я таки вас умоляю, не надо ничего выдумывать.
■I - п
for i in xxanga (0,num):
a.append(0)
print a
Вопросы есть? Вопросов нет. Заполнить ноль любым другим числом вы,
несомненно, сможете.
Почему эта задача снова здесь, ведь мы сё уже решали? Во-первых, так
положено, исходя из педагогических соображений, - возвращаться к
решению уже решённой задачи другими способами. Во-вторых, задача на
самом деле другая - раньше от нас не зребовалось, чтобы значения были
рашыми. В-третьих, мы легко и просто решим задачу, которую решить
вообще-то довольно трудно. А почему у нас будет просто - потому что в
стандартном модуле Питона random всё уже решили до нас. У нас есть
список, пусть из десяти элементов. Нам надо выбрать из него несколько,
пусть три случайных и разных элемента. Задача не то чтобы
101
невыполнимая, но требует некоторых усилий. Попробуйте. Проблема в
том, что нельзя просто выбирать элементы со случайным номером из
первого списка - они, совершенно случайно, могут и повторяться. А в
Питоне всё уже реализовано до нас:
import random
а = [1,2,3,4,5,6,7,8,9,10)
Ь « random.sample( а, 3)
print b
(6, 10, 1]
а-1)
for i in xranga (0,num):
a.append!!)
print a
for 1 in xranga10,num):
a[i| - alij + 1
print a
sum = 0
for i in xranga/0, num):
sum-sum + a(i]
print sum
sum=0
for i in xrange (0,num):
sum«sum+a[1]
print sum
av=float(sum) / num
print av
102
7. Найти минимальный элемент массива н его заодно его индекс:
min-a(0]
index=0
for i in хгапдеi1,10):
if a[l) < min:
min-a[ i J
index=i
print numNeg
print numNotNeg
for i in xrange)0,len(a)-3):
a(i)=a[i+3]
for i in xrangef 2en(a)-3, len(a)):
a(i)-0
print a
indexEven-0
103
indexEven^i
break
print indexEven
а = (1,2,3,4,5]
b - а
print а
print b
(1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)
а(0) - -101
Ы41 = -105
print а
print Ь
(-101, 2, 3, 4, -105)
104
(-101, 2, 3, 4, -105)
а - |1,2,3,4,5)
ь-(]
for i in xrange!0, Jen(а)):
b.append) a(1))
print a
print b
a(0) - -101
b(4] - -105
print a
print b
(1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)
(-101, 2, 3, 4, 5)
(1, 2, 3, 4, -105)
а = (1,2,3,4,5]
105
b - [10,20,30]
print a
print b
а - (1,2,-3,4,-5)
ь - [)
for 1 in xranga10, lan I a)):
if a[i] > 0:
b.append(a(i))
print a, b
а - (1,2,3,4,5)
Ь - [101,102,103,104]
N1 - 2
N2 - 3
for i in xxange(ion(a)-1,Nl-1,-1):
dal a(i]
for i in xranga(0,N2):
a.append (b (i ])
print a
106
вы, на самом деле, удалите пятый. А удаляя пятый, которого уже и нет
вовсе, программа завершится аварийно.
(1,2,3,4,51
ь - (1
for 1 in xrxngeiTen(а)-I,-1,-1):
b.append(а[i1>
ь - ()
for 1 in xrxnget0,lentа)):
b.append(а(-1-1))
107
А почему не ноль? А как тогда отличить его от самого левого? Итак -
последний - минус один, предпоследний - минус два, и так далее. Это надо
обязательно запомнить. А теперь меняем местами без второго списка:
for i in хгапф«|0,l«n(a)/2):
a(i]-a[i] ♦ a[-i-l]
a(-i-l]-a(lj - at-l-lj
a(il=a[il - a[-i-11
import random
а=(|
for i in xrange'0,30) :
r«random.randint(0,100)
a.append(r)
print a
b- (I
for i in xrange(3, 30):
for k in xranja(0, a [ i ]) :
if k»k == a(i] :
b.append(a[ij)
print b
108
случайном порядке и так, чтобы каждое число встречалось ровно один раз.
Задача не выдуманная, вполне реальная. Как это реализовать?
import random
а - (]
for i in xranga(0,10):
a.append<i+l)
print a
109
Второе. Я долго думал, не написать ли fox i in хгапде 1о, 10): Всё-таки
вместо этого решил добавить единицу в следующую строку. Достаточно
ли тысячи обменов для качественной случайности? А сколько обменов
будет на самом деле, ведь в случае равенства индексов обмен не
производится? И главный вопрос - а зачем я проверяю индексы на
равенство? Неужели, если они равны, переприсваивание не сработает и
сломается? Отвечаю - не знаю и знать не хочу. И проводить опыты не
хочу. Я лучше проверю индексы. Такая у меня философия
про1раммирования. И ещё одно, уже нс вопрос, а проявление удивления.
Заполняем мы список в диапазоне (0,10), а случайный элемент из него же
выбираем в диапазоне (0,9). Это претензия к разработчикам языка, если
что.
Вложенные списки.
Или многомерные, как вам больше нравится
print Ь
print с[0) + с[1] + с[2) + с(3] + с[41
d = (1, 99, True, 2.87**3.14, 3.14’*2.87, 'It was many and many a year ago' ]
print d
(1, 99, True, 27.399911226570875, 26.680139129184198, ' It was many and many
a year ago']
110
впечатление на девушку, выучите, поможет. Если девушка американская,
конечно. Хотя и на нашу няшу стихи на непонятном языке окажут
стимулирующее воздействие.
Почему возведение в степень? В одной из книг Мартина Гарднера -
прочитайте обязательно - я нашёл вопрос: что больше, е* или д' ?
Наконец-то я нашёл время и место это узнать. И вам тоже пришлось.
# конец Комментария
а = (1,2,3,4,51
Ь = [101,102]
а = а + b
print а
a.append(Ь)
print а
а0 - а[0]
аЬ = а[5)
print аО, а5
1 (101, 102]
111
а50 - a(5J {01
print а50
101
а-()
п-5
п-3
for 1 in xrangef 0, п) :
wrk-(]
for k in xrange(0,m) :
wrk.append(0)
a.append(wrk)
112
print a
<10, 0, 0], (0, 0, 0), to, 0, 0), [0, 0, 0), (0, 0, 0)]
Список заполнен без затей, нулями. Нудные читатели могут задать вопрос
- а почему я решил, что у нас пять строк по три элемента? Почему не пять
столбцов по три элемента в каждом, ведь можно понять эту запись и так?
Просто потому, что так принято. В большинстве языков
программирования матрицы задаются по строкам. Единственно заметное
исключение - Фортран. И ещё напоминание, хотя об этом уже и
говорилось, но немного другими словами. Мы можем обратиться как к
любому элементу в отдельности, так и ко всей строке целиком:
(4, 5, 6]
[(1, 2, 3), (444, 445, 446), (7, 8, 9))
import random
mnum = 12
113
mtemps - ( -8.О,-4.6,-0.8, 5.4,12.7,15.3, 18.2,17.6,11.2, 3.3,-2,-2.7)
year = (]
for i in xrange(0,12):
month = (]
for к in xrang*(0,mdays(1]):
month.append(random.uniform! mtemps[i]-5, mtemps(i]+5])
year.append(month)
•print year
# совет
Выясните, чем отличается равномерное распределение случайной
величины от нормального распределения её же. Догадайтесь, какое из них
у нас в живой природе. Поменяйте распределение на другое, функция есть
В МОДуле random.
# конец Совета
mavs - ()
for i in xrange(0,12):
mavs.append(0)
for k in xrange (0,mdays [i]) :
mavs(i) = mavsli) ♦ year(i)[k]
mavs(i) - mavsli] / mdays[i]
print mavs
114
На что обратить внимание - на запятую после первой строки вывода. Это
уже было, запятая запрещает переход на новую строку после первого
вывода. Не очень красиво, конечно, зато просто. Интереснее и новее метод
ijust. Как и любой метод, он вызывается через точку от имени объекта.
Методы специфичны для типа объекта. Наше mavs(i] является объектом
строкового типа, поэтому обладает методами строк. Сам по себе метод
ijust очень прост. После обращения к нему с параметром 10, строка *123’
превращается в строку ‘123 ’, то есть получает семь дополнительных
пробелов в зад. Это так и называется - выровнять по левому краю.
1 2 3
4 5 6
7 8 9
стало:
7 4 1
8 5 2
9 6 3
11 19 20 24 25
10 12 18 21 23
04 09 13 17 22
03 05 08 14 16
01 02 06 07 15
115
Сделайте это! Вы сможете.
Срезы.
Всякие странности и экзотичности
а = [100,101,102,103,104]
print а[1:3]
[101, 102]
Что интереснее, можно присвоить срезу целый список, причем его длина
не обязана совпадать с длиной среза. Вот три варианта: список той же
длины и элементы просто заменяются; список длиннее среза и исходный
список растягивается; список пустой и операция эквивалентна удалению
элементов. Код я подсократил - предполагается, что после каждой
операции исходный список восстанавливается.
а = (100,101,102,103,1041
а(1:3] - [1101,1102]
а[1:3] = (11,12,12.5,12.75)
а[1:3] - (]
116
|100, 101, 102, 103, 104)
а - (100,101,102,103,104)
print а
print а(2:]
print а 1:2]
print а|:)
а • (100,101,102,103,104)
b = а[:)
ЩО) - -1
print а, Ь
[100, 101, 102, 103, 104] [-1, 101, 102, 103, 104)
117
Список по имени в теперь существует независимо от списка но имени л.
Ь - 123.45
с - (1,2,3, Ь)
Ь - 3.14
print с
print а
print Ь
print 'Ь(Ц - ', Ь(1]
print 'Ь(1:)’, Ь(1:)
(1, 2, 3)
(1, 2, 3)
Ь[1) - 2
Ь[1:] (2, 3)
118
Ещё одна особенность кортежа. В арифметических выражениях часто
используются скобки, причём без лишних ограничений - то есть если
можно заключить что-то в скобки, то почему нет? Например, можно так:
(3 + 5 + 7) = (3) + (5) + (7). Смысла особого в этом нет, но никто и не
запрещает. А как будет выглядеть кортеж из одного целого элемента?
Правильно, вот так: (7). А как отличить, где кортеж, а где выражение? А
кортеж из одного элемента, не важно какого, обязательно записывается
таким образом: (7,)
А - 0; В = 0; С = О
loots “ ()
if А о 0:
♦ квадратное уравнение, дискриминант вычисляем
dis - В"*2 - 4*А‘С;
if dis > 0:
xl = <-B>dis**0.5)/(2*A);
x2 - (-B-dis“0.5)/(2*A);
result - 'два корня’
roots.append(xl)
roots.append(x2)
elif dis -- 0:
xl - <-B)/(2*A); x2 - xl
result “ 'один корень'
119
roots.append(xl)
else:
result « 'корней нет'
•lif В о 0:
I линейное уравнение
xl = -С/В
result • 'один корень'
roots.append(xl)
else:
♦ особый случай
if С -- 0:
result " 'тождество'
else:
result “ 'корней нет'
print result
for i in xran^etO, len(roots)):
print roots(il
Возможные варианты:
аоО
дискриминант положителен
дискриминант отрицателен
2,4,3 корней нет
а = 0, b о 0
120
0,2, -10 один корень 5
3
О, 5, 3 один корень -
а = О, b - 0, с о О
0,0, 10 корней нет
а = 0, b = 0, с = 0
0,0, 0 тождество
Но и это ещё не всё. В тесте (5, 10, 5) ожидаемый ответ -12/3, реально
полученный -1. Замена на (5.0, 10.0, 5.0) не помогает. Тщательно изучив
исходный текст, никаких ошибок я не обнаружил. Тогда меня осенило и я
пересчитал на бумажке корни уравнения. Оказалось, программа права, а я
сначала ошибся. Бывает.
121
Глава шестая, короткая. Строки
Это будет недлинная глава о строках. Немного теории. Чем они
отличаются от списков, а чем - нет. Какие у них есть методы. Какие для
них есть функции. И, конечно, опыты и эксперименты. Для начала,
главное, что надо запомнить, - строки очень похожи на списки. И понять,
что в главном они от них отличаются.
Повторение пройденного
и чем строки похожи на списки
print s3
Маленькой ёлочке холодно зимой
з4 ~ s2 * 3
122
К символам можно обращаться отдельно, по индексу, так же, как к
элементам списка. Значит, точно так же со строками можно работать в
цикле. Простая задача - посчитать, сколько раз в строке встречается буква
'а’.
numA • О
for i in xrange < 0, Ten(s)) :
if s(il — 'a':
numA » numA + 1
print “num of A's = ", numA
num of A's « 2
Всё понятно, ничего нового. Но, чтобы хоть что-то новое всё-таки было,
перепишем цикл немного по-другому.
for sin in з:
if sim “ 'a' :
numA - numA + 1
II теория
Теперь обдумайте, что такое sim. Правильный ответ-это символ строки. А
какой именно? Все символы по очереди - от первого до последнего. Такой
цикл возможен только потому, что у строки есть первый символ, есть
последний и для каждого символа, кроме последнего, мы можем
однозначно указать следующий за ним символ.
# конец Теории
S - ' • '
Ah, distinctly I remember it was in the bleak December,
And each separate dying ember wrought its ghost upon the floor.
123
numThe - О
for i in «range:0,Ian(s)-2>:
if s(i:i+3] -- 'the':
numThe - numThe + 1
number of the’s - 2
ус одинокий
Белеет пар
8(1] - 'К'
3(1) - 'х'
TypeError: 'str' object does not support item assignment
124
именно это и значит - строку нельзя изменить. 'Этому вроде бы
противоречит следующий код:
Так что же, со строками совсем ничего нельзя сделать? Как известно, если
нельзя, но очень хочется, то можно. Для начала, обычно можно сделать
что-то тупо и в лоб. Следующий кусок кода удаляет из строки пробелы,
тупо и в лоб:
for х in s:
if x о ' ':
s_out = s_out ♦ X
s - s_out
print s
Возможно, вам показалось, что это ну уж очень тупо. Это потому, что
задача паша была не совсем элементарна. Вспомним ту, что была чуть
раньше - нам захотелось поменять первый символ строки. Задача проще -
и решение проще.
125
По сути, мы меняем строку, но делаем вид, что сё не меняем. Имя
изменяемой строки должно присутствовать слева от оператора
присваивания. Для того чтобы делать таким образом со строкой всё, что
угодно, существуют методы. Вернёмся к задаче удаления пробелов из
строки и решим сё по новой технологии.
Скажи-кадядя,аедьнедаром
s - s.find*'дядя')
print ’з = ", s
s • 8
where - s.find('дядя')
print 'where - ', where
where - 8
126
Теперь всё хорошо и строка цела. И запомним, что метод возвращает
позицию только первого вхождения подстроки. И ещё - если подстроки в
строке нет вообще, то мы получим в ответ минус единицу.
Теперь практ ическая сторона. Как в программе вывести эти две строки на
двух строках? Вариант - использовать два оператора print не предлагать.
Эго, конечно, сработает - для двух строк. А если нам нужен весь Евгений
Онегин на экране? Напоминаю - онегинская строфа это четырнадцать
строк. Что, рисовать четырнадцать однотипных операторов? На всякий
случай напомню. Вот так, совершенно очевидным способом, написать
нельзя:
127
двух значениях - общепринятом и программистском. Есть способ с
тройными кавычками, с которым вы уже знакомы. Пишем так:
128
переход в начало следующей строки. Последовательностей много, но эта
самая полезная, ей и ограничимся.
S -'111\n222\n333\n444'
print 3
111
222
J33
444
s -г•111\п222\пЗЗЗ\п444•
print s
' 111\п222\пЗЗЗ\п444'
129
Глава седьмая. Функции
Напоминание - что такое функция
inport math
х « math.sin(math.pi/2)
print ' Sin(pi/2) = x
Sin(pi/2) = 1.0
s - [1,2,3,4,5)
print S
del з(21
print s
(1, 2, 3, 4, 5)
[1, 2, 4, 51
130
Функции. Сделать программу понятнее
а • 'Иванов'
Ь = 'Петров*
с - 'Кошкин-Собакин'
ad - *01.01.1970'
bd = *02.02.1980'
cd - *03.03.1990'
def names():
а = 'Иванов'
b “ 'Петров'
с = 'Кошкин-Собакин*
def birthdays():
ad - *01.01.1970*
bd = *02.02.1980*
cd • '03.03.1990*
namesO
bi rthdaysO
131
def names() :
Он состоит из зарезервированного слова def, произвольного имени
функции и пары скобок с двоеточием. После нажатия на клавишу <Enter>,
как и всегда в таких случаях, имеем в следующей строке автоматически
вставленные пробелы. Всё то, что написано на этом уровне, обычно
называется тело функции. Вызов функции состоит из се имени и
обязательно пары круглых скобок. При вызове функции выполняется код,
записанный в её теле. Проверьте.
def odin О:
print 'Один я умный в белом пальто стою красивый'
132
оказаться и непредсказуемым. С другой стороны, согласитесь, что
жономия кода значительна.
def fillEmptySpaceO :
print 'Привет’
print 'Привет’
print 'Привет'
Ii1lEmptySpace()
Привет
Привет
Привет
133
скучным и неправдоподобным, то вы правы лишь отчасти. Скучным - да,
неправдоподобным - нет.
fillEmptySpace(2)
Привет
Привет
п - 2
fillEmptySpace(n‘2)
fillEmptySpace( 'Ку-ку', 2)
Ку-ку
Ку-ку
134
Очевидно, если параметр не один, надо очень аккуратно следить, чтобы
порядок их строго соблюдался. Проведите опыт - поменяёте при вызове
параметры местами. Расскажите, что случилось, и объясните, почему.
import random
def ourReplacel з,
what,
forWhat):
where “ s.find(what)
if where >* 0:
word = random.choice(forWhat)
s - s.replace) what, word)
print s
135
что вы без чего-то обойдетесь - значит обойдётесь. Однако если вы хотите
узнать, как работает Питон за пределами самых простых программ, -
прочитайте. И если вы хотите знать, как надо писать программы на любом
языке программирования, - тоже прочитайте. Конечно, в этом разделе вы
нс узнаете все секреты ремесла, но хоть что-то узнаете.
ourPlus( 1,2)
ourPlus! 1.23, 3.456)
ourPlus( ’Oh ’, ’babe!’)
ourPlus( [1,2,3,4,5), [77,88,99))
136
г - 3
z - 4.686
z - Oh babe!
z - (1, 2, 3, 4, 5, 77, 88, 99)
Казалось бы, чего проще. Ведь абсолютно ясно, что мы должны получить в
результате - [1,2,3,101]. Получим мы, однако, нечто совсем другое и
неожиданное:
г - (1, 2, 3, 101]
« - 2
v - 3.1
137
s - ’abed'
L = (1,2,3,4,5)
if type(x) == int:
print 'yes'
•Ise:
print 'no'
yes
if type<у) == float:
if type's) — str:
if type'L) — liet:
def ourReplace ( s,
what,
forWhat):
if (type(s) “■ etr) and (typelwhat) “ str) and \
(type (forWhat) — diet):
where = s.find(what)
if where >« 0:
word = random.choice(forWhat)
s “ s.replace! what, word)
print s
138
Теперь о вещах важных и бесспорных - для меня бесспорных. Проверять
или не проверять параметры на типы - личное дело каждого. А вот
проверять входные данные на адекватность и корректность -
необходимость, это уже от особенностей языка не зависит. В нашем случае
желательны проверки на вхождение заменяемой подстроки в исходную
строку и на то, что список слов для заменяя не является пустым.
Реализуйте это сами.
Настоящие функции
import math
х = 0.5
• ■ math.sin(х)
print 'sin(', x, 1) = ', f
def onlyFivef):
result - 5
139
return result
f - onlyFive ()
print 'onlyFive - f
onlyFive - 5
# для занудных
По определению считается, что 0!=1. Л почему, собственно? Так я ведь
уже сказал - по определению.
# конец Для
def Fuct( N ):
result - 1
for i in xrangef 2, N+l):
result “ result • i
return result
f - Fuct<4)
print f
24
Мне нс нравится N+l в цикле - а что делать? Если мы хотим, чтобы цикл
выполнялся до N, должны писать N+1. Ещё один опыт с функциями.
140
Вычислим синус. Вычислим не путём вызова одноимённой функции, а
по-настоящему, суммируя ряд. Что это значит. Есть вот такая формула:
sin х = х -
x = 0.5
I “ ourSin(x)
print 'sin(', x, ') - t
141
Результат вам понравился? Да или нет? А вы запускали программу, вот
честно, ведь нет? Я очень честный, и если, ие проверяя, поместил в книгу
программу, а в ней ошибка, то я в этом признаюсь и даже рассказываю, как
я эту ошибку искал. Всегда полезнее учиться на чужих ошибках. А что
ошибка здесь есть, я догадался, увидев ответ. Ответ получился таким,
сравните его с полученным от уже готового, питонского синуса:
142
def nothing ():
print 'do nothing'
xxx « nothing О
print 'xxx - ', xxx
<lo nothing
xxx “ None
Эго самое None вовсе не означает, что случилась какая-то ошибка. Это
специальное значение, которое функция возвращает, когда она ничего не
возвращает. Какой в ней смысл и как эту переменную можно использовать,
1>бдумайтс сами.
Ещё о функциях
def odin () :
print 'Один я умный в белом пальто стою красивый'
def odin ( s ):
result - s ♦ ’\подин я умный в белом пальто стою красивый'
return result
143
def prime) n ) :
result = True
for i in xrange) 2, int(n**0.5)+1):
if n I 1 *» 0:
result • False
break
return result
print prime(18)
print prime(19)
False
True
def onlyNumsl L ):
I что-то там
11, 2, 3, 7)
def onlyNumsf L ):
for x in L:
if type(x) <> int:
L.remove(x)
result = L;
return result
144
Для чего вроде бы ненужная переменная result? А для того, что я имею
мнение - в конце функции мы должны иметь raturn result и это
обязательно. Результат, однако, огорчает:
Ц, 2, 3, True, 7)
def onlyNumsl I. 1:
for i in xxanga len(L)-l, -1, -1)1
if type(L,[i]) <> int:
del L[i]
result = L;
return result
H, 2, 3, 7)
145
Не боюсь показаться назойливым - запомните, пожалуйста. А теперь
кончим строить из себя самых умных и решим задачу тупо и в лоб.
def onlyNums( L ):
result = t)
for x in L:
if type(x) =« int:
result.append(x)
return result
(1, 2, 3, 7]
И ещё о функциях.
Всякое не очень обязательное
146
result - result I 1000
return result
■iluminum 21.205665
iron 61.6535075
gold 103.829219
iron 61.6535075
61.6535075
walnut 5.026528
147
цилиндры стандартного диаметра и стандартной высоты. Другими
словами, радиус и высоту задавать не обязательно, а вот удельный вес -
таки да. Возникает вполне естественное желание написать где-то так:
tungsten 151.1885375
oak 5.497765
Второе. Без этого нельзя. Это называется рекурсия. Это нужно очень и
очень редко, но в таких случаях без рекурсии уже никак нс обойтись. Но
хотя в работе это мало кому понадобится, ни один учебник
программирования обойтись без рекурсии нс может, и от каждого
программиста ждут хотя бы теоретического знания, что это такое.
148
Рекурсия - это когда функция вызывает сама себя. Это называется прямая
рекурсия. Есгь ещё и косвенней! рекурсия. Эго когда функция а вызывает
функцию в, а уже та вызывает функцию а. Сразу возникает действительно,
кроме шуток, важный вопрос - а как же такая функция завершится?
Конечно, если функция будет вызывать себя с теми же параметрами, с
которыми её вызвали снаружи, то цепочка вызовов окажется бесконечной.
Отсюда простой вывод - при каждом вызове хотя бы один из параметров
должен меняться. Более того, обязательно должно быть такое значение
параметра, при котором цепочка вызовов прерывается и возвращается
конкретное значение.
Первая. Есть дерево каталогов, или директорий, или папок - как это
называется в вашей операционной системе? Обойдите всё дерево и
выведите имена всех файлов во всех каталогах. Более практично, однако,
подсчитать общий размер всех файлов. На самом деле, тут есть нюанс -
149
даже пустой каталог занимает сколько-то байт - но этим можно
пренебречь.
3,0,0
0,3,0
0,0,3
2,1,0
2,0,1
и так далее.
def Trinomial( а, Ь, с, х) :
result = а*х**2 + Ь‘х + с
return result
6.0
Кстати, я только что узнал, что trinomial это трёхчлен на английском. Что
до работы функции, то всё просто и ожидаемо. Поменяем код:
150
def Trinomial О:
result • a*x**2 ♦ b*x ♦ c
return result
•i - 1;
i> - 2;
e. ~ 3;
x - 1.0
f = Trinomial ()
print ' f , f
6.0
Продолжаем опыты.
def Trinomial О:
а = 3
b • 4 НИ
result « а’х**2 + Ь*х ♦ с
return result
а - 1;
b - 2;
с - 3;
х - 1.0
I • Trinomial()
print 'f • •, f
10.0
151
самостоятельно подсчитать результат, то увидите, что действуют те
значения переменных л и в, которые заданы внутри функции. Тех
переменных, которые снаружи, изнутри не видно. Это так красиво и
называется — область видимости, а переменные, которые внутри,
называются локальные переменные. Локальные они, разумеется, только по
отношению к внешнему миру, для самой функции это самые обычные
переменные.
def Trinomial О:
а = а + 1
result “ а^х^'г ♦ Ь*х + с
return result
def Trinomial () :
с - а + 1
result = а‘х‘*2 + Ь*х + с
return result
5.0
152
используется переменная а, которой внутри функции нет. Значит сё ищут
снаружи - и находят. Таким вот образом оно и работает.
Как я уже говорил, если вы хотите всё усвоить быстро и легко, это раздел
можно пропустить. Дальше будет ещё несколько скучных опытов.
def Trinomial О:
а - 1000000
Ь = 1100000
result - а*х**2 ♦ Ь‘х ♦ с
return result
а = 1;
Ь - 2,-
с = 3;
х - 1.0
f - Trinomial О
print ' f = ', f
before 1 2 3 1.0
f = 2100003.0
after 1 2 3 1.0
def Changed:
а = [4,5,6]
b.append(99)
а - (1,2,3)
Ь - (101,102,103)
153
before (1, 2, 3) (101, 102, 103)
after [1, 2, 3] [101, 102, 103, 99]
def simpleProc( n, s, L ):
n - 1
s « ' 1'
L - П1
nOut - 12345
«Out - '12345'
LOut - [1,2,3,4,5)
154
print 'before', nOut, sOut, LOut
simpleProc( nOut, sOut, LOut)
print 'after ', nOut, sOut, LOut
def simpleProc( n, s, L ):
del L(2)
L.append(6)
155
очень непросто - запомним, так делать не надо. Если так делать надо,
значит что-то в программе не так.
daf ourSin< х ) :
def Fuct< N ):
result = 1
for 1 in xrangef 2, Nil):
result = result * 1
return result
result = 0
for N in xrange(l,20):
result = result + (-1)••(N+1)‘(x‘*(2‘N-l))/Fuct(2‘N-l)
roturn result
def first*):
def second ():
x - 1
f - X + у + z
print ’f = ’, f
x - 11
у = 12
second*)
x - 101
у - 102
156
г - 103
first*)
Функции с функциями
157
каком значении х функция принимает самое маленькое значение?
Область математики, которая этим занимается, называется нелинейное
программирование. Слово программирование здесь имеет примерно то же
отношение к программированию на компьютере, что и математическая
функция к питоновской, то есть почти никакое. О том, как искать
минимум, написаны сотни книг и десятки тысяч статей, потому что это
действительно нужно и действительно важно.
a = -100.0; b - +100.0
eps “ 0.01
x - opt< func, a,b, eps)
print 'min x - x
158
интервале, иначе не получается. Интервал задастся строкой а - -юо.о,- ь
- *юо.о. Обратите внимание, что в одной строке целых два оператора,
разделённые точкой с запятой. И заметьте, что числа заданы в плавающем
формате, с точкой. Иначе с ними обращались бы как с целыми и в
результате последующего деления получались бы целые результаты, что
нас совсем не устраивает.
159
оптимизации в конце концов обнаруживается одномерная оптимизация.
Как сказал великий русско-еврейский комик Аркадий Райкин, о которым
вы, возможно, и не слышали - Внутри средневекового рыцаря наши
опилки. В математике оно часто так. А уж в программировании всегда.
И ещё, если функция линейная, то всё будет совсем по-другому. И
занимается этим отдельная ветвь прикладной математики, под названием
линейное программирование.
# конец Информации
160
Теперь я познакомлю вас с интересным понятием программирования -
псевдокод. Это означает, что мы пишем программу как бы по-русски, но
имея в голове, что чуть позже всё это будет переведено с русского языка на
язык программирования. В нашем случае псевдокод выглядит так:
xl - a ♦ (b-a>/3
x2 = a + <(b-a)/3)*2
fa = f(a>,’ fb = fib)
fxl = flxl); fx2 - f(x2)
ShowProcess()
161
» if fxl <- fx2:
b-x2;
xl = a ♦ (b-a) /3
x2 - a ♦ ((b-a)/3)*2
fa = f(a); fb = f(b)
fxl - f(xl); fx2 - f(x2)
Showprocess()
x « a + (b-a)/2
return x
def g( *xs):
result “ 1
for x in xs:
result = result * x
result - result •• (1.0/len (xs))
return result
G - 1.81712059283
162
его и не надо, чтения достаточно. Если вам цикл кажется неочевидным,
можно его заменить на другой, подлиннее и традиционнее:
163
Поэтому нельзя отказываться от текстового описания результата.
Предлагаю добавить его в список последним элементом. Охотно
допускаю, что в реальной программе это оказалось бы неудобным, но у нас
пока не реальная программа. Имеем вот такую функцию, в первом
приближении:
def QuaEq(А,В,С):
roots “ []
result - 'так, на всякий случай'
if А О 0:
dis - В<42 - 4*А*С;
if dis > 0:
xl - (-B+dis“0.5) I <2*A) i
x2 - (-B-dis**0.5)/(2*A);
result ” 'два корня'
•lif dis — 0:
xl - (-B)/(2*A)i x2 - xl
result • 'один корень'
xl - 0; x2 - 0
roots.append(xl)
roots.append(x2)
elif В <> 0:
xl - -C/B
result = 'один корень'
roots.append(xl)
if C == 0:
result - 'тождество'
else:
result = 'корней нет'
roots.append(result)
return roots
roots » QuaEq(3,10,3)
print roots[len(roots)-1]
for i in xrange(0,len(roots)-1):
print roots(i)
два корня
164
-0.333333333333
-3.0
roots = QuaEq(0,5,3)
один корень
-1
Пора с этим что-то сделать. Почему именно сейчас? Потому что наше
решение уравнения оформлено в виде функции. Весь смысл функции в том
и заключается, что её можно и нужно использовать многократно. А значит,
наш скорбный труд не пропадёт.
def QuaEq(А,В,С):
A = float (A); В = float (В); C = float (C)
roots - []
165
Потому что это очень важно для понимания Питона. У переменных типа
нет. Переменная - это просто ящик, в котором что-то лежит. А вот у этого
что-то тип очень даже есть. Обдумайте.
Попутно мы решили ещё одну проблему, сами того не заметив. Что было
бы, пока мы не добавили эту строку, при таком вызове:
Дальше хуже. Типов нет. На вход функции можно задать что угодно, лишь
бы параметров было три. Например, вот такое:
166
Глава восьмая, короткая. Модули. Коротко
Коротко не потому, что неважно. Модули - это очень важно. Но в Питоне
идея модулей реализована осень просто. Разумеется, как и всегда, есть
хитрые навороты, но сама основа настолько проста, что проше просто быть
не может.
Постановка задачи
функция:
def S_1(а.Ь):
return (a‘bt/2
Почему функция называется s_i - потому, что будет s_2. А почему без
фантазии и однообразно?
т=яW
2
167
c2 sin a cos а
import math
def S_2(a,uqol):
return ((a“2)‘ math.tan(ugol))/2
def S 3(c,ugol):
return (c“2‘math.sin(ugol)‘math.cos(ugol))/2
s - S_l(3,4)
s = S_2<3,math.radians(53))
s = S_3<5,math.radians(53))
Решение задачи
168
невозможно. В смысле, что это не требует почти никаких усилии. Мы
берём вот этот код
import math
def 3_l(a,b):
return (a‘b)/2
def S_2(a,ugol):
return <(a**2)* math.tan(ugol))/2
def S_3(c,ugol):
return (c**2*math.sin(ugol)*math.cos(ugol))/2
и сохраняем его в файл под именем triMod.py. tri - это не в том смысле,
что у нас только три функции, это сокращение от слова triangle. Всё.
Модуль готов. Поверьте, в других языках программирования создание
модуля требует заметно больших усилий. Теперь о том, как нам его
использовать. Эго чуть сложнее. Вог грн вызова трёх функций:
import triMod
import rath
s = triMod.S_l(3,4)
print s
s - triMod.S_2(3,math.radians(53))
print s
s = triMod.S_3(5,math.radians (53))
print s
Здесь уже есть на что обратить внимание. Появилась строка import triMod.
Перед именами вызываемых функций появилось уточнение, к какому
модулю они принадлежат. Это ожидаемо и понятно. Теперь о важном. Как
видите, всё получилось быстро и очень просто. Но, как всегда, есть
нюансы.
169
в нашем случае это значит, что если бы нам не нужно было вызывать
преобразование в радианы, то строку import math в головной программе
можно было бы удалить. Это хорошая новость, потому что упрощает
процесс программирования.
# запоздалое разъяснение
Почему головная программа! Головная программа - это та штука, которая
вызывает всех, а её никто нс вызывает, кроме пользователя.
Мужчины любят женщин
Женщины любят детей
Дети любят хомячков
И только хомячки никого не любят
# конец Запоздалого
И всё отлично работает. На самом деле, всё гораздо сложнее и работает всё
отлично только потому, что оба текста программ размещены в одном
каталоге. А если нет? В Питоне реализован довольно сложный алгоритм
поиска подключаемого модуля. Для учебных задач, вроде нашей, это
неактуально. В реальной жизни, скорее всего, используемые вами - и
всеми - модули будут лежать в каком-то одном каталоге. Программы,
использующие эти модули, будут наверняка храниться в совсем других
каталогах. Но это, как принято выражаться, - далеко выходит за рачки
обсуждаемых в нашей книге тем.
170
не могли видеть вообще, потому что он написан на совсем другом языке
программирования. Многие математические модули написаны на С или
C++, так они быстрее работают. Разумеется, написанный нами модуль с тем
же успехом мог бы ссылаться и на ещё один опять же написанный нами
модуль и так далее. Это тривиально, но об этом обязательно надо сказать.
Есть программа, не важно, в каком файле, и есть два модуля, в файлах л.ру
и в.ру. Первый модуль:
import В
def doSomething A(> :
print 'Ok'
def do():
В.doSomething_B()
Второй модуль:
import A
def doSomeching_B():
print '"I'm here'''
A.doSomething_A()
171
I’m here
Ok
Третье замечание. Кроме волшебного слова import, есть еще слово from.
Разница в том, что from извлекает из модуля нс абсолютно всё, а только то,
что мы конкретно закажем. Минус в том, что так будет однозначно
длиннее. Плюс в том, что перед именами функций можно нс указывать
через точку имена модулей. Я считаю, что на данном этапе нашего
развития возможность эта для нас лишняя.
pl - 3.14158
import mathconsts
def SofCircle(R):
return mathconsts.pi*R*’2
mathConsts.pi = 3
print 'нате пи самое лучшее пи в мире и равно mathConsts.pi
наше пи самое лучшее пи в мире и равно 3
172
I Ie делайте так, пожалуйста.
tt показ эрудиции
В 1897 году американский штат Индиана принял закон о том, что ж = 3.2.
То есть тспсрь-то они утверждают, что закон был принят только палатой
представителей, а после отклонён сенатом, но мы-то знаем. Голосом
Задорнова - ну тупыыые...
# конец Показа
Украинское радио.
- Так, - говорит DJ, - а от принтов лист eid Петрика з села Залутвки.
Петрик просить передати п/сню про комбайн. Добре, Петрику, слухай
теню про комбайн.
Второй выход.
- О, знову лист eid Петрика з села Залутвки, Петрик знову просить
передати теню про комбайн. Добре, Петрику, слухай теню про комбайн.
Третий.
- А ось Петрик з села Залутвки просить передати композицию Twenty first
century shizoid man з першого альбому групи "Kim Кремзон"... Петрику', не
вы%:;ся, слухай теню про комбайн...
анекдот, конечно
173
маленькая. Да, в нашем случае её можно бы и не делить на модули. Но если
вырастить кабанчика в десять раз толще, то придётся. И разделка туши
будет производиться именно по тем же линиям, что и у нас.
Вам кажется, что всё это странно и глупо, и вообще ни о чём? Для
маленькой, шрушечной, учебной программы - да, всё это ни о чём. Для
программы реальной, той, которая за деньги, вопрос становится
важнейшим. К тому времени, как специалиста допускают к разработке
программ за деньги, он осознаёт одну очень важную вещь.
Программировать - легко, проектировать - сложно. Правильно
спроектированную программу можно отдать программировать
программисту третьего сорта. Он справится, если за ним присматривать и
периодически подвергать публичной порке. Такова суровая жизнь.
174
Хотя мы начинаем программировать с головной программы, уже на этом
этапе вы должны определить интерфейсы всех трёх подчинённых
модулей. Поскольку в модуле с константами комментировать будет явно
почти нечего, начнём с пего:
import sqSq
import sqLineory
import sqldent
import sqConst
def Input():
global A,B,C
A - 3; В - 10; C - 3
def Output () :
print 'A - '.A, ' В = ', В, ' С = ',C
if roots <> (]:
print roots(len(roots)-1)
if len(roots) >= 2:
print sqConst.sqRoots
175
for i in xrangelO, len(roots)-1):
print roots[i)
A = 0; В = 0/ C - 0
Input ()
if A <> 0:
roots “ sqSq.Rez(A,B,C)
•lif В <> 0:
roots = sqLineary.Rez<B,C)
•lif (С <> 0) or (C — 0) :
roots “ sqldent.Rez(C)
•Ise:
roots » (sqConst.sqStrange]
Output 0
def Input() :
global А,В,С
А - 3; В - 10; С - 3
176
описание результата. Теперь три модуля для трёх вариантов. Обратите
внимание, что первые два вполне можно использовать и отдельно, к примеру
для решения линейного уравнения. Третий, сам по себе, вряд ли кому-то
понадобится. Сначала модуль для полноценного квадратного уравнения.
Важно следующее - если головная программа может храниться в файле с
любым именем, это никого не волнует, то внешний модуль должен иметь в
точности то имя, которое указано в инструкции импорта.
import sqConst
def Rez(A,B,C) :
dis - B**2 - 4*A*C
if dis > 0:
xl - (-B»dis**0.5)/<2*A>
x2 = (-B-dis**0.5)/{2*A)
roots = [xl,x2,sqConst.sqTwo]
elif dis “ 0:
x - <-B)/(2*A)
roots = (x,sqConst.sqOne)
roots = [sqConst.sqNone]
return roots
import sqConst
import sqConst
177
def Rez(C) :
if C — 0:
roots - [sqConst.sqld]
else:
roots = [sqConst.sqNone]
return roots
а - з в- ю с-з
Два корня
А теперь корни
-0.333333333333
-3.0
178
Глава девятая. Файлы
Что такое файл, вообще
Ещё очень важный момент - для того, чтобы прочитать файл, надо знать,
как его записали. Если вам это кажется очевидным, поздравляю и
усиливаю формулировку - чтобы знать, что прочитать из файла, надо
знать, что в него записали. Странно? Скоро поймёте.
179
называться как-то не так. но они обязательно есть. В традиционных языках
текстовые файлы обычно задвинуты куда-то на обочину. Если вы хотите
получить файл, вы получаете просто файл. Чтобы получить текстовый
файл надо предпринять некоторые дополнительные усилия. В Питоне
текстовые файлы лежат в основе иерархии и если вы хотите просто файл -
то получите именно текстовый файл. Давайте познакомимся.
Я это уже писал, и не раз, в своих книгах. На всякий случай -.wow книги это
тс, которые я написал, и нс важно, под каким именем они изданы.
Повторюсь, но в этот раз на более высоком и продвинутом уровне. Может
быть, даже и очень для кого-то сложном.
180
один как бы символ - код возврата каретки. Он возвращает нас к началу
строки, в которой мы находимся. Без него мы бы так и зависли в конце
строки, куда перешли. Эти два символа - перевод строки и возврат каретки
- всегда ходят парой, в Windows, по крайней мере.
181
f.close ()
182
t.write! 'ВыОегает...\n');
(.close!)
Я привёл весь код, а не только изменённые строки. Эго только для начала,
пока вы нс заучили наизусть, что всякий файл должен быть сначала
открыт, а потом закрыт. Теперь результат соответствует ожиданиям:
4 здесь читаем
f .close!)
183
Что изменилось? Вместо имеем теперь 'г'. Это значит, что файл нс
создаётся с нуля, а читается. Или, специально для альтернативно
одарённых, файл уже существует и, очень может быть, создали его не мы.
Файл - ещё раз напоминаю, речь сейчас только о текстовом файле - можно
прочитать разными способами.
Хорошо ли это - такое чтение файла? Да, это хорошо, но если нас только
интересует содержание файла. Чаще бывает, когда нам интереснее каждая
строка файла в отдельности. Мы хотим её изучить, проанализировать и
разобрать на символы. Кроме того, вполне возможен случай, котла
текстовый файл слишком большой. Вот прямо сейчас передо мной лежит
реальный текстовый файл объёмом 27 мегабайт, в котором 576957 строк.
Это протокол некоторого процесса, завершившегося аварийно. Файл надо
разобрать, изучить и проанализировать. Чтение такого файла в один приём
может потребовать слишком много памяти или времени. И, главное, если
мы прочитали файл сразу, а нас интересуют отдельные его строки, то
разборка его на эти строки ложится на нас.
Сейчас мы будем читать файл построчно. Это значит, что за один раз мы
читаем только одну строку. Если для вас это очевидно и вам обидно за
такие мои разъяснения, то я за вас рад. Для того чтобы построчное чтение
184
„мело хоть какую-то цель, сделаем самое простое, что только может быть
выведем для каждой строки её длину.
Кривоногий и хромой
НыОегает...
s - f.read(I
print 'length(s) = len(s)
length (s) = 60
185
while s <> '':
s = f.readline<)
print 'length(s) = 2en(s>
length(s) - 28
length(s) - 20
length(s) - 12
length(s) - 0
s * f.readline()
while so '':
print s, ' length(s) =', 2en<s)
s - f.readlineO
while True:
s - f.readlineO
if s —
break
print s
s • f.readlineO
з - s.rstripO
186
Итак, с высоты птичьего полёта, есть два способа чтения текстового файла.
Или мы читаем его одним оператором, но потом нам придётся повозиться,
чтобы разобрать его по строкам. Или мы повозимся в начале, но прочитаем
файл построчно. Есть и третий способ.
f.write(si)
f.write(s2)
187
Шаг четвёртый. Запись объектов в файл.
Только для Питона
Это очень удобный и простой способ записи данных в файл. Даже более
чем удобный и простой - идеальный. Но есть один нюанс. Теперь цитата,
мне кажется, к месту. Для тех, кто в школе с русским языком не дружил - а
будущие программисты обычно нс дружат - поясняю. Мужик по фамилии
Даль - автор Толкового словаря живого великорусского словаря. Типично
русская фамилия не случайна. Этимологический словарь русского языка
составил Макс Фасмер, а Правила русского правописания сочинил
Розенталь. Тенденция, однако. Вот, собственно, цитата.
Смысл в том, что если вы пишете файлы этим способом, то его прочитает
только такой же казак, как и вы - в смысле такой же питонист. Текстовый
файл прочтут почти все, бинарный, о котором в следующем разделе,
вообще абсолютно все, а вот этот - увы нет.
188
Теперь наша задача - записать это безобразие н файл. Ну и прочитать
после, само собой. Как ни странно, это совсем не сложно, хотя и придётся
подключить дополнительный модуль.
import pickle
t « openf 'd:/something.strange*, '«')
pickle.dump(L, f)
I .close ()
11 рО
II
.112
aF3.141582
'Blue oyster'
1>1
1101
а (1р2
199
<1177
ч (1рЗ
.iS'Your mother dont like me'
P4
.ia.
Разумеется, я, если постараюсь, смогу объяснить вам что это, и почему оно
выглядит и пахнет именно гак. Но мне кажется, это лишнее. Главное
понять, что никто и никогда прочитать это не сможет. Кроме нас,
программистов на Питоне. А мы будем читать это следующим образом:
out L -
11, 2, 3.141582, 'Blue oyster'. True, [99, 77, |J, 'Your mother dont like
me') |
189
используются функции модуля pickle. И. в конечном итоге, файл всё равно
получается текстовым.
190
Глава десятая. Файлы бинарные
Бинарные файлы настолько важны, что появилась неотложная нужда
выделить их в отдельную главу. Если вы прочитали всё предыдущее - всё
предыдущее можно забыть. Шучу, конечно. Забывать ничего не надо,
любые знания бесценны. Тем не менее, все эти файлы - это не совсем
настоящие файлы. Настоящие файлы - те, в которые пишутся бинарные
данные, как они есть. Скорее всего, 'акая формулировка вам непонятна -
но я объясню. Если вы пропустили, по моему совету, предыдущие разделы
и перешли сразу сюда, то вам будет даже проще.
R - 10
S - 3,1415 • R“2
191
print R, S
import struct
f = open, 'd:/square.bin', 'wb')
data = struct.pack('<i4’,R)
f.write(data)
data = struct.pack('<f4',S)
f.write(data)
f.close ()
data • struct.pack('<i4R)
f.write(data)
192
пой операции. Если мы позже захотим успешно прочитать наш файл, то
должны сейчас запомнить это число наизусть. Это не какая-то особенность
Питона. Это общий принцип работы с бинарными файлами, а 99% файлов
в мире бинарные. Хотя, скорее всего, я неправ. 99.99%.
data = struct.pack('<f4S>
E.write(data)
data = f.read(4)
к - struct.unpack*'<14data)
print 'R * ', R
data = {.read(4)
S • struct.unpack*'<f4data)
print 'S • ', S
f.close ()
193
R = struct.unpack(*<i4data)
S = struct.unpack(*<f4'.data)
R_ - R(0)
print 'R_ “ ', R_
R = 10
data - struct.pack(11R)
R = struct.unpack('i', data)
194
гом, кто из них лучше. Целое или плавающее число занимает в нашем
случае четыре байта. Есть младший байт. Есть старший. Знак меньше
указывает, что сначала пишется младший байт, знак больше - что
наоборот. В других, традиционных языках, если не извращаться
специально, порядок записи - от младшего к старшему.
# интересный факт
195
Если вы заглянете в любой исполняемый файл, тот, который с
расширением *.ехс, то обнаружите в начале некие буквы MZ. Эго
сокращение от имени и фамилии Миша Цукерман. Вот честное слово. И
как теперь с этим жить?
# конец Факта
Как записать и прочитать строку
Однако для реализации этой опции нам надо научиться записывать в файл
третий, после целых и плавающих, тип данных - строки. С глубоким
сожалением, должен заметить, что технология заметно отличается, и не в
лучшую сторону. Вот запись:
name - 'circle'
data = struct.pack('6s',name)
f.write(data)
data ■ f.read(6)
name - struct.unpack('<6s',data)
print 'name “ ', name
name ■ ('circle',)
196
Запись и чтение работают, но теперь нам приходится запоминать не только
ю, что мы пишем в файл строку, но и то, что её длина именно шесть
символов. От запоминания того, что это именно строка, мы избавиться не
сможем, но вот с шестью символами проще. Надо выполнить небольшую
ымену.
I пипа
• lute « struct .pack (' 6s', name)
t .write(data)
• стало
data = struct.pack('7p', name)
i .write(data)
Что будет с нашим новым файлом после такой записи? Файл увеличился
на один байт, его длина теперь равна семи и в первом байге записана длина
строки. В этой длине строки записано однако не семь, а шест ь - нагому что
это и есть длина строки. Это надо твёрдо усвоить - длина строки при таком
способе отличается на единицу, в меньшую сторону, от её размера в файле.
197
При записи в файл мы указываем, что пишем семь байт, или семь
символов, как вам это больше понравится. Байт с длиной строки тоже
учитывается. Читаем из файла опять-таки семь байт, потому что семь и
записали, это понятно. А вот то, что при распаковке мы указываем тоже
семь, мне кажется несколько нелогичным, но что имеем, то имеем. Это
надо просто запомнить.
К чему, однако, все эти сложности? В чём награда? Теперь нам не надо
помнить длину строки, нам надо только помнить что это именно строка. Её
длина хранится в файле. Точно так же любая другая программа на совсем
другом языке программирования, читающая наш файл, должна только
знать, что её ожидает далее именно строка. И, ещё раз, с записью строк в
файл ситуация просто безобразная во всех языках программирования.
Ничего не поделать.
data - f.read(l)
Ls - struct, unpack С<Ы*, data)
form = ’<• + »tr(Ls|0)) + 's'
data = f.read(int(Ls(0]))
name - struct.unpackf form, data)
198
Учебная задача
Почему сигнатура имеет длину восемь байт, если из них заняты только
шесть? Чтобы удобнее было смотреть на файл в шестнадцатеричном виде.
Красную шапочку помните? Почему у тебя такие большие глаза? Уши?
Зубы? Вот и в программировании то же самое. Многое программируется
именно так, а не по-другому, только для того, чтобы потом было проще
тестировать, отлаживать и исправлять ошибки. Эго признак хорошего
программиста, плохие программисты об этом и нс думают. И ещё -
проверку сигнатуры оформим как функцию, возвращающую булевское
значение. Просто чтобы напомнить, как это делается.
А теперь я открою вам тайну. Ну или сакральное знание, как вам больше
нравится. Я не шучу, многие программисты доходят до этого годами, во
всех смыслах доходят. Программа должна быть максимально
симметричной. Если мы задумали написать функцию, которая читает и
проверяет сигнатуру, мы должны немедленно подумать и о функции,
которая пишет сигнатуру. И мы сё напишем тоже. А почему только пишет,
но не проверяет? А потому, что мы абсолютно уверены, что на этапе
записи сигнатура в полном порядке. Вот эти две функции:
def writeSign(f):
data = struct.pack<'<8s', goodsign)
f.write(data)
def checksign(f) :
data - f.read<8>
sign - struct.unpack! '<8s',data)
if sign[0) -- goodsign:
return True
return False
199
или больше, его не рекомендуется писать в программе как оно есть. В
нашем случае этим значением является сигнатура ’Random '. Мы должны
завести специальную переменную, в котором это значение хранится.
# расширение кругозора
В других языках есть такое понятие, как константа. Это выглядит почти
как переменная, с одним только отличием. Константу нельзя изменить. В
Питоне констант, в точном смысле этого слова, нет. Если вам непонятно,
зачем нужна переменная, которую изменить нельзя, значит вы полностью
прониклись духом Питона.
Кстати, в некоторых очень традиционных языках программирования есть
ещё и константы, значения которых можно изменять.
# конец Расширения
L - tl
for i in xrange(0, howMany):
r = random.randint(1,100)
L.append(r)
print L
f .close ()
Возможно, у вас возник вопрос, почему в программе два цикла - один для
генерации случайных чисел, другой для их записи. Почему нельзя было
объединить всё в один цикл? Ведь программа стала бы короче? Это
хороший, правильный вопрос. Этого делать нс надо по очень простой
причине - потому что это разные задачи и разные процессы. Можно даже
выразиться сильнее - если что-то в программе можно поделить на части,
обязательно делите.
200
II ешё. В коде присутствует отладочный вывод на экран. Он даёт такие
результаты:
4
|18, 15, 95, 51)
ik - clreckSignff)
I. - 11
If Ok:
data - (.read(4)
hcwMany « struct.unpack( '<i4', data)
for i in xrange:0, howMany[0]):
data - f.read(4)
r = struct.unpack! '<i4', data)
L.append(r(0])
• Ise:
print 'Bad. very bad.'
t.close!)
Обобщаем и систематизируем
201
Начать с того, что целые переменные можно записывать в файл с разным
размером - в байтах. Мы писали с размером четыре. Хорошо это или
плохо? Есть такая философская наука - диалектика. Она учит нас, в
частности, что истина конкретна. В нашем случае это означает, что если
размер файла для нас важен, то, возможно, четыре много. А если у нас ну
очень большие целые числа, то, возможно, это мало. Поясняю.
import struct
something = 128
f.close О
202
Как это всё прочитать, я указывать не буду, не сомневаюсь, вы справитесь
отлично. Почти то же самое и с плавающими числами, только выбор там
меньше.
■mcthingFloat = 123.45
|neSenseOfLife - True
• булевское
data - struct.pack( •?!', theSenseOfLife)
I.write(data)
В первом приближении это всё, что надо знать о бинарных файлах. Есть
ещё другие форматы и другие способы записи, например возможность
одновременной записи большого количества однотипных данных, но без
них можно обойтись. Пока можно.
203
Глава одиннадцатая. Графика
Это хорошая, добрая глава. Мы будем рисовать. Сначала мне каталось, что
глава эта будет почти никак не связана с другими. Оказалось, показалось.
Прежде чем что-то нарисовать, очень неплохо будет освоить ещё один
несложный метод работы с функциями. Конкретно - передача параметров
по именам. Чуть раньше я его упоминал, но объявил, что нам он не нужен.
Был неправ.
Подготовительные упражнения.
Параметры по именам
iron 61.6535075
iron 61.6535075
204
Перед каждым значением параметра указано имя параметра. Зачем это
нужно и какие даёт преимущества? Обычно говорят - так гораздо
нагляднее и с первого взгляда всё понятно, причём всем. Второе
преимущество, параметры теперь можно писать в произвольном порядке:
Для чего это нужно и где это используется, вы увидите почти немедленно,
как только мы займёмся непосредственно рисованием.
Начинаем рисовать
205
niainloop ()
206
Следующая строка, которая с.pack, является специфической для именно
I (итона. Её надо написать и всё тут.
Линни со смыслом
207
c.create_lir>e< 0,200, 400,200)
c.create line* 200,0, 200,400)
хО - 200
уО - 200
s - 400
208
I la самом деле, возможности метода для рисования линий несколько шире.
Разных параметров, или, по другой терминологии, опций, у него больше
двадцати. Кроме того, точек, по которым рисуется линия, может быть
больше двух. Демонстрирую. Сначала напишем функцию, рисующую
равносторонний треугольник, в качестве параметров - сторона и
координаты левого нижнего угла. Из геометрии. Высота равностороннего
, ал/З п
феугольника по его стороне вычисляется так: п = . Для чего нам
209
необходимо обеспечить замкнутость линии. Проверьте, результат должен
быть тот же. Немного усложним код, или упростим, кому как покажется:
# бесполезная информация
Некоторые правильные многоугольники можно построить циркулем и
линейкой. Гаусс доказал, что правильный многоугольник можно
построить, если число его сторон равно простому числу Ферма. На данный
210
момент известны следующие простые числа Ферма - 3, 5, 7, 257, 65537.
Подходит и любое число сторон, которое является произведением этих
чисел и любой степени двойки, например Зх 7 х 257 х 210.
Один слишком навязчивый аспирант довёл своего научного руководителя
до того, что тот сказал ему: «Идите и разработайте построение
правильного многоугольника с 65537 сторонами». Аспирант удалился,
чтобы вернуться через 20 лет с соответствующим построением
(которое хранится в архивах в Геттингене).
О Литлвуд, Математическая смесь.
Эго потому, что у них компьютера не было.
# конец Бесполезной информации
If drawRs:
for i in «range(1,numOfVt1):
x - roundiR"math.cos(alfa*(i-1)));
у = roundiR'ma th. s i n (a 1 fa*(i-1,))i
L = (<xO+R),yOj
for i in «range!1,numOfV+1):
x - round(R*n>ath.cos<alfa*i)):
у ■ round(R*math.sin(alfa*i))s
L.append( (xO+x,yO-y) )
c.create_line< L, width - 3 )
211
Рисование многоугольника начинается с крайней правой точки, той, что
лежит на положительной части оси абсцисс - если вы понимаете, о чём я
говорю. Вершины мы рисуем в порядке, противоположном движению
часовой стрелки. Counter-clockwise - это если вы хотите показаться
избыточно умным.
212
Возвращаемся к геометрии. Радиусов больше не надо, просто нарисуем
всего и много:
213
неразборчиво. Можно поправить. Добавим в заголовок функции параметр
со значением по умолчанию.
Вот результат.
214
Если кто-то линии рисует, значит, кто-то их и стирает? Специального
метода для стирания линий, увы, нет. Но уже нарисованную линию можно
заново нарисовать белым цветом, точнее цветом фона. Если точнее, линию
можно нарисовать любым цветом. Книга не цветная, поэтому картинки не
будет, а пример кода будет.
С прямыми у нас уже всё очень хорошо, я надеюсь. Теперь кривые. Самая
совершенная из кривых - круг. И его лучший друг эллипс, разумеется.
Где-то далеко вверху мы уже нарисовали оси координат и треугольник на
их фоне. Треугольник уберём и нарисуем круг, с центром в начале
координат и радиусом сто. Это самое простое, что только может быть с
нарисованным кругом. Вот программный код, базирующийся на ранее
написанном:
xO - 200
yO = 200
s = 400
R - 100
215
c.create_line< xO,O, xO,s, width = 1)
mainloopO
216
Последний параметр, как несложно догадаться, это толщина линии,
которой нарисован эллипс. Жирные точки по углам - тоже эллипсы,
только маленькие, и рисуются они так:
_ _ <э\'3
ее равен Л =------ , где а - сторона треугольника, а п, в свою очередь,
высота треугольника, равная Л = .Всё для вас должно быть уже просто
и очевидно.
х0 = 200
у0 - 200
а - 150
h - (3**0.5)*а/2
R = (3**0.5)*а/3
217
c.create_line(xO,yO, x0+a/2,y0-h, width=3)
c.create_line IxOia,yO, x0*a/2,yO-h, width-3)
ir.alnloopO
хо - 100
у0 = 200
218
То, что картинка как бы вытянута по вертикали, так это так и задумано.
Тонкой линией нарисованы координаты и тот эллипс, от которого мы
откусили кусок. Сама дуга толстая и жирная. Что интересно? Для
рисования эллипса и дуги использованы одни и те же координаты, а
результат принципиально разный. Будем разбираться. По умолчанию, дуга
имеет размер ровно в 90°. В эгом отношении Питон ведёт себя очень
дружественно к программисту, в других языках используют вместо
градусов радианы. Дуга идёт от горизонтали - то есть от линии, идущей
между началом координат и крайней правой точкой нашего эллипса.
Заданные 90° отсчитываются против часовой стрелки. Если всё это
кажется вам пока не очень понятным, то я с вами согласен. Постигать
рисование дуг будем на опытах.
219
Итак, параметр extent отвечает за размер дуги в градусах. Теперь видно,
почему оговорено рисование именно против часовой стрелки. Идём
дальше. Сейчас мы нарисовали дугу между тремя и шестью часами, если
вернуться к понятиям часового циферблата. А если надо между
полуднем - двенадцатью - и тремя? Учим новое слово.
Разумеется, то, что два раза встретилось число 45 - это чисто случайно и
совпадать они не обязаны. Третий параметр, представляющий интерес и
уникальный именно для дуги, называется не очень оригинально - style.
Чем рассказывать словами, проще показать.
хО - 100
уО = 200
г - 70
220
Извините за некоторую небрежность стиля программирования.
Разумеется, лучше было бы оформить рисование дуги и соогвстствующсго
ей эллипса в виде функции. Зато так быстрее. Главное, всё сразу понятно.
Текст
Последний параметр, шрифт, при выводе текста очень важен. При выводе
линии мы могли не задавать её цвет и толщину. По умолчанию линия
чёрная и толщина её равна единице, обычно это всех устраивает.
Параметры по умолчанию для шрифта обычно не устраивают никого. Нс
потому, что они плохие, а потому, что требования к тексту бывают очень и
очень разными. Далее - шрифт задаётся немного сложнее, чем цвет или
толщина. Шрифт - это, строго выражаясь, кортеж. Кортеж, напоминаю,
это нечто, заключённое в круглые скобки и через запятую. Для
какого-нибудь другого языка программирования такое определение
прозвучало бы наивно и даже глупо, а в Питоне - в самый раз. Задаём мы
имя шрифта и размер. Поскольку параметры передаются не по именам,
порядок их следования важен. Л теперь, наконец, результат.
221
Однако на этом мы не остановимся. Обозначим ещё и стороны, а также
углы. По традиции, стороны обозначаются маленькими буквами и
курсивом. Сами буквы совпадают с буквами для углов напротив сторон.
Чтобы всех запутать, углы при вершинах А,В,С подписываются
греческими буквами а, Р, у. Пойду против традиции и сначала приведу
результат, а только потом код.
222
Что интересного мы здесь видим? Места, куда выводить текст,
подбираются научным методом тыка. С этим ничего не поделать.
Греческая буква у выглядит несколько не так, но с этим тоже ничего не
поделать - просто подберите другой шрифт, посимпатичнее. В описании
шрифта добавился ещё один параметр - 'italic*. Слово это, в переводе с
их языка на человеческий, означает курсив. Параметр этот несколько
нетрадиционен, в том смысле, что слов в строке может быть много,
например‘italic bold underline'. Результат будет таким-
Что важнее, обратите внимание, на то, как задаётся этот код символа. А
именно text»' \хб1 •. Об этом мы уже говорили раньше, в главе о строках, х
обозначает шестнадцатеричную систему счисления. Бэкслеш - обратный
слеш - разделяет символы, заданные в этой самой шестнадцатеричной
системе.
223
Прямоугольники и многоугольники.
В том числе и бет углов
Нс знаю, может вам хотелось увидеть именно это, но что касается меня, то
точно нет. Хорошая сторона - нам достаточно указать только вершины
многоугольника, замкнутость многоугольника будет обеспечена без нашей
заботы. Теперь о главном - а почему он весь черный? Проблема в
параметре fin, точнее, не в самом параметре, а в том, что мы его нс
224
задали. Параметр этот отвечает на вопрос - а каким, собственно, цветом
закрашивать внутренности многоугольника? Если параметр пропущен, то,
как и обычно - чёрным. То есть отсутствующий параметр, это то же самое,
что и fill - 'black*. Нас это не устраивает. Если мы напишем fill = ", то
внутренности останутся прозрачными, то есть - всё, что было внутри
многоугольника, всё это и останется в неприкосновенности. Пробуем.
Код:
Но ведь это уже совсем никуда не годится! Просто бред какой-то! Что же
случилось? Действительно, бред какой-то. Питон считает, что если ему не
дано другого указания, то контур многоугольника надо рисовать
прозрачным цветом. Прозрачный - это невидимый, если что.
225
Немного страшно выглядит, конечно, но мы ведь именно этого и хотели?
Последний штрих. Напишем вот так:
226
Разумеется, многое из того, что было сказано о рисовании линий, точно так
же относится и к многоугольникам. Вот пример:
Картинки
хО - 100
у0 - 200
shift = 70
227
c.create_bitmap( xO»shift, 50, bitmap - 'error');
c.create_bitmep( x0*shift, 80, bitmap = 'gray75');
c.create_bitmap( xO»shift,110, bitmap - 'graySO');
c.create_bitmap( xO»shift,140, bitmap - 'gray25');
c.creat«_bitmap( xO+shlft,170, bitmap = 'grayl2'>;
mainloopO
Результат:
Но, как уже было сказано, позволено вывести и что-то своё, из файла. Хотя
Питон и здесь идёт своим путём. В любом графическом редакторе можно
создать файл формата BMP - но Питону он нс подойдёт. Нужен
обязательно файл формата X Bitmap. Что это такое? Как ни странно, это
текстовый файл в шестнадцатеричном формате. Или, если так звучит
лучше, шестнадцатеричный файл в текстовом формате.
228
Вам непонятно? Что вам? Даже программистам преклонного возраста
непонятно. Сам формат - дремучий атавизм.
И анатомическое разъяснение
Атавизм, если кто не знал, да ещё и забыл, это такая часть организма,
которая когда-то была очень полезна, а теперь никому ни нужна и растёт
чисто по привычке. Например, у человека есть хвост - называется копчик.
Раньше он был длинный и эффективный, можно было висеть и
раскачиваться, зацепившись за ветку. Теперь он маленький и где-то под
кожей, но он там есть. Проверьте.
// конец Анатомии
•define mu width 32
•define mu_height 8
static unsigned char x bits
0x00, 0x00, 0x00, 0x00,
Oxff, Oxff, Oxff, Oxff,
Oxff, Oxff, Oxff, Oxff,
Oxff, Oxff, Oxff, Oxff,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
Сохраните его как простой текст. Имя файла пусть будет mu.xbm. *.ХВМ -
традиционное расширение для файлов типа X Bitmap. Что мы сделали?
Первые две строки задают ширину и высоту, именно в таком порядке,
наше картинки. Сразу видно, что её размер 32x8. В директиве Adeline при
задании ширины и высоты традиционно используется имя файла. Если
229
файл называется mu.xbm, директива - «define mu width 32. Это
необязательно, префикс может быть любым, за исключением х. На всякий
случай, префикс - это то, что перед подчёркиванием.
230
важны. Теперь вспоминаем - 00|6 = 000000002. Вог эти восемь двоичных
цифр, конкретно нулей, отвечают та восемь точек на экране и по
горизонтали. Если у нас нули - ничего не рисуется. А что нарисует группа
Oxff? Элементарно. FF(6 =111111112. Имеем восемь нарисованных точек.
f.close ()
231
А можно ли просто и без затей загрузить изображение из файла какого-то
более привычного формата? Можно. Но, как и во многих других случаях,
Питон идёт здесь своим путём. Для начала, вы можете вывести
изображение в формате PPM. Вы о таком слышали? Вот и я тоже. Или
слышал и много лет как забыл. Далее следует столь же популярный
формат PGM. К счастью, ещё допускается и вполне традиционный формат
GIF. Для очень образованных, знающих, что GIF бывает и анимированный,
уточняю - анимация нс предусмотрена. В этом случае будет выведен
только первый кадр из файла. Но есть и хорошая новость - изображение
имеет право быть цветным. Никаких дополнительных заклинаний для
этого произносить не требуется. Если изображение цветное - оно и
отобразится как цветное.
PIL расшифровывается как Python Image Library - ведь всё понятно и без
перевода, правда? Библиотеку уже не поддерживают почти десять лет, но
есть то, что в кинематографе называется вбоквел, а у некоторых
программистов fork. Эта штука называется Pillow и, по слухам, имеет
намного большие возможности. Нам выше головы хватит и начальной
библиотеки PIL. Библиотека совершенно бесплатна. Где взять? Можно
здесь - http://w\’w.pythonware.com. Или просто наберите в любом
поисковике PIL. Установка библиотеки не представляет никаких
затруднений - при условии, что сам Питон установлен корректно. Вы
справитесь.
232
Приложение А. Консоль
Сейчас мы займёмся крайней левой иконкой. Выглядит она вот так -
Python
Ire»
. Извините за чёрный фон, в оригинале он был зелёным. Если
вызывать про!рамму через Меню, то вызов будет выглядеть так -
ПрО1раммы \Python 2.7 \Python (command line). Опять-таки, во всех книгах
это называется консоль. После запуска выглядит консоль, скажем прямо,
страшно:
»>2*3
5
»>2-3
-1
»>2»3
6
»>2/3
о
>»2 % 3
2
»>3 % 2
1
233
»>2.0/ 3.0
0.66666
»>2/3.0
0.66666
Надо явным образом указать, что у нас нс целые числа, а дробные, то есть
после целой части поставить точку. Можно у обоих чисел, можно у одного.
»>а - 2
»>Ь « 3
>»С - а + 2*‘Ь
»>print с
10
х - -1
if X > 0:
... print 'positive'
else:
_ print 'negative'
negative
Можно делать почти всё, только к чему эти извращения? Зачем нам
вообще эта консоль? Забудьте.
234
Приложение В. Другие числа
Этот раздел можно вообще не читать - потому он здесь, в приложениях.
Если не прочитаете, ничего не потеряете. Если прочитаете, узнаете что-то
новое и расширите кругозор. Узнать что-то новое, тем более относящееся к
тому, чем вы интересуетесь, всегда полезно. В любом случае, я вас
предупредил.
2
сокращаемыми, как или нет, как все остальные в этом ряду - это
inport fractions
а ■ fractions.fraction <2,3)
b - fractions.Fraction 0,5)
c « a + b
print a,b,c
235
import fractions. Дроби сами собой ниоткуда не возникают, надо писать
имя модуля и имя создающей дробь функции, говоря по-другому —
конструктора функции. В чём преимущество использования дробей? Это
легко увидеть, проделав те же вычисления, но с обычными плавающими
числами.
afl = 2.0/3.О
bfl - 3.0/5,0
х - fractions.FractiontO.125)
print х
1/8
Для тех, кто забыл, напоминаю 0.125 = -. Всё работает отлично, пока.
8
Теперь резко упрощаем задачу, не 0.125, а всего-навсего 0.1. Результат
несколько сюрпризен:
у - fractions.Fraction(0.1)
print у
3602879701896397/36028797018963968
236
разница? Это тот уникальный случай, когда разница есть. В натуральные
дроби идеально преобразуются только тс числа, которые представимы в
натуральных дробях с знаменателем, являющимся степенью двойки.
Обдумайте.
х - 1 + 2J
У - 3 - lj
plus - X + у
minus = х - у
mule “ х • у
237
print 1X = ',x,• у = ', у
print plus
print minus
print mult
x = (l+2j> у - (3-lj)
(4+1])
(-2+3j)
(5+5j)
Второе. Как-то так само собой получилось, что у нас во всех примерах
исходные и расчетные комплексные числа состоят только из целых
компонент. Разумеется, это не обязательно. Бывают и другие числа:
Z = 2**0.5 + 3.14158]
print z
(1.41421356237+3.14158])
div - х / у
print 'div - ', div
div - (0.1»0.7j)
238
Питоне, присутствуют? Не знаю, что называется - дань традиции. Только
было это очень давно и в традиционных языках об этом давно уже забыли.
Это называется числа с фиксированной точностью. Здесь опять
начинается длинный и нудный и бесполезный разговор о том, что где-то
внутри числа они все двоичные. Почти всегда, разговор этот ни о чём,
программисту этого знать не нужно, но иногда, очень редко, оно вылезает.
К чему эта шутка? Пишем вот такой несложный код и получаем вот такой
неожиданный результат:
-1.11022302463е-16
239
print a
0.0
0.4
0.0
-1.0
2.22044604925е-16
import decimal
240
a = 0.3 + 0.3 + 0.3 - 0.9
print 'old.a « a
old.a = -1.11022302463e-16
new.a 0.0
Ь « а + 0.33
Число с фиксированной точкой - это как вирус, или, если это вам ближе,
как вампир - кого укусил, тот тоже стал вампиром. Если в вычислениях
задействовано число с фиксированной точностью, то и все дальнейшие
вычисления должны содержать только числа с фиксированной точкой. В
той области программирования, где я существую, это никому не нужно, но
нс могу же я говорить за всех.
241
Приложение С. Можно ли сделать ЕХЕ-файл?
Если вы написали программу, то судьба её может быть разной. Учебная
программа, скорее всего, будет выброшена. Максимум, её текст будет
оставлен в качестве образца, откуда можно списать, но выполняться
программа не будет. Возможно, вы будете пользоваться программой сами.
А может быть, этой программой будет пользоваться кто-то ещё. Понятно,
что в этом случае программу надо этому кому-то передать. А как это
сделать?
242
Приложение D. Философия Питона
Чтобы узнать всю философию Питона, достаточно в среде
программирования - оболочке - набрать команду import this. В ответ вы
получите следующее:
243
Ошибки должны вылезать наружу.
Если не заглушить их явно.
Если не понимаете - не пытайтесь угадать.
Должен быть один - и хорошо бы только один - способ сделать это.
Хотя способ бывает неочевиден - если ты нс голландец.
# видимо, тонкин намёк на легализацию легких наркотиков.
# в стране вечных тюльпанов.
Сейчас лучше, чем никогда.
# Одного танкового генерала спросили:
# - Какой танк самый лучший?
# - Тот, который здесь и сейчас, - ответил он.
Хотя никогда часто лучше, чем прямо сейчас.
# нет, нет, нет, нет мы хотим сегодня.
Если реализацию трудно объяснить, то идея хреповата.
Если реализацию легко объяснить, то, может, и ничего.
Прос транства имён обалденная идея - дайте две!
# лично мне как-то безразлично.
244
Приложение Е. Все системы счисления на трёх страницах
Сначала математические разъяснения для тех, кто не знал да ещё и забыл -
а что такое система счисления. Числа, с которыми мы обычно имеем дело,
записаны в десятичной системе счисления. Это настолько само собой
подразумевается, что на это даже не надо специально указывать. Обычно
система счисления помечается мелкими циферками справа внизу, вот так:
123,0. Так положено, но, разумеется, всегда пишут просто 123. Что это
означает? 123|О = lx IO2+ 2х 101+3х 10° =123. На всякий случай
напоминаю, что любое число в нулевой степени всегда равно единице.
245
Как легко догадаться, в двоичной системе всего две цифры: ноль и
единица - поэтому число наподобие 123 записать в этой системе просто
невозможно. Перевод в десятичную систему выглядит так:
101111 12 = 26 + 0 + 24 +З3+22 + 21 +2° = 64 + 16 + 8 + 4 + 2 + 1 = 95 .
Теперь от том, как всё это богатство записать в Питоне. Напоминаю, если
раньше об этом не говорил - внутри все числа одинаковы, то, чем сейчас
занимаемся, это всего лишь форма записи, мы выбираем ту форму, которая
нам сейчас удобнее. В порядке возрастания основания системы счисления
- двоичная, восьмеричная и шестнадцатеричная.
х2 - оыошп
х8 - 0о123
х16 = 0x123
xAll - х2 + xR + х16
print 'XAll - xAll
xAll - 469
246
А теперь очень полезная таблица, которая очень пригодится любому
настоящему программисту. И которую надо, страшно сказать, заучить
наизусть.
Для чего это понадобится - для изучения файлов, что у них внутри. Для
изучения памяти - что у неё внутри. Для изучения всего остального - что у
него внутри. И вообще, это нужно почти всегда и почти везде - если
всерьёз программировать.
247
Приложение F. Всё о битах и байтах
Есть такое понятие - единица измерения. В чём измеряется длина?
Правильно, основная единица измерения длины метр. А чего-нибудь
поменьше можно? Можно - дециметр, сантиметр, миллиметр. А где эта
последовательность кончается? Я точно не знаю, надо посмотреть в
интернетах. Ну не знаю я, какая самая маленькая единица измерения
длины. И, уверен, вы тоже нс знаете.
Есть такое понятие - информация. И у неё, информации, разумеется, тоже
есть своя единица измерения. Только, в отличие от длины, у информации
есть самая маленькая единица измерения и на её основе строятся единицы
измерения побольше. Единицы измерения больше - можно, меньше -
нельзя.
Основной единицей является не бит, а байт. Байт - это восемь бит или, что
то же, восемь двоичных цифр. Что содержат байты? В первую очередь,
целые числа, причём нс просто целые, а беззнаковые целые, иначе говоря.
248
натуральные. Формат хранения их прост и незатейлив - просто пишем
число в двоичном виде, вот так:
00000000, =0)0
000000012 = 1|0
Ничего большего, чем восемь единиц, поместить в байт нельзя. Значит 255
- максимальное число, которое байт может выразить. Двоичная запись
длинная - это плохо. Что ещё хуже, трудно не ошибиться в подсчёте
ноликов и единичек. Надо что-то делать. Как нетрудно заметить, восемь
это два раза по четыре, а четыре двоичные цифры в точности
соответствуют одной шестнадцатеричной. Результат:
00000000, = 00|6
00000001, = 01,6
11111111, = FF.b
Именно в такой форме обычно представляют то, что содержит память, что
записано в файле, что передано по каналу связи. В тех, разумеется,
случаях, когда вам точно надо это знать. Итак, один байт может хранить
целое число в диапазоне 0..255. Немедленно возникают два вопроса. А
если нам нужны отрицательные числа? А если нам этого диапазона мало -
а его всегда мало?
Ответ на первый вопрос. Ответ на вопрос - положительное или
отрицательно - содержит ровно один бит информации, или одну двоичную
цифру. Если из восьми двоичных цифр одна ушла на кодировку знака,
значит, диапазон нашего числа сократился в два раза. Это понятно. Что
сложнее, запись положительных чисел производится как обычно, а вот
запись отрицательных - в так называемом дополнительном коде.
Обрабатывать их гак удобнее, а вот понять глазами, что там записано -
сложнее.
Ответ на второй вопрос. Целые числа из одного байта есть во всех
традиционных языках, но только как вспомогательный инструмент.
Когда-то давно стандартом было два байта, теперь четыре. То есть если у
вас есть целая переменная - чуть нс сказал, если мы объявили целую
переменную - то в ней будет четыре байта, причём со знаком. Какой
диапазон она сможет вместить, посчитайте сами. Впрочем, бывают целые
числа и длиннее.
249
Теперь вернёмся к булевским переменным. Хотя в теории она должна
занимать только один бит, никто не мелочится. В большинстве языков под
неё выделяется один байт. Понимается это так - если ноль, то ложь, если
единица, то истина. Это стандартно, но обычно к процессу подходят более
гибко - если нс ноль, а что угодно, то истина.
Формат записи плавающих чисел сложный и очень сложный. Ни разу не
встречал человека, который мог бы, взглянув на плавающее число в
шестнадцатеричном виде, определить, что в нём хранится. Хуже того, этих
форматов минимум два, в смысле минимум два распространённых.
Стандартное плавающее число занимает четыре байта, но бывают и
больше.
О строках. Здесь грустно. Раньше было не очень грустно - один символ это
один байт. Теперь не угадаешь, может и два. Называется это словом
Юникод. Хорошая новость - закодировать можно почти любой символ,
включая китайские иероглифы. Кстати, базовый китайский компьютерный
стандарт включает 6900 иероглифов. Расширенный - плюс ещё столько
же.
Плохая новость - по внешнему виду нс угадаешь, какая кодировка,
традиционная или Юникод. Чтобы всё запутать окончательно, надо
наложить на это отсутствие стандартного метода записи строк в файл и
насладиться результатом. Что там за ужас с хранением строк в памяти, я
упоминать не буду.
Теперь очень важное понятие - слово. Относится оно, скорее, к железной
составляющей компьютера. Процессор не может обратиться к отдельному
биту. Он и к отдельному байту обратиться не может. Он может обратиться
только к слову. Обычно слово - четыре байта. Что отсюда следует для
простого программиста? Использование вместо переменной в четыре
байта аналогичной переменной в один байт экономит место, но, скорее
всего, не экономит время. Обработка чстырёхбайтового целого займёт
ровно столько же времени, сколько и однобайтового. Разумеется, есть
вариант - вы можете пожертвовать своим временем и написать какую-то
хитрую обработку по четыре однобайтовых числа за один раз. Оно вам
надо?
Более крупные единицы измерения информации служат только и именно
для измерения её количества. То есть объёма файлов, памяти, дисков и так
далее. Ни с какими простыми переменными они не соотносятся. Нет такой
простой переменной, которая занимала бы килобайт. Сложных - сколько
угодно. Массив или список легко займут хоть килобайт, хот ь мегабайт.
250
Килобайт это 1024 байта, что равно 2'° . С математической или
программистской точки зрения, смесь двоек и десяток выглядит как-то
неорганично. Скорее всего, кому-то хотелось иметь какой-то аналог
весового кило!рамма. Единицы крупнее:
251
Приложение G. Обмен данными с другими программами
через файлы
Логично завершить книгу, обсудив, как стыковать программы на Питоне с
программами на других языках или как написать единую программу
частично на Питоне, а частично на C++ или Дельфи. Написать одну
программу на нескольких языках сложно. Обмениваться данными между
двумя программами, написанными на разных языках, много проще. Самый
простой способ - обмениваться данными через файлы. Это медленно, это
не очень надёжно, это негибко - но это очень просто и очень понятно.
Этим и займемся.
252
Программа на Питоне с комментариями, которые будут длинными:
import struct
I-----------------------------------------------------------------------------------
def 3trToFile( S, F) :
length - len(S)
numOflnts « 10
data • struct.pack( '<14', numOflnts)
f.write(data)
x - 0.99“100
data - struct.pack( '<f4', x)
f.write(data)
stroka_l - 'And the silken sad uncertain rustling of each purple curtain'
strToFile( stroka_l, f)
f .closed
Теперь самый важный вопрос. Почему строки пишутся именно так, как
они пишутся здесь? Ответ - а как? Универсального способа не вообще.
253
Стандартный питонский способ через формат з не подходит - читающая
программа мало того, что не догадается, что перед ней строка. Даже если
каким-то чудом она это поймёт, ей неизвестно будет, сколько в строке
символов. Второй формат, который р, кажется более перспективным. Ведь
он специально для совместимости с Паскалем, который, как всем известно,
то же Дельфи, только в профиль. Но есть нюанс. Или два. Мы можем
записать - и прочитать - таким способом только короткие строки, до 255
символов. И, само собой, другим языкам программирования этот формат
совершенно неизвестен.
var
F : file;
numOflnts : integer;
int : integer;
fl : single;
strokal, stroka_2 : string;
i : integer;
<.................................................................................................................................... >
function strFromFile( var F : file) : string;
var
length : integer;
ch : char;
i : integer;
begin
BlockReadt F, length, 4);
result:-'';
for i:-l to length do begin
BlockReadt F, ch, 1);
result:-result ♦ ch;
end;
end;
(..................................................................................................................................... I
begin
AssignFilet F, 'd:\py.bin');
ReSet ( F, 1);
254
•nd,
stroka_l:-stxFromFile(F);
stroka_2:=strFromFile(F);
CloseFlle1F);
•nd:
ver
len : integer;
ch : char;
i : integer;
begin
len:«bength(s);
BlockWrite) F, len, 4);
lnt:-12345;
BlockWrite) F, int, 4);
fl:=123.45;
BlockWrite( F, fl, 4);
CloseFile (F);
255
•nd;
* coding: ср1251
import struct
def StrFromFile<F>:
result = ''
data = F.read(4)
length - struct.unpack('<14',data)
for i in xrange!0,length[0]):
data • F.read(l)
ch - struct.unpack( '<sl', data)
result • result > ch[0]
return result
data - F.read(4)
int = struct.unpack! '<14', data)
print Int
data = F.read(4)
fl - struct.unpack! '<f4', data)
print fl
stroka ■ StrFromFile(F)
print stroka
F.close()
Простой учебник
www.solon-press.ru
ООО "СОЛОН-Пресс»
123001, г. Москва, а/я 82
Телефоны: (495) 617-39-64, 617-39-65
E-mail: [email protected],
www.solon-press.ru