Programar Juegos Arcade
con Python y Pygame

Chapter 20: Formatos

Aquí debajo puedes encontrar una tabla de referencias rápidas para cuando formatees un texto. Para una explicación detallada de cómo funciona el formateo de un texto, deberás seguir leyendo.

Número Formato Salida Descripción
3.1415926 {:.2f} 3.14 2 lugares decimales
3.1415926 {:+.2f} +3.14 2 lugares decimales con signo
-1 {:+.2f} -1.00 2 lugares decimales con signo
3.1415926 {:.0f} 3 Sin decimales (se redondeará)
5 {:0>2d} 05 Añade ceros por la izquierda
1000000 {:,} 1,000,000 Formato numérico con separador coma
0.25 {:.2%} 25.00% Formato porcentual
1000000000 {:.2e} 1.00e+09 Notación exponencial
11 {:>10d}         11 Alineación a la derecha
11 {:<10d} 11 Alineación a la izquierda
11 {:^10d}     11 Centrado

20.1 Números Decimales

Intenta ejecutar el siguiente programa, imprime unos cuantos números aleatorios.

import random

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

La salida está alineada por la izquierda y el aspecto de los números es horrible:

16
13
2
0
10
3
18
1
14
5

Podemos utilizar el formateo para que, alineándolos hacia la derecha, la lista de números presente un mejor aspecto. El primer paso es usar el comando format sobre la cadena. Observa:

import random

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

Esto consigue que nuestro programa esté a un paso de alinear a la derecha el número, pero aún no hemos acabado. Observa que la cadena termina con .format(x). Realmente, todas las cadenas son instancias de la clase llamada String. Esta clase posee unos métodos que podemos invocar. Uno de ellos es el método format.

La función format no imprimirá las llaves {}, en su lugar las reemplazará con el valor contenido en x. El resultado (debajo) se parece a lo previamente ya habíamos obtenido.

7
15
4
12
3
8
7
15
12
8

Para alinear a la derecha, añadimos información extra sobre cómo se debe formatear el número contenido entre las llaves {}:

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
 

Esto está mejor; hemos alineado a la derecha los números! Pero cómo funciona? La expresión :2 que hemos añadido es todo menos intuitiva.

Vamos a desglosarlo: los símbolos { } le dicen al ordenador que vamos a formatear un número. A continuación del símbolo :, dentro de las llaves, se encuentra la información para el formato. En nuestro caso es un 2 que especifica un ancho de campo de dos caracteres. Este valor le indica al ordenador que tiene que ajustar el número dentro de un campo de dos caracteres de ancho. Por defecto, el intentaría justificar a la derecha los números y a la izquierda el texto.

Mejor aún, el programa ya no necesita llamar a str( ) para convertir el número a cadena de texto. Podemos dejar fuera las conversiones a cadena.

¿Pero qué sucede si tienes números grandes? Bien, primero creamos unos números aleatorios grandes:

import random

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

Esto produce un resultado justificado a la derecha, pero aún no luce todo lo bien que debiera:

 18394
 72242
 97508
 21583
 11508
 76064
 88756
 77413
  7930
 81095
 

Pero, ¿dónde están las comas? Esta lista tendría un mejor aspecto poniendo separadores cada tres dígitos. Observa a continuación cómo lo hacemos :

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

Hemos añadido una coma después del especificador de ancho de campo, con lo que ahora nuestros números tienen comas. La coma debe ir después del especificador de ancho de campo, nunca antes. Las comas son incluidas en el cálculo del ancho de campo. Por ejemplo, 1,024 tiene un ancho de campo de 5, y no de 4.

Podemos imprimir varios valores y combinarlos con texto. Ejecuta el siguiente código:

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

El programa sustituirá las llaves por números imprimiendo, además, el texto que hay en la cadena entrecomillada:

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

Si existe un conjunto de tres llaves, el ordenador espera encontrar tres valores que ordenar con el comando format. El primer valor que aparezca en la lista sustituirá a la primera llave.

Algunas veces queremos que un determinado valor aparezca por duplicado, o a lo mejor, queremos que aparezca en un orden distinto al que se introduce con la función format.

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

Observa que al colocar números entre las llaves, estamos especificando el orden en que queremos que los parámetros sean imprimidos por función format. Los parámetros los enumeramos empezando por 0, de forma que x es considerado el parámetro 0.

También podemos especificar la información de formato después de los dos puntos. Por ejemplo:

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

Podemos ver que el código anterior mostrará los valores justificados hacia la derecha y con un ancho de campo de tamaño cuatro:

C - ' 777' A - '   5' B - '  66' C y otra vez - ' 777'

20.2 Cadenas

Veamos ahora cómo formateamos cadenas de texto.

La siguiente lista tiene un aspecto horrible.

mi_fruta = ["Manzanas", "Naranjas", "Uvas", "Peras"]
mis_calorias = [4, 300, 70, 30]

for i in range(4):
    print(mi_fruta[i], "son", mis_calorias[i], "calorías.")
Manzanas son 4 calorías.
Naranjas son 300 calorías.
Uvas son 70 calorías.
Peras son 30 calorías.

Ahora intentémoslo usando el comando format. Observa cómo podemos introducir texto adicional y más de un valor sobre la misma línea.

mi_fruta = ["Manzanas", "Naranjas", "Uvas", "Peras"]
mis_calorias = [4, 300, 70, 30]

for i in range(4):
    print("{:7} son {:3} calorías.".format(mi_fruta[i], mis_calorias[i]) )
Manzanas  son   4 calorías.
Naranjas son 300 calorías.
Uvas  son  70 calorías.
Peras   son  30 calorías.

Esto luce mejor y se parece más a lo que estamos buscando. ¿Pero qué sucedería si no quisiéramos los números justificados a la derecha y el texto a la izquierda? Pues podemos usar los caracteres < y > tal como se ve a continuación:

mi_fruta = ["Manzanas", "Naranjas", "Uvas", "Peras"]
mis_calorias = [4, 300, 70, 30]

for i in range(4):
    print("{:>7} son {:<3} calorías.".format(mi_fruta[i], mis_calorias[i]) )
 Manzanas son 4   calorías.
Naranjas son 300 calorías.
 Uvas son 70  calorías.
  Peras son 30  calorías.

20.3 Ceros al Principio

Lo siguiente produce una salida que no está bien del todo:

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

Necesitamos unos ceros al principio para poder mostrar los números como se ven en los relojes. En lugar de emplear 2 para el ancho de campo, utiliza 02. Esto rellenará el campo con ceros en lugar de con espacios.

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

20.4 Números en Coma Flotante (Reales)

También podemos controlar los resultados en coma flotante. Examina el siguiente código y su salida:

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

Un formato del tipo .2 significa que el número será mostrado con una precisión de dos dígitos. Lamentablemente, esto quiere decir, que si mostramos el número 123, que tiene tres cifras significativas, en lugar de redondearlo, lo imprimirá en notación científica: 1.2e+02.

Un formato del tipo .2f (observa la f) quiere decir que mostremos el número con dos dígitos después del punto/coma decimal. De forma que 1 aparecerá como 1.00 y 1.5555 como 1.56.

También podemos especificar un carácter de ancho de campo en el programa:

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))

El formato 10.2f no significa que sean 10 dígitos antes y dos después del punto/coma decimal. Indica un ancho de campo total de 10. De esta forma, habrán 7 dígitos antes del punto/coma decimal, el punto/coma contará como uno más, y dos dígitos después.

'       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 Imprimir Dólares y Centavos

Si quieres usar un punto/coma para los precios, puedes usar una f.Observa:

precio1  = 3.07
impuesto1   = precio1 * 0.06
total1 = precio1 + impuesto1

print("Precio:  ${0:5.2f}".format(precio1) )
print("Impuesto:    {0:5.2f}".format(impuesto1) )
print("------------")
print("Total: ${0:5.2f}".format(total1) )

¡Recuerda! Sería más fácil pensar que %5.2f significaría 5 dígitos, un punto/coma, seguido de dos dígitos. Pero no es así, Esto indica un ancho de campo total de 5, incluyendo el punto/coma decimal y los dos dígitos siguientes. Ésta es la salida:

Precio:  $ 3.07
Impuesto:     0.18
------------
Total: $ 3.25

Peligro, cuando trabajamos con transacciones financieras, el código anterior tiene un error muy común ¿Puedes verlo? Intenta hacerlo con el código expandido siguiente:

precio1  = 3.07
impuesto1   = precio1 * 0.06
total1 = precio1 + impuesto1

print("Precio:  ${0:5.2f}".format(precio1))
print("Impuesto:    {0:5.2f}".format(impuesto1))
print("------------")
print("Total: ${0:5.2f}".format(total1))

precio2  = 5.07
impuesto2   = precio2 * 0.06
total2 = precio2 + impuesto2

print()
print("Precio:  ${0:5.2f}".format(precio2))
print("Impuesto:    {0:5.2f}".format(impuesto2))
print("------------")
print("Total: ${0:5.2f}".format(total2))


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

Ésta es la salida:

Precio:  $ 3.07
Impuesto:     0.18
------------
Total: $ 3.25

Precio:  $ 5.07
Impuesto:     0.30
------------
Total: $ 5.37

Gran total: $ 8.63

¿Localizas el error? ¡Tienes que vigilar los errores de redondeo! Observa el ejemplo, pareciera que el total debiera ser $ 8.62, pero no es así. El formato de impresión no modifica el número, sólo el resultado! Si modificamos el formato de impresión para que incluya tres dígitos después del punto/coma decimal, la razón del error aparecerá más claramente:

Precio:  $3.070
Impuesto:    0.184
------------
Total: $3.254

Precio:  $5.070
Impuesto:    0.304
------------
Total: $5.374

Gran total: $8.628

Una vez más, el formato de visualización no cambia el número. Debes usar el comando round para cambiar el valor y redondearlo verdaderamente. Observa:

precio1 = 3.07
impuesto1 = round(precio1 * 0.06,2)
total1 = precio1 + impuesto1

print("Precio:  ${0:5.2f}".format(precio1))
print("Impuesto:    {0:5.2f}".format(impuesto1))
print("------------")
print("Total: ${0:5.2f}".format(total1))

precio2 = 5.07
impuesto2 = round(precio2 * 0.06,2)
total2 = precio2 + impuesto2

print()
print("Precio:  ${0:5.2f}".format(precio2))
print("Impuesto:    {0:5.2f}".format(impuesto2))
print("------------")
print("Total: ${0:5.2f}".format(total2))


print()
gran_total = total1 + total2
print("Gran total: ${0:5.2f}".format(gran_total))
Precio:  $ 3.07
Impuesto:     0.18
------------
Total: $ 3.25

Precio:  $ 5.07
Impuesto:     0.30
------------
Total: $ 5.37

Gran total: $ 8.62

El comando round controla cuántos dígitos después del punto/coma decimal debemos redondear. Nos devuelve el valor redondeado pero sin cambiar el valor original. Observa:

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

Observa debajo para que veas, cómo, el modo en que alimentamos la función round() con valores como -2, para el número de dígitos luego del punto/coma decimal, afecta al resultado:

1234.57
1234.6
1235.0
1230.0
1200.0

20.6 Su Uso en Pygame

No tenemos que usar el formateo de cadenas solo para declaraciones de impresión. El ejemplo timer_es.py utiliza formateo de cadena para volcar en pantalla un texto que simula un reloj:

# Usamos el formateo de cadenas de Python para añadir ceros al principio
cadena_de_salida = "Hora: {0:02}:{1:02}".format(minutos,segundos)

# Lo volcamos en la pantalla
texto = font.render(cadena_de_salida, True, NEGRO)
screen.blit(texto, [250, 250])

20.7 Repaso

20.7.1 Test

Haz click para ir a los Ejercicios.


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