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
| Formato | Significado |
| d | Entero decimal con signo. |
| i | Entero decimal con signo. |
| o | Número octal sin signo. |
| u | Forma obsoleta de «d». |
| x | Número hexadecimal sin signo (en minúsculas). |
| X | Número hexadecimal sin signo (en mayúsculas) . |
| e | Número flotante en formato exponencial (en minúsculas). |
| E | Número flotante en formato exponencial (en mayúsculas). |
| f | Número flotante con parte decimal. |
| F | Número flotante con parte decimal. |
| g | Igual que «e», pero si el exponente es bajo utiliza «f». |
| G | Igual que «E», pero si el exponente es bajo utiliza «F». |
| c | Carácter simple. |
| r | Cadena (convierte los elementos mediante repr()). |
| s | Cadena (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. |
| 0 | En 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). |
| espacio | Dejará 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.
Ejercicio 14
Crea una agenda que nos permita almacenar el nombre, apellidos y teléfono de nuestros contactos. El programa deberá permitir la creación de contactos, el borrado, la búsqueda de un contacto y la visualización de la agenda con los contactos ordenados alfabéticamente.
Solución
Finalizamos (ahora sí) todo el contenido relacionado con las estructuras de datos. Nos encontramos a una entrega del final del curso. Ya sólo nos falta saber cómo almacenar la información generada.
¡Nos leemos!
#04 / Fundamentos de la Programación
- Estructuras de Datos (Matrices)
- Funciones
- Funciones (Recursividad)
- Estructuras de Datos (Registros)
- Ficheros
- ANEXO – Ejercicios Resueltos
Proyecto Final – LIFE, el Juego de la Vida
¿Quieres estar al día de todo lo publicado en el blog?
Para ello puedes suscribirte a mi canal RSS y/o seguirme en mis Redes Sociales.
