#10 – Estructuras de Datos (IV)

Espera… ¿Otra vez estructuras de datos? ¿No habíamos acabado ya con eso? Hasta ahora hemos visto los tipos escalares (enteros, flotantes) y los secuenciales (cadenas, listas). Pero… ¿no sería genial poder crear nuestros propios tipos? Para ello contamos con la estructura registro.

Tipos Estructurados: el Registro

Los tipos que vamos a aprender a crear son tipos especiales formados por la combinación de varios de los tipos que ya conocemos. Tomemos como ejemplo la creación del tipo «Persona».

Creando una Persona

Nuestra persona va a consistir en tres componentes: el nombre, el apellido y la edad. Los dos primeros serán cadenas, mientras que el tercero será un entero.

Mediante variables

Con nuestros conocimientos actuales, la primera opción sería utilizar variables:

nombre = "Pepe"
apellido = "Garcia"
edad = 22

Pero claro, si nos interesa tener más de una persona… La cosa se complica.

nombre2 = "Maria"
nombre2 = "Lopez"
edad2 = 36

nombre3 = "Alberto"
apellido3 = "Perez"
# ETC ETC ETC

Mediante listas: una única variable

Pero claro, nosotros sabemos utilizar listas, de forma que los datos estén relacionados. Con nuestros conocimientos actuales, esta sería la solución más elegante a la que podríamos llegar:

pepe = ["Pepe", "Garcia", 22]
maria = ["Maria", "Lopez", 36]

personas = [pepe, maria]

Añadir nuevas personas a la lista sería sencillo, y con la definición de funciones auxiliares, tendríamos un programa bastante funcional.

def mostrarPersonas(persona):
	print("Nombre: "+persona[0])
	print("Apellido: "+persona[1])
	print("Edad: "+str(persona[2]))
	
for persona in personas:
	mostrarPersonas(persona)

Pero esta solución también presenta algunos problemas. En este caso, sólo tenemos tres componentes, y no es difícil recordar que en la posición 0 tenemos el nombre, en la 1 el apellido, y en la 2 la edad. Pero… ¿Y si quisiéramos almacenar 10 componentes? ¿Y si fueran 20? ¿O 100?

Nueva estructura: Registro

La solución es crear el tipo de datos Persona. ¡OJO! No una variable persona, un tipo de datos (cuándo hablemos de tipos, los pondremos en Mayúscula). Este tipo de datos agrupará el nombre, el apellido y la edad. Se parecerá a una lista, pero su acceso se realizará completamente diferente.

ATENCIÓN: Python no permite la creación de registros de forma nativa, como sí lo hacen otros lenguajes de programación. En su lugar permite la creación de clases. Las clases son algo más avanzado que no se tratará en este curso (pertenecen a la denominada Programación Orientada a Objetos). No obstante, sí aprenderemos lo suficiente para poder utilizarlas como si fueran registros.

Definición de Registro

Definir un tipo es parecido a definir una función. Nuestro tipo Persona quedaría así en Python:

class Persona:
    def __init__(self, nombre = "", apellido = "", edad = 0):
        self.nombre = nombre
        self.apellido = apellido
        self.edad = edad

Tranquilos, no os asustéis. De golpe hemos visto muchas cosas nuevas, pero una vez explicadas veréis que son muy sencillas:

  • Definir un registro es muy similar a definir una función. Simplemente utilizamos la palabra reservada class y el nombre del nuevo tipo comenzado por Mayúscula.
  • La función __init__ es lo que se conoce como un constructor. Es una función que se llama automáticamente cada vez que se crea un elemento del nuevo tipo.
  • Los elementos de la función entre paréntesis serían los elementos por defecto. Esto quiere decir que si creamos una persona sin indicarle ningún dato, el nombre y el apellido serán listas vacías y la edad 0.
  • Self hace referencia al propio registro. Por ello, el constructor utiliza self.nombre (mi propio nombre) para almacenar la información contenida en nombre (el nombre indicado al crear la variable). Lo mismo para el resto de elementos.

Crear una variable de nuestro nuevo tipo Persona consiste en una única línea:

pepe = Persona("Pepe", "Garcia", 22)

Acceso a Registro

Y ahora viene la mejor parte de los registros. Para acceder a sus componentes, debemos utilizar el operador . (punto) de la forma variable.componente:

def mostrarPersonas(persona):
	print("Nombre: "+persona.nombre)
	print("Apellido: "+persona.apellido)
	print("Edad: "+str(persona.edad))

Copia de Registro

Veamos el siguiente ejemplo, donde queremos crear dos personas que son hermanos gemelos. Dado que sólo se diferencian en el nombre, la primera idea sería hacer:

mariano = Persona("Mariano", "Abad", 18)
jacinto = mariano
jacinto.nombre = "Jacinto"

Ahora, utilizamos la función mostrarPersona() con ambas variables, y obtenemos la siguiente salida por pantalla:

Nombre: Jacinto                                                                                                       
Apellido: Abad                                                                                                        
Edad: 18                                                                                                              
Nombre: Jacinto                                                                                                       
Apellido: Abad                                                                                                        
Edad: 18 

¿Y esto? ¿Por qué hay dos Jacintos?

¿Recordáis lo que ocurrió al intentar copiar listas? Si simplemente utilizabamos el operador =, no se copiaba la lista, si no la dirección de memoria que apuntaba a la lista. Con los registros ocurre exactamente lo mismo. Así que si queremos copiar un registro, debemos crear uno nuevo y copiar los diversos componentes uno a uno.

mariano = Persona("Mariano", "Abad", 18)
jacinto = Persona("Jacinto", mariano.apellido, mariano.edad)

Anidamiento de Registros

Por último, indicar que un registro puede contener a su vez otro registro como uno de sus componentes. Esto nos permite crear una infinidad de posibles nuevos tipos de datos.

Anexo: Mejorando print()

Hasta ahora, para las cadenas mostradas mediante las funciones print() e input(), nos hemos estado sirviendo de la concadenación. Sin embargo, existe una forma más elegante de mostrar texto y variables en una misma cadena de texto.

Operador %

print("El trabajador %s gana %8.2f euros mensuales." % ("Manolo", 1287.366))
El trabajador Manolo gana  1287.37 euros mensuales.

Mediante el operador % podemos determinar el formato de los elementos. Su uso es sencillo: en la cadena que se mostrará, marcamos los «huecos» donde irán las variables mediante el operador % y una indicación del formato a aplicar. Tras dicha cadena, de nuevo el operador % seguido de el conjunto de variables entre paréntesis. En el primer % se mostrará la primera variable, en el segundo, la segunda, y así sucesivamente.

En este ejemplo, el primer elemento a mostrar (%s) será una cadena (string), y el segundo (%8.2f) un número flotante. A este último, le indicamos que no deberá de ocupar más de 8 dígitos, y que dos de ellos deberán ser la parte decimal. El punto es contado como un dígito a la hora de formatear el espacio, y, si os fijáis, en la salida se ve un «espacio» extra para llegar a ocupar el espacio de 8 dígitos.

Elementos de Formato
FormatoSignificado
dEntero decimal con signo.
iEntero decimal con signo.
oNúmero octal sin signo.
uForma obsoleta de «d».
xNúmero hexadecimal sin signo (en minúsculas).
XNúmero hexadecimal sin signo (en mayúsculas) .
eNúmero flotante en formato exponencial (en minúsculas).
ENúmero flotante en formato exponencial (en mayúsculas).
fNúmero flotante con parte decimal.
FNúmero flotante con parte decimal.
gIgual que «e», pero si el exponente es bajo utiliza «f».
GIgual que «E», pero si el exponente es bajo utiliza «F».
cCarácter simple.
rCadena (convierte los elementos mediante repr()).
sCadena (convierte los elementos mediante str()).
%Si no hay argumento, se muestra el carácter «%».

Operador {} y método .format()

print("El trabajador {1} gana {0} euros mensuales.".format(1287.366, "Manolo"))
El trabajador Manolo gana 1287.366 euros mensuales.

Este es un método todavía más potente. Con el método .format(), podemos darle formato a una cadena. Para ello, tras la cadena utilizamos un punto y la función format(). El modo de usarla es muy importante, así que recordadlo. Después, mediante el operador {}, indicamos que elemento de format se mostrará en cada posición (siendo 0 el primero).

Este modo de formatear puede combinarse con el anterior, y hasta podemos asignarle nombres a las posiciones:

print("El trabajador {1:s} gana {0:8.2f} euros mensuales.".format(1287.366, "Manolo"))
print("Hoy es {dia:2d} de {mes:s} de {anyo:d}".format(dia=26, mes="diciembre", anyo=2019))
El trabajador Manolo gana  1287.37 euros mensuales.                                                                   
Hoy es 26 de diciembre de 2019    

Podemos utilizar distintas opciones con format() para mejorar el aspecto visual final:

Opción Comportamiento
<El elemento estará alineado a la izquierda, dejando los espacios a la derecha (por defecto en cadenas).
>El elemento estará alineado a la derecha, dejando los espacios a la izquierda (por defecto en números).
^El elemento estará alineado al centro.
0En los números, los espacios serán sustituidos por 0.
,Los millares estarán separados con una coma.
=Fuerza el espacio entre el signo del número y el primer dígito.
+El signo del número será siempre mostrado.
El signo del número sólo se mostrara en los negativos (por defecto).
espacioDejará libre el espacio correspondiente al signo en los números positivos.
print("La hora actual es {0:02d}:15 y tenemos una temperatura de {1:<+5.1f} grados.".format(9, 7.589))
La hora actual es 09:15 y tenemos una temperatura de +7.6  grados.

Finalizamos (ahora sí) todo el contenido relacionado con las estructuras de datos. Nos encontramos a una entrega del final del curso. ¿Adivináis qué nos puede faltar? Mientras tanto, matad el tiempo con el ejercicio 12 y el intérprete onlineGDB.


Marcar como favorito enlace permanente.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *