Лог №2
Контрольная работа
Вопрос: попадает ли величина eπ-π в интервал [19.99, 20]?
Ответ:
1 Python 3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 10:38:22) [MSC v.1600 32 bit (Intel)] on win32
2 Type "copyright", "credits" or "license()" for more information.
3 >>> import math
4 >>> 3**2
5 9
6 >>> math.pi
7 3.141592653589793
8 >>> math.e
9 2.718281828459045
10 >>> math.e ** math.pi - math.pi
11 19.99909997918947
12 >>> 19.999 <= math.e ** math.pi - math.pi <= 20.0
13 True
Комментарии к домашней работе
Перевести 2 радиана в градусы в питоне легко, а вот нормально это дело сформатировать...
Питон пытается изображать число в виде x*10n, и поэтому ничего не выходит.
И хелпы тут не помогают...
Но если на сайте docs.python.org поискать в Library reference описание типа str, то в нём, в описании str.format, можно найти синтаксис языка шаблонов для str.format.
Среди дикой кучи возможностей, которые там есть, можно найти, что в конце строки форматирования можно приписать букву, описывающую способ выведения значения:
пустота – для строк как s, для целых чисел как d, для дробных чисел как g
s – String format. This is the default type for strings and may be omitted.
b – Binary format. Outputs the number in base 2.
c – Character. Converts the integer to the corresponding unicode character before printing.
d – Decimal Integer. Outputs the number in base 10.
o – Octal format. Outputs the number in base 8.
x – Hex format. Outputs the number in base 16, using lower- case letters for the digits above 9.
X – Hex format. Outputs the number in base 16, using upper- case letters for the digits above 9.
n – Number. This is the same as 'd', except that it uses the current locale setting to insert the appropriate number separator characters.
e – Exponent notation. Prints the number in scientific notation using the letter ‘e’ to indicate the exponent. The default precision is 6.
E – Exponent notation. Same as 'e' except it uses an upper case ‘E’ as the separator character.
f – Fixed point. Displays the number as a fixed-point number. The default precision is 6.
F – Fixed point. Same as 'f', but converts nan to NAN and inf to INF.
g – General format. For a given precision p >= 1, this rounds the number to p significant digits and then formats the result in either fixed-point format or in scientific notation, depending on its magnitude.
Попробуем:
1 >>> "Два радиана это {:e} градусов".format(math.degrees(2))
2 'Два радиана это 1.145916e+02 градусов'
3 >>> "Два радиана это {:f} градусов".format(math.degrees(2))
4 'Два радиана это 114.591559 градусов'
5 >>> "Два радиана это {:x} градусов".format(math.degrees(2))
6 Traceback (most recent call last):
7 File "<pyshell#12>", line 1, in <module>
8 "Два радиана это {:x} градусов".format(math.degrees(2))
9 ValueError: Unknown format code 'x' for object of type 'float'
Логично, дробные числа довольно непонятно, как приводить в шестнадцатеричный формат.
Если мы в питоне хотим ввести шестнадцатеричное число, то мы пишем 0x20 (получается 32), для двоичных 0b100 (получается 8). Логично и при выводе чисел на экран приделывать к ним такие же префиксы, чтобы не запутаться. (Но питон за нас этого не делает, это должны делать мы своими руками).
Списки
Список – это свалка чего-нибудь через запятую.
Мы можем захотеть из списка взять что-нибудь по номеру. Нумерация, как и в строках, начинается с 0. Как и в строках, есть нумерация с обратного конца, которая начинается с -1 (то есть -1 – это последний элемент списка, -2 предпоследний, и т.п.).
Длина, как и для строк:
Мы можем положить в список что угодно, в том числе и другие списки. Никакого специального способа доставать элемент из вложенного списка в этом случае нет: нужно сначала достать сам вложенный список, а потом из него достать элемент. Впрочем, это совсем просто:
У списков есть полезные методы:
L.append(x) – добавить элемент x в конец списка L
L.insert(n, x) – воткнуть элемент x перед позицией с номером n (у остального списка нумерация при этом съедет)
L + M – сделать новый список, являющийся конкатенацей L и M, то есть склеенный из элементов одного и за ними элементов второго. Мы склеиваем новый список из L и M, а сами списки L и M при этом не меняются.
1 >>> w.append([7,8])
2 >>> w
3 [[1, 2], [3, 4], [5, 6], [7, 8]]
4 >>> z
5 ['a', 'b', 'c', 'd']
6 >>> z.append('hello')
7 >>> z
8 ['a', 'b', 'c', 'd', 'hello']
9 >>> z.insert(3, 'e')
10 >>> z
11 ['a', 'b', 'c', 'e', 'd', 'hello']
12 >>> z.insert(-1, 'e')
13 >>> z
14 ['a', 'b', 'c', 'e', 'd', 'e', 'hello']
15 >>> w
16 [[1, 2], [3, 4], [5, 6], [7, 8]]
17 >>> w[1].insert(2, 'r')
18 >>> w
19 [[1, 2], [3, 4, 'r'], [5, 6], [7, 8]]
20 >>> w[1].append('y')
21 >>> w
22 [[1, 2], [3, 4, 'r', 'y'], [5, 6], [7, 8]]
23 >>> w + y
24 [[1, 2], [3, 4, 'r', 'y'], [5, 6], [7, 8], 1, 2, 3, 4]
25 >>> y + w
26 [1, 2, 3, 4, [1, 2], [3, 4, 'r', 'y'], [5, 6], [7, 8]]
27 >>> y
28 [1, 2, 3, 4]
В отличие от строк, списки мы менять можем:
1 >>> y[2] = 'x'
2 >>> y
3 [1, 2, 'x', 4]
4 >>> w
5 [[1, 2], [3, 4, 'r', 'y'], [5, 6], [7, 8]]
6 >>> w[1][2] = 5
7 >>> w
8 [[1, 2], [3, 4, 5, 'y'], [5, 6], [7, 8]]
9 >>> y = ['a', 'b', 'c']
10 >>> x = ['a', 'b']
11 >>> y[1] = 2
12 >>> y
13 ['a', 2, 'c']
14 >>> y = ['a', 'b', 'c', 'd', 'e', 'f']
15 >>> y
16 ['a', 'b', 'c', 'd', 'e', 'f']
Как и со строками, мы можем брать у списков слайсы.
1 >>> y[2:5]
2 ['c', 'd', 'e']
3 >>> y[0:0]
4 []
5 >>> y[0:6]
6 ['a', 'b', 'c', 'd', 'e', 'f']
7 >>> y[3:]
8 ['d', 'e', 'f']
9 >>> y[:3]
10 ['a', 'b', 'c']
11 >>> y[:]
12 ['a', 'b', 'c', 'd', 'e', 'f']
13 >>> y[-3:]
14 ['d', 'e', 'f']
15 >>> w
16 [[1, 2], [3, 4, 5, 'y'], [5, 6], [7, 8]]
17 >>> w[1][:2]
18 [3, 4]
19 >>> w[1] = None
20 >>> w
21 [[1, 2], None, [5, 6], [7, 8]]
Есть два способа удалять элемент из списка:
как правило, дурацкий – для этого есть новая конструкция del x[i] – удалить элемент по номеру (при этом нумерация того, что после него, съезжает)
как правило, удобный – удалить элемент из списка и заодно сразу положить его в переменну – метод L.pop() (для последнего элемента) или L.pop(n) (для элемента с индексом n)
Программы
Питонский код можно сохранить в файл и этот файл исполнять.
Но теперь чтобы увидеть что-то на экране, нам недостаточно просто написать какое-то выражение, нужно сказать, что мы хотим его напечатать. Для этого есть функция print(anything)
1 print("Hello, world!")
Вот что получится, если отыскать менюшку Run и пункт Run module (которое сокращается до кнопочки F5).
Первые две строки – это мусор, который добавляет IDLE, чтобы сказать, что это он жив. Они не играют никакой роли, и я не буду их дальше воспроизводить.
Циклы
Любимое дело программиста – заставлять компьютер повторять тупую работу кучу раз (чтобы программист при этом сидел и пил кофе).
Предположим, мы хотим пройтись по элементам списка и для каждого элемента списка выполнить пару строк кода. Это дословно так и переводится на английский, а там и на питонский:
Выходит:
Hello, world! Element number is 1 Hello, world! Element number is 2 Hello, world! Element number is 3 Hello, world! Element number is 4 Hello, world! Element number is 5
Важно: отступ (т.е. количество пробелов от начала строки) в этом случае играет существенную роль. В цикле повторяются только те строки, которые пишутся с отступом, а всё, идущее за ними, что пишется с тем же отступом, что и for, будет исполняться после того, как цикл кончится.
Строки с отступом после for называют телом цикла, а переменную, указанную между for и in называют переменной цикла.
Конструкция for имя_переменной in список: тело-цикла значит следующее:
- имя_переменной = первый элемент списка
- тело-цикла
- имя_переменной = второй элемент списка
- тело-цикла
- имя_переменной = третий элемент списка
- тело-цикла
- ...
(до тех пор, пока список не кончится)
Словом итерация обозначают один раз, когда исполнилось тело цикла.
Циклы: по буквам строки
Мы уже видели, что строка ведёт себя как список букв. Вот ещё одно подтверждение:
H e l l o , w o r l d ! Done
Заодно увидели, что тело цикла действительно заканчивается, когда мы перестаём писать строки с отступами.
Циклы: по каждому второму
Для прохода по каждому второму элементу списка в питоне нет специальных циклов. Зато есть специальные слайсы, которые нам позволяют сделать список из каждого второго элемента списка:
Третье число в слайсе и есть шаг, с которым мы идём.
Теперь, чтобы напечатать каждую вторую букву строки, мы можем сделать так:
H l o o l !
Циклы с номером индекса в руках (range)
В питоне есть функция, которая делает список из всех чисел от 0 до n-1. Называется range.
Точнее сказать, эта функция делает не список, а штуку, которая называется итератор. Она не занимает почти нисколько памяти (можно легко сделать итератор на число шагов больше, чем число байт в памяти компьютера), и с ней почти ничего нельзя делать: только пройти по ней циклом for или сделать из неё список функцией list. (А это почти одно и то же).
range может принимать до трёх аргументов, они будут такие же, как у слайсов: начало, конец, шаг.
range кушает только числа
Поэтому если мы хотим список индексов строки "helo", нам нужно сначала получить её длину:
Теперь мы можем пройти по индексам букв строки, и для каждого индекса вывести его и букву строки, лежащую по такому индексу, например:
0 H 1 e 2 l 3 l 4 o 5 , 6 7 w 8 o 9 r 10 l 11 d 12 !
Именованные аргументы у функций
Кроме простых функций типа sin(x), с которыми никаких ухищрений не хочется, в питоне бывают функции, которым можно давать множество разнообразнейших аргументов, большинство из которых давать не обязательно, а значит, нужно уметь различать, о каком аргументе мы говорим.
Для этого можно передавать аргументы по имени. Например, у функции print кроме неограниченного количества безымянных аргументов (которые она просто выводит на экран, разделяя пробелами), есть ещё парочка именованных:
sep говорит о том, какой строкой склеивать выводимые значения (по умолчанию print склеивает их через пробел, и мы можем этим управлять)
end говорит о том, что писать в конце после всего – обычно питон в этом месте печатает символ "перенос строки" (пишется "\n"), выведение которого заставляет курсор уехать в начало следующей строки
1 >>> print(1, 2, 3, sep=', ')
2 1, 2, 3
3 >>> print(1, 2, 3, sep=' little indians ')
4 1 little indians 2 little indians 3
5 >>> print(1, 2, 3, end='')
6 1 2 3
7 >>> print(1, sep=' little indians ')
8 1
9 >>> cell = 1
10 >>> print(cell, sep=' little indians ')
11 1
12 >>> help(print)
13 Help on built-in function print in module builtins:
14
15 print(...)
16 print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
17
18 Prints the values to a stream, or to sys.stdout by default.
19 Optional keyword arguments:
20 file: a file-like object (stream); defaults to the current sys.stdout.
21 sep: string inserted between values, default a space.
22 end: string appended after the last value, default a newline.
23 flush: whether to forcibly flush the stream.
24
25 >>> print(1, 'a', 2, 'b')
26 1 a 2 b
27 >>> print(1, 'a', 2, 'b', sep=';')
28 1;a;2;b
Кавычки в кавычках
Раз мы заговорили о специальных символах, вот ещё пара слов.
Если мы хотим вывести строку с кавычками или апострофами, у нас могут быть проблемы:
Питон после 'I' решил, что строка кончилась, и это мы к строке прилепили какой-то ерунды.
Чтобы он понял, что апостроф не является концом строки, мы должны сделать так, чтобы апостроф не являлся символом завершения строки. Например, взять строку в другой тип кавычек:
Но этот приём не работает, если мы хотим в строке говорить о нескольких типах кавычек сразу.
Тогда есть спецсимвол \, который влияет на смысл стоящего за ним символа:
если за ним стоит символ, играющий сам по себе для строк особый символ (кавычка или \), то этот символ перестаёт играть особый смысл, и питон понимает, что мы просто хотим этот символ иметь как часть строки.
'\n' обозначает перенос строки
'\t' обозначает табулятор
- ... есть ещё несколько аналогичных сокращений, которыми никто никогда не пользуется
'\u0020' мы можем вводить уникодный символ по его коду (символ с кодом 20 – это пробел)
Вложенные циклы
Тело цикла может содержать и другие циклы.
Образ мысли при анализе такой программы такой: мы смотрим на внешний цикл и думаем "для каждого элемента из списка выполнить вот этот блок", а дальше смотрим на то, что делает этот блок, и понимаем, что будет, если его повторить нужное количество раз.
Например, хотим мы вывести список списков в виде таблицы:
1 2 3 4 5 6
Надо заметить, что более простой подход не совем работает:
Впрочем, он как раз показывает, за какой именно кусок отвечает внешний цикл – сейчас он выводит по одной строке за итерацию.
Графика
Минимальная графическая программа на питоне выглядит так:
Она рисует окошко, которое, кажется, ничего не делает.
На самом деле, любое окошко отвечает каждый момент на кучу действий: то пользователь пошевелил мышкой, то нажал что-то на клавиатуре, то окошко нужно нарисовать, то уничтожить, то свернуть, то оно скрылось, то его нужно перерисовать... Жизнь графической программы – это постоянное ожидание входящих событий и реакция на них. Ожидание событий происходит в "главном цикле программы" – "main loop".
Поэтому до того, как мы не вызовем root.mainloop(), окошко ещё не появится (впрочем, IDLE может создать иллюзию, что root.mainloop() не нужен; но IDLE не единственный способ запускать питонские программы!), и, более того, до тех пор, пока окошко не будет уничтожено, питон не выйдет из root.mainloop() и не перейдёт к следующей строке.
Поэтому в любой графической программе последняя строка должна быть root.mainloop()
В окошке мы можем располагать разные графические элементы (умное слово "виджеты"): кнопки, менюшки, поля ввода, скроллбары и прпрпрпр.
Пока что мы будем играть с графическим элементом "холст" (Canvas).
Любой графический элемент нужно сначала создать (при этом сказать, в каком окошке он находится), а потом сказать питону, каким образом мы хотим его расположить в родительском окошке. Самый простой способ это сказать – метод pack(). Он просто располагает элементы стопочкой.
Мы можем сказать pack(), чтобы элемент занимал всё пространство по осям x, y, или обеим, и растягивался/сужался, когда меняется размер родительского окна.
Это и делает пример выше.
Наконец, холст. Холст нужен для того, чтобы рисовать!
Для этого у него есть методы:
create_oval – рисует овал или круг; овал задаётся как вписанный в прямоугольник, расположение которого мы задаём по левому-верхнему и правому-нижнему углу
create_line – рисует линию
create_rectangle – прямоугольник
- и множество других
Которые принимают координаты, fill для заливки, outline для рамки, width для толщины рамки, и кучу других (бес)полезных параметров.
Все координаты считаются от левого-верхнего угла экрана.
Всё это прекрасно описал Фридрих Лундх, правда, он описал это для второго питона. Существенное отличие, впрочем, только одно: во втором питоне модуль назывался Tkinter, а теперь tkinter
1 import tkinter as tk
2 root = tk.Tk()
3 canvas = tk.Canvas(root, width=500, height=200, background="blue")
4 canvas.pack(fill="both", expand="yes")
5 canvas.create_oval(50,100, 150,200, fill="pink", outline="red")
6 canvas.create_line(150,100, 300,100, fill="pink", width=30)
7 canvas.create_line(200,100, 200,200, fill="pink", width=30)
8 canvas.create_line(300,100, 300,200, fill="pink", width=30)
9 root.mainloop()
Пример: японский ёжик
Если мы хотим задать линию не двумя парами координат, а углом, длиной и точкой начала, то нам придётся вспомнить немного тригонометрии.
Впрочем, всё просто. Если от начала координат мы хотим уйти на расстояние L в направлении alpha, то мы придём в точку:
x = L * cos(alpha)
y = L * sin(alpha)
А если не от начала координат, то к этим x, y, нужно прибавить наше смещение. Например:
1 import tkinter as tk
2 import math
3 root = tk.Tk()
4 canvas = tk.Canvas(root, width=500, height=500, background="white")
5 canvas.pack(fill="both", expand="yes")
6
7 for angle in range(0, 360, 20):
8 x0 = 200
9 y0 = 200
10 x1 = math.cos(math.radians(angle)) * 100 + x0
11 y1 = math.sin(math.radians(angle)) * 100 + y0
12 canvas.create_line(x0,y0, x1,y1, fill='red', width=10)
13
14 root.mainloop()
Вопрос на эрудированность: почему он японский?