Программирование аркадных игр и обучение информатике

Программирование аркадных игр
и обучение информатике

Chapter 20: Форматирование

20.1 Десятичные числа

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

import random

for i in range(10):
    x=random.randrange(20)
    print(x)

Вывод выглядит ужасно: он выравнен по левому краю:

16
13
2
0
10
3
18
1
14
5

Мы можем использовать форматирование с помощью print, создав список чисел, выравненный по правому краю. Первым шагом будет использование команды format() на строке. Посмотрите ниже:

import random

for i in range(10):
    x=random.randrange(20)
    print("{}".format(x) )

Вышеприведённый код не выравнивает числа по правому краю, но он принуждает программу к использованию форматирования. Посмотрите, как заканчиваются строки при команде .format(). Строка на самом деле является инстанцией класса и этот объект обладает методами, которые можно вызвать. Один из них это format().

Функция format() не выведет фигурные скобки. Вместо этого, она заменит {} на значение, сохранённое в x. Выводи (ниже) выглядит точно так же, как и раньше.

7
15
4
12
3
8
7
15
12
8

Для выравнивания по правому краю, надо добавить больше информации между фигурными скобками {}:

import random

for i in range(10):
    x=random.randrange(20)
    print("{:2}".format(x) )
 7
15
 4
12
 3
 8
 7
15
12
 8

Так то лучше, мы выровняли числа! Но как это работает? { } говорят компьютеру о том, что мы будем форматировать число. После : внутри фигурных скобок, мы добавляет информацию по форматированию. Мы пишем 2 для задания длины поля в два символа. Длина поля говорит компьютеру, чтобы он попытался разместить число внутри поля длиной в 2 символа. По умолчанию, он будет стараться выравнивать числа по правому краю, а текст - по левому.

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

Что, если у нас числа побольше? Создадим список больших случайных чисел:

import random

for i in range(10):
    x=random.randrange(100000)
    print( "{:6}".format(x) )

Это выдаёт нам вывод, выровнянный по правому краю, но всё ещё не выглядящий хорошо:

 18394
 72242
 97508
 21583
 11508
 76064
 88756
 77413
  7930
 81095
 

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

import random

for i in range(10):
    x=random.randrange(100000)
    print( "{:6,}".format(x) )
65,732
30,248
13,802
17,177
 3,584
 7,598
21,672
82,900
72,838
48,557

Запятая должна находится перед полем, обозначающим длину, но не после. Запятые учитываются при вычислении длины поля. Например, 1,024 обладает длиной в 5, а не 4.

Мы можем вывести несколько значений, скомбинировав их в текст. Запустите нижеприведённый код.

x=5
y=66
z=777
print ("A - '{}' B - '{}' C - '{}'".format(x,y,z) )

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

A - '5' B - '66' C - '777'

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

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

x=5
y=66
z=777
print ("C - '{2}' A - '{0}' B - '{1}' C again - '{2}'".format(x,y,z) )

Заметьте, что, размещая число внутри фигурных скобок, мы можем задать порядок выведения параметров, переданных функции format(). Нумерация параметров начинается с 0, так что x считается параметром 0.

Мы всё ещё можем задать форматирование информации после двоеточия. Например:

x=5
y=66
z=777
print ("C - '{2:4}' A - '{0:4}' B - '{1:4}' C again - '{2:4}'".format(x,y,z) )

Мы можем заметить, что вышеприведённый код покажет значения, выровненные по правому краю, длиной в четыре символа:

C - ' 777' A - '   5' B - '  66' C again - ' 777'

20.2 Строки

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

my_fruit = ["Apples","Oranges","Grapes","Pears"]
my_calories = [4,300,70,30]

for i in range(4):
    print(my_fruit[i],"are",my_calories[i],"calories.")
Apples are 4 calories.
Oranges are 300 calories.
Grapes are 70 calories.
Pears are 30 calories.

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

my_fruit = ["Apples","Oranges","Grapes","Pears"]
my_calories = [4,300,70,30]

for i in range(4):
    print("{:7} are {:3} calories.".format(my_fruit[i],my_calories[i]) )
Apples  are   4 calories.
Oranges are 300 calories.
Grapes  are  70 calories.
Pears   are  30 calories.

Выглядит классно, точно так, как мы хотели. Но что, если мы не хотим, чтобы число было выровнено по правому краю, а текст - по левому? Мы можем использовать символы < и >, как это сделано в следующем примере:

my_fruit = ["Apples","Oranges","Grapes","Pears"]
my_calories = [4,300,70,30]

for i in range(4):
    print("{:>7} are {:<3} calories.".format(my_fruit[i],my_calories[i]) )
 Apples are 4   calories.
Oranges are 300 calories.
 Grapes are 70  calories.
  Pears are 30  calories.

20.3 Ведущие нули

Это создаёт неправильный вывод:

for hours in range(1,13):
    for minutes in range(0,60):
        print( "Time {}:{}".format(hours, minutes) )		
Time 8:56
Time 8:57
Time 8:58
Time 8:59
Time 9:0
Time 9:1
Time 9:2

Нам нужно использовать ведущие нули для отображения чисел на часах. Вместо указания 2 для длинны поля, нужно использовать 02. Это заполнит поле нулями, вместо пробелов.

for hours in range(1,13):
    for minutes in range(0,60):
        print( "Time {:02}:{:02}".format(hours, minutes) )		
Time 08:56
Time 08:57
Time 08:58
Time 08:59
Time 09:00
Time 09:01
Time 09:02

20.4 Числа с плавающей точкой

Мы также можем контролировать вывод с плавающей точкой. Оцените следующий код и его вывод:

x=0.1
y=123.456789
print( "{:.1}  {:.1}".format(x,y) )
print( "{:.2}  {:.2}".format(x,y) )
print( "{:.3}  {:.3}".format(x,y) )
print( "{:.4}  {:.4}".format(x,y) )
print( "{:.5}  {:.5}".format(x,y) )
print( "{:.6}  {:.6}".format(x,y) )
print()
print( "{:.1f}  {:.1f}".format(x,y) )
print( "{:.2f}  {:.2f}".format(x,y) )
print( "{:.3f}  {:.3f}".format(x,y) )
print( "{:.4f}  {:.4f}".format(x,y) )
print( "{:.5f}  {:.5f}".format(x,y) )
print( "{:.6f}  {:.6f}".format(x,y) )
0.1  1e+02
0.1  1.2e+02
0.1  1.23e+02
0.1  123.5
0.1  123.46
0.1  123.457

0.1  123.5
0.10  123.46
0.100  123.457
0.1000  123.4568
0.10000  123.45679
0.100000  123.456789

Формат .2 обозначает отображение числа с точностью в две цифры. К сожалению, это значит, что если мы выведем число 123, которое обладает тремя цифрами, то вместо его округления мы получим его научную запись: 1.2e+02.

Формат .2f (заметьте f) значит отображение числа с двумя цифрами после запятой. Так что число 1 будет отображено как 1.00, а число 1.5555 выведется как 1.56.

Программа также может задать длинну поля:

x=0.1
y=123.456789
print( "'{:10.1}'  '{:10.1}'".format(x,y) )
print( "'{:10.2}'  '{:10.2}'".format(x,y) )
print( "'{:10.3}'  '{:10.3}'".format(x,y) )
print( "'{:10.4}'  '{:10.4}'".format(x,y) )
print( "'{:10.5}'  '{:10.5}'".format(x,y) )
print( "'{:10.6}'  '{:10.6}'".format(x,y) )
print()
print( "'{:10.1f}'  '{:10.1f}'".format(x,y) )
print( "'{:10.2f}'  '{:10.2f}'".format(x,y) )
print( "'{:10.3f}'  '{:10.3f}'".format(x,y) )
print( "'{:10.4f}'  '{:10.4f}'".format(x,y) )
print( "'{:10.5f}'  '{:10.5f}'".format(x,y) )
print( "'{:10.6f}'  '{:10.6f}'".format(x,y) )

Формат 10.2f не значит 10 цифр до дробной части и 2 после. Он означает общую длинну поля 10. Так что в итоге у нас будет 7 цифр до дробной части, разделитель, который считается ещё одним символом, и 2 символа в дробной части.

'       0.1'  '     1e+02'
'       0.1'  '   1.2e+02'
'       0.1'  '  1.23e+02'
'       0.1'  '     123.5'
'       0.1'  '    123.46'
'       0.1'  '   123.457'

'       0.1'  '     123.5'
'      0.10'  '    123.46'
'     0.100'  '   123.457'
'    0.1000'  '  123.4568'
'   0.10000'  ' 123.45679'
'  0.100000'  '123.456789'

20.5 Вывод долларов и центов

Если вы хотите распечатать дробное число с ценой, вы можете использовать f. Ознакомьтесь с примером:

cost1 = 3.07
tax1 = cost1 * 0.06
total1 = cost1 + tax1

print("Cost:  ${0:5.2f}".format(cost1) )
print("Tax:    {0:5.2f}".format(tax1) )
print("------------")
print("Total: ${0:5.2f}".format(total1) )

Помните! Легко подумать, что %5.2f будет означать пять цифр, дробный разделитель и затем ещё два символа. Но это не так. Это значит, что общая длинна поля - пять, включая дробный разделитель и две цифры после него. Вот вывод:

Cost:  $ 3.07
Tax:     0.18
------------
Total: $ 3.25

Осторожно! В вышеприведённом коде есть ошибка, частая в работе с финансовыми переводами. Можете ли вы её заметить? Попробуйте найти её в расширенном варианте кода нижнего примера:

cost1 = 3.07
tax1 = cost1 * 0.06
total1 = cost1 + tax1

print("Cost:  ${0:5.2f}".format(cost1) )
print("Tax:    {0:5.2f}".format(tax1) )
print("------------")
print("Total: ${0:5.2f}".format(total1) )

cost2 = 5.07
tax2 = cost2 * 0.06
total2 = cost2 + tax2

print()
print("Cost:  ${0:5.2f}".format(cost2) )
print("Tax:    {0:5.2f}".format(tax2) )
print("------------")
print("Total: ${0:5.2f}".format(total2) )


print()
grand_total = total1 + total2
print("Grand total: ${0:5.2f}".format(grand_total) )

Here's the output:

Cost:  $ 3.07
Tax:     0.18
------------
Total: $ 3.25

Cost:  $ 5.07
Tax:     0.30
------------
Total: $ 5.37

Grand total: $ 8.63

Заметили ошибку? Нужно опасаться ошибок с округлением. Посмотрите на этот пример. Кажется, что сумма должна быть $ 8.62, но это не так.

Формат вывода не меняет число, только вывод. Если мы поменяем форматирование вывода так, чтобы оно включало три знака в дробной части, причина ошибки становится более очевидной:

Cost:  $3.070
Tax:    0.184
------------
Total: $3.254

Cost:  $5.070
Tax:    0.304
------------
Total: $5.374

Grand total: $8.628

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

cost1 = 3.07
tax1 = round(cost1 * 0.06,2)
total1 = cost1 + tax1

print("Cost:  ${0:5.2f}".format(cost1) )
print("Tax:    {0:5.2f}".format(tax1) )
print("------------")
print("Total: ${0:5.2f}".format(total1) )

cost2 = 5.07
tax2 = round(cost2 * 0.06,2)
total2 = cost2 + tax2

print()
print("Cost:  ${0:5.2f}".format(cost2) )
print("Tax:    {0:5.2f}".format(tax2) )
print("------------")
print("Total: ${0:5.2f}".format(total2) )


print()
grand_total = total1 + total2
print("Grand total: ${0:5.2f}".format(grand_total) )
Cost:  $ 3.07
Tax:     0.18
------------
Total: $ 3.25

Cost:  $ 5.07
Tax:     0.30
------------
Total: $ 5.37

Grand total: $ 8.62

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

x=1234.5678
print( round(x,2) )
print( round(x,1) )
print( round(x,0) )
print( round(x,-1) )
print( round(x,-2) )

Посмотрите ниже, чтобы разобраться, что случается, если функции round() подаются негативные значения вроде -2 для количества цифр после запятой:

1234.57
1234.6
1235.0
1230.0
1200.0

20.6 Применение в Pygame

Не обязательно использовать форматирование строк только для выражения print. Пример timer.py использует строчное форматирование и рисует результирующий текст на экране, создавая тем самым таймер:

# Используем форматирование строк в python для добавления ведущих нулей
output_string = "Time: {0:02}:{1:02}".format(minutes,seconds)

# Нарисовать на экране
text = font.render(output_string,True,black)
screen.blit(text, [250,250])

20.7 Проверка пройденного

  1. Возьмите следующую программу:
    score=41237
    highscore=1023407
    
    print("Очки:      "+str(score) )
    print("Рекорд: "+str(highscore) )
    

    Которая сейчас выводит:

    Score:      41237
    High score: 1023407
    

    Используйте форматирование метода print так, чтобы вывод выглядел следующим образом:

    Score:          41,237
    High score:  1,023,407
    
  2. Создайте программу, идущая по циклу от 1 до 20 и выводящая, чему равно текущее число в минус первой степени. Используйте форматирование метода print чтобы достигнуть следующего вывода:
    1/1  = 1.0   
    1/2  = 0.5   
    1/3  = 0.333 
    1/4  = 0.25  
    1/5  = 0.2   
    1/6  = 0.167 
    1/7  = 0.143 
    1/8  = 0.125 
    1/9  = 0.111 
    1/10 = 0.1   
    1/11 = 0.0909
    1/12 = 0.0833
    1/13 = 0.0769
    1/14 = 0.0714
    1/15 = 0.0667
    1/16 = 0.0625
    1/17 = 0.0588
    1/18 = 0.0556
    1/19 = 0.0526
    1/20 = 0.05  
    
  3. Напишите рекурсивную функцию, которая будет вычислять числа фибоначчи, при этом применяя форматирование вывода. Ваш результат должен выглядеть чем-то вроде:
     1 -       0
     2 -       1
     3 -       1
     4 -       2
     5 -       3
     6 -       5
     7 -       8
     8 -      13
     9 -      21
    10 -      34
    11 -      55
    12 -      89
    13 -     144
    14 -     233
    15 -     377
    16 -     610
    17 -     987
    18 -    1597
    19 -    2584
    20 -    4181
    21 -    6765
    22 -   10946
    23 -   17711
    24 -   28657
    25 -   46368
    26 -   75025
    27 -  121393
    28 -  196418
    29 -  317811
    30 -  514229
    31 -  832040
    32 - 1346269
    33 - 2178309
    34 - 3524578
    35 - 5702887
    
  4. Почему оно выполняется так медленно? Как можно заставить его выполняться быстрее?

You are not logged in. Log in here and track your progress.