POO Programación Orientada a Objetos en Python
- Publicado por: Rafael Fernandez
- Categoría: Blog Principiante Python
Comprender los conceptos claves de la POO tales como, Objetos, Clases, Métodos, Herencia, polimorfismo, etc.
Implementar ejemplos usando los conceptos clave de la POO usando Python
¿Qué es la programación orientada a objetos?
La Programación Orientada a Objetos (POO u OOP según sus siglas en inglés) es un paradigma de programación que usa objetos y sus interacciones para diseñar aplicaciones y programas de computadora. Está basado en varias técnicas, incluyendo herencia, modularidad, polimorfismo, y encapsulamiento. Su uso se popularizó a principios de la década de 1990. Actualmente son muchos los lenguajes de programación que soportan la orientación a objetos.
La programación Orientada a objetos (POO) es una forma especial de programar, más cercana a como se expresan las cosas en la vida real que otros tipos de programación.
La POO es un paradigma de la programación de computadores; esto hace referencia al conjunto de teorías, estándares, modelos y métodos que permiten organizar el conocimiento, proporcionando un medio bien definido para visualizar el dominio del problema e implementar en un lenguaje de programación la solución a ese problema.
La POO se basa en el modelo objeto, donde el elemento principal es le objeto, el cual es una unidad que contiene todas sus características y comportamientos en sí misma, lo cual lo hace como un todo independiente, pero que se interrelaciona con objetos de su misma clase o de otras clase, como sucede en el mundo real.
Conceptos Fundamentales de la POO
La programación orientada a objetos es una forma de programar que trata de encontrar una solución a estos problemas. Introduce nuevos conceptos, que superan y amplían conceptos antiguos ya conocidos. Entre ellos destacan los siguientes:
- Clase
Definiciones de las propiedades y comportamiento de un tipo de objeto concreto. La instanciación es la lectura de estas definiciones y la creación de un objeto a partir de ellas.
- Objeto
Instancia de una clase. Entidad provista de un conjunto de propiedades o atributos (datos) y de comportamiento o funcionalidad (métodos), los mismos que consecuentemente reaccionan a eventos. Se corresponden con los objetos reales del mundo que nos rodea, o con objetos internos del sistema (del programa). Es una instancia a una clase.
- Método
Algoritmo asociado a un objeto (o a una clase de objetos), cuya ejecución se desencadena tras la recepción de un “mensaje”. Desde el punto de vista del comportamiento, es lo que el objeto puede hacer. Un método puede producir un cambio en las propiedades del objeto, o la generación de un “evento” con un nuevo mensaje para otro objeto del sistema.
- Mensaje
Una comunicación dirigida a un objeto, que le ordena que ejecute uno de sus métodos con ciertos parámetros asociados al evento que lo generó
- Comportamiento
Está definido por los métodos o mensajes a los que sabe responder dicho objeto, es decir, qué operaciones se pueden realizar con él.
- Evento
Es un suceso en el sistema (tal como una interacción del usuario con la máquina, o un mensaje enviado por un objeto). El sistema maneja el evento enviando el mensaje adecuado al objeto pertinente. También se puede definir como evento la reacción que puede desencadenar un objeto; es decir, la acción que genera.
- Atributos
Características que tiene la clase
- Propiedad o atributo
Contenedor de un tipo de datos asociados a un objeto (o a una clase de objetos), que hace los datos visibles desde fuera del objeto y esto se define como sus características predeterminadas, y cuyo valor puede ser alterado por la ejecución de algún método.
- Estado interno
Es una variable que se declara privada, que puede ser únicamente accedida y alterada por un método del objeto, y que se utiliza para indicar distintas situaciones posibles para el objeto (o clase de objetos). No es visible al programador que maneja una instancia de la clase.
- Componentes de un objeto
Atributos, identidad, relaciones y métodos.
- Identificación de un objeto
Un objeto se representa por medio de una tabla o entidad que esté compuesta por sus atributos y funciones correspondientes.
POO en Python
Clases, Objetos y Métodos
En python la POO se expresa de manera simple y fácil de escribir pero debes tener en cuenta que para programar debes entender cómo funciona la teoría de la POO y llevarla a código.
La teoría de la POO nos dice que todos los objetos deben pertenecer a una clase, ya que esta es la base para diferenciarse unos de otros teniendo atributos y comportamientos que los distingan de otros objetos que pertenezcan a otras clases, para crear clases en python lo hacemos de la siguiente manera:
class Coche():
Como puedes ver para crear una clase lo hacemos escribiendo la palabra class seguida del nombre de la clase y un par de paréntesis, debes tener en cuenta que el nombre de la clase que hayas creado debe empezar por mayúsculas y si tiene más de una palabra debes usar la notación de camello.
Ya que tenemos una clase debemos definir sus atributos y comportamientos, para hacer esto debemos dejar la sangría correspondiente para indicarle que estamos escribiendo dentro de la clase, para definir un atributo simplemente creamos una variable con total normalidad y un valor que le quieras dar:
class Coche(): ruedas=4
Ahora que ya tenemos un atributo podemos agregarle un comportamiento que en python se conoce como métodos, para definir un método lo hacemos igual como lo hacemos con una función con la palabra por defecto def y el nombre de dicho método pero para diferenciar un método de una función lo hacemos escribiendo dentro de sus paréntesis el parámetro self:
def desplazamiento(self): pass
La palabra self hace referencia a los objetos que pertenezcan a la clase y la palabra pass que colocamos dentro del método le indica a el intérprete de python que todavía no le hemos definido ningún funcionamiento a ese método, ya que, si no escribimos la palabra pass cuando todavía no le asignemos nada al método al ejecutarlo nos dará un error.
Ya que explicamos esto terminaremos de definir el método desplazamiento y dentro esto solo colocaremos un print que nos dirá “El coche se esta desplazando sobre 4 ruedas”:
class Coche(): ruedas=4 def desplazamiento(self): print("El coche se esta desplazando sobre 4 ruedas")
Cuando tenemos nuestra clase lista ya podemos empezar a crear objetos que pertenezcan a esa clase, para crear objetos lo hacemos de la siguiente manera:
miVehiculo=Coche()
Después del “=” le estamos especificando a que clase pertenece el objeto que acabamos de crear.
Para poder mostrar todo los atributos y comportamientos que tiene un objeto a la hora de ejecutar un programa de POO en python, hacemos lo siguiente:
Para mostrar atributos:
miObjeto.atributo
Para mostrar métodos:
miObjeto.metodo()
Siguiendo con el ejemplo, para mostrar en pantalla el atributo y el comportamiento de la clase que le dimos a nuestro objeto “miVehiculo” lo hacemos de la siguiente manera:
print("Mi coche tiene ", miVehiculo.ruedas, " ruedas") miVehiculo.desplazamiento()
Si has seguido este ejemplo ya tendrías el ejemplo completo de esta manera:
class Coche(): ruedas=4 def desplazamiento(self): print("El coche se esta desplazando sobre 4 ruedas") miVehiculo=Coche() print("Mi coche tiene ", miVehiculo.ruedas, " ruedas") miVehiculo.desplazamiento()
Y al ejecutarlo nos mostrara lo siguiente:
Mi coche tiene 4 ruedas El coche se esta desplazando sobre 4 ruedas
Puedes observar que hemos creado un objeto que pertenece a una clase y cuya clase tiene atributos y comportamientos únicos de que lo diferencia de otros objetos. El ejemplo habla de que nuestros objetos son vehículos y sus diferentes tipos que en la POO serían clases, tales como: motos, aviones, trenes, etc. En la POO podemos agregar todas las clases que queramos o necesitemos. Ahora agregaremos la clase moto con sus propios atributos y comportamientos:
class Moto(): ruedas=2 def desplazamiento(self): print("La moto se esta desplazando sobre 2 ruedas")
Ahora tenemos dos clases que nos permitirán creas más objetos que tengan diferentes atributos y comportamientos, tal y como se muestra en el siguiente ejemplo:
class Coche(): ruedas=4 def desplazamiento(self): print("El coche se esta desplazando sobre 4 ruedas") class Moto(): ruedas=2 def desplazamiento(self): print("La moto se esta desplazando sobre 2 ruedas") miVehiculo=Coche() print("Mi coche tiene ", miVehiculo.ruedas, " ruedas") miVehiculo.desplazamiento() print("---------------Segundo objeto---------------") miVehiculo2=Moto() print("Mi moto tiene ", miVehiculo2.ruedas, " ruedas") miVehiculo2.desplazamiento()
Si ejecutamos el ejemplo nos mostrara lo siguiente:
Mi coche tiene 4 ruedas El coche se esta desplazando sobre 4 ruedas ---------------Segundo objeto--------------- Mi moto tiene 2 ruedas La moto se esta desplazando sobre 2 ruedas
La POO tiene tres tipos de propiedades que permiten facilitar esta forma de programar y en este tutorial te explicaremos cada una ellas que son: la encapsulación, herencia y polimorfismo.
Polimorfismo
La palabra polimorfismo viene del griego pilo que significa muchas y morfismo que significa formas, es decir, muchas formas. El polimorfismo en python es la capacidad que tienen los objetos de diferentes clases para usar un comportamiento o atributo del mismo nombre pero con diferente valor.
En el ejemplo anterior de clases, objetos y métodos como pueden observar todos los vehículos tienen la capacidad de desplazarse pero no se desplazan de la misma manera, dado que una moto se desplaza sobre dos ruedas y un coche se desplaza sobre cuatro, eso es el polimorfismo.
class Coche(): ruedas=4 def desplazamiento(self): print("El coche se esta desplazando sobre 4 ruedas") class Moto(): ruedas=2 def desplazamiento(self): print("La moto se esta desplazando sobre 2 ruedas")
Métodos propios de las clases en Python
Método constructor
El método constructor se conoce como el método que le da el estado inicial a una clase, vamos a crear un objeto y le asignaremos este método para ver su funcionalidad:
class Cachorro(): def __init__(self, color, raza): self.color = color self.raza = raza perrito = Cachorro("Marrón claro", "Golden retriever") print("Este es un cachorro de la raza {} y es de color {}".format(perrito.raza, perrito.color))
Si te das cuenta al instanciar el objeto perrito, debemos pasarle los parámetros de su estado inicial los cuales son los que definimos en el método constructor color y raza. Al ejecutar este código obtendremos lo siguiente:
λ python POO.py Este es un cachorro de la raza Golden retriever y es de color Marrón claro
Método string
El método string es otro de los métodos propios de las clases de Python, utilizando el mismo ejemplo anterior veamos que pasa cuando ejecutamos una salida por pantalla de lo que se encuentra instanciado en el objeto:
class Cachorro(): def __init__(self, color, raza): self.color = color self.raza = raza perrito = Cachorro("Marrón claro", "Golden retriever") print(perrito)
Ahora le preguntamos a python que existe en la instacia perrito y obtenemos:
λ python POO.py" <__main__.Cachorro object at 0x000002882115A888>
Python nos dice que obtenemos un objeto del tipo Cachorro, simplemente nos dice que es una instancia en la clase Cachorro pero no se cual son sus propiedades, es decir su color y su raza entonces es aquí donde entra el método string que vamos a definir:
class Cachorro(): def __init__(self, color, raza): self.color = color self.raza = raza def __str__(self): return """\ Raza: {} Color: {}""".format(self.raza, self.color) perrito = Cachorro("Marrón claro", "Golden retriever") print(perrito)
Ahora ejecutamos nuevamente el código y obtendremos lo que se encuentra instanciado dentro de ese objeto y todo gracias a que definimos el método __str__:
λ python POO.py" Raza: Golden retriever Color: Marrón claro
Método repr
Este método es muy parecido al string, su única diferencia es que se utiliza para que el mismo lenguaje python identifique que hay dentro. Una vez dicho esto, se puede decir que las diferencias que tienen ambos es que el método string es usado para que un humano pueda saber lo que hay dentro del objeto mientras que el método repr() es usado para que el programador sepa que hay dentro del objeto, vamos a rehutilizar el código anterior, pero esta vez le agregaremos un atributo id:
class Cachorro(): def __init__(self, color, raza, id): self.color = color self.raza = raza self.id = id def __str__(self): return """\ Raza: {} Color: {}""".format(self.raza, self.color) def __repr__(self) -> str: return "<Cachorro {}>".format(self.id) perrito = Cachorro("Marrón claro", "Golden retriever", 1) print(repr(perrito))
Por ejemplo, si ejecutamos este código podrémos ver que ahora la función nos retorna un string diciendonos que hay una instancia del tipo cachorro con el id número 1:
λ python POO.py" <Cachorro 1>
Si te fijas obtenemos el formato que dimos al reescribir el método repr(), pero cualquier persona al leer esto no sabrá porque se representa de la forma <Cachorro 1> solamente nosotros los programadores sabremos que es un objeto porque es así como el interprete de python hace referencia a los objetos, aunque falten otras cosas como por ejemplo la dirección en memoria en la que se encuentra, pero para este caso remplazamos el método repr() para mostrar el tipo de objeto y el id que lleva asignado dicho objeto.
Encapsulación
La encapsulación es una forma de darle uso exclusivo a los comportamientos o atributos que posee una clase, es decir, protege esos atributos y comportamientos para que no sean usados de manera externa.
En python para hacer que un atributo o comportamiento sea privado tenemos que colocar un par de guiones bajos antes del nombre del atributo o comportamiento “__nombre”.
Para empezar nuestro ejemplo de encapsulación vamos a crear una clase que llamaremos “Ejemplo” y dentro de ella declaramos un método al que llamaremos “publico” que contendrá un return que solo mostrara una cadena de texto que dirá “Soy un método público a la vista de todo”:
class Ejemplo(): def publico(self): return "Soy un método público, a la vista de todo"
Ahora declaramos un método que se llame “privado” pero antes de su nombre pondremos un par de guiones bajos “__” y dentro del método una cadena de texto como en el método anterior:
class Ejemplo(): def publico(self): return "Soy un método público, a la vista de todo" def __privado(self): return "Soy un metodo privado, para ti no existo"
Ya con todo esto creamos un objeto perteneciente a la clase ejemplo y procedemos a imprimir los dos métodos hemos creado:
class Ejemplo(): def publico(self): return "Soy un método público, a la vista de todo" def __privado(self): return "Soy un metodo privado, para ti no existo" objeto = Ejemplo() print(objeto.publico()) print(objeto.__privado())
Si ejecutamos el ejemplo nos mostrara algo parecido:
Soy un método público, a la vista de todo Traceback (most recent can last): File “encapsulación.py”, line 12, in (module) print(objeto.__private()) AttributeError: ‘Ejemplo’ object has no attribute ‘__privado’
Como puedes ver al ejecutarlo si nos muestra el contenido del método publico pero a la hora de mostrar el método privado nos dice que tal método no existe pero en realidad si solo que por ser privado no puede ser mostrado externamente.
Python posee varias formas de mostrar contenido privado y una de ella en el name mangling, para aplicar el name mangling debemos hacerlos de esta manera:
print(objeto._Ejemplo__privado())
Al poner el nombre del objeto seguido de un punto “.”, un guion bajo “_”, el nombre de la clase y luego el nombre del método privado le estamos diciendo a python que tiene derecho a mostrar este método privado y si ejecutas el programa cambiando esa línea de código de esta manera te mostrara lo siguiente:
Soy un método público, a la vista de todo Soy un metodo privado, para ti no existo
Con esto ya podemos acceder a los métodos privados que queramos pero como mencionamos anteriormente hay varias maneras de mostrar atributos o comportamientos que sean privados y para esto existen dos métodos especiales que sirven para acceder a los valores privados, estos dos métodos son el get (obtener, conseguir) y el set (establecer, colocar).
Para empezar declaramos un constructor que le de estado inicial a un atributo, este constructor se conoce como __init__ al cual vamos a hacer que sea privado y eso los hacemos de la siguiente manera:
class Ejemplo(): def __init__(self): self.__oculto="Me encuentro oculto en la clase" def publico(self): return "Soy un método público, a la vista de todo" def __privado(self): return "Soy un metodo privado, para ti no existo"
Ahora que tenemos un atributo privado, creamos un método get que sería algo como decir “obtener oculto” y dentro del método get le vamos a decir que nos retorne el atributo que está oculto:
class Ejemplo(): def __init__(self): self.__oculto="Me encuentro oculto en la clase" def publico(self): return "Soy un método público, a la vista de todo" def __privado(self): return "Soy un metodo privado, para ti no existo" def get_oculto(self): return self.__oculto
Y ahora solo hacemos un llamado a este método y ejecutamos el programa:
class Ejemplo(): def __init__(self): self.__oculto="Me encuentro oculto en la clase" def publico(self): return "Soy un método público, a la vista de todo" def __privado(self): return "Soy un metodo privado, para ti no existo" def get_oculto(self): return self.__oculto objeto = Ejemplo() print(objeto.publico()) print(objeto._Ejemplo__privado()) print(objeto.get_oculto())
Y al ejecutarlo nos mostrara lo siguiente:
Soy un método público, a la vista de todo Soy un metodo privado, para ti no existo Me encuentro oculto en la clase
Como puedes ver hemos accedido al atributo privado por medio de un método get, ahora vemos a utilizar un método set, este método sirve para cambiarle los valores a todo lo que este encapsulado y eso lo hacemos de la siguiente manera:
class Ejemplo(): def __init__(self): self.__oculto="Me encuentro oculto en la clase" def publico(self): return "Soy un método público, a la vista de todo" def __privado(self): print ("Soy un metodo privado, para ti no existo") def get_oculto(self): return self.__oculto def set_oculto(self): self.__oculto = self.__privado() objeto = Ejemplo() print(objeto.publico()) print(objeto._Ejemplo__privado()) print(objeto.get_oculto()) objeto.set_oculto()
En este ejemplo le hemos cambiando al método “__privado” el return por un print y le asignamos su valor al atributo oculto por medio del método set para que nos imprima lo siguiente:
Soy un método público, a la vista de todo Soy un metodo privado, para ti no existo None Me encuentro oculto en la clase Soy un metodo privado, para ti no existo
Con esto hemos explicado un poco como aplicar la encapsulación en python, ahora solo falta explicar el concepto de herencia en python que es uno de los más importantes en la programación orientada a objetos.
Herencia
La Herencia explica que puede crearse un objeto a partir de otro objeto ya existente. El nuevo objeto hereda todas las cualidades del objeto del que deriva y además puede añadir nuevas funcionalidades o modificar las ya existentes.
Para aplicar la herencia en python debemos crear una súper clase o clase padre la cual tendrá los atributos y comportamientos principales que tendrán todas las clases derivadas de la clase padre, en este tutorial basaremos nuestros ejemplo de herencia con un caso de padres e hijos y para empezar declaramos la clase padre a la cual llamaremos “Padre” y le daremos los siguientes atributos y un comportamiento de la siguiente forma:
class Padre(): caballo="negro" ojos="azules" def conducir_coche(self): print ("La persona sabe conducir coches")
Ahora que tenemos la clase padre podemos declarar una clase que herede todos los atributos y comportamientos que tiene la clase padre, esta clase que derivada de la clase padre la vamos a llama “Hijo” y para indicar en python que una clase que está heredando de otra hay que escribir el nombre de la clase de la cual está heredando dentro de los paréntesis que están después del nombre de la clase que lo hereda:
class Hijo(Padre):
Haciendo esto automáticamente la clase hijo ya tiene todo los atributos y comportamiento que tiene la clase padre sin necesidad de haberlos declarado dentro de la clase hijo.
La herencia también permite que las clases derivada de una clase padre también tengan atributos y comportamientos propios que no están en la clase padre:
class Hijo(Padre): def conducir_moto(self): print ("La persona sabe conducir moto")
Con esto si creamos un objeto perteneciente a la clase hijo y le decimos que imprima todos los atributos y comportamientos que tiene la clase padre lo hará con total normalidad como si los hubieras declarado en la clase hijo tal y como se muestra el en siguiente ejemplo:
class Padre(): caballo="negro" ojos="azules" def conducir_coche(self): print ("La persona sabe conducir coches") class Hijo(Padre): def conducir_moto(self): print ("La persona sabe conducir moto") persona=Hijo() print(persona.caballo) print(persona.ojos) persona.conducir_coche() persona.conducir_moto()
Si ejecutamos este ejemplo nos mostrara lo siguiente:
Negro azules La persona sabe conducir coches La persona sabe conducir moto
A partir de este punto ya conocemos la POO en python, entonces quiero que hagamos un creador de personajes para un videojuego. Utilizaremos humanos y orcos como la raza de cada personaje y usaremos un diccionario para establecer los atributos estandares para cada raza y la clase de cada raza.
Nota:
Requisitos para programar el creador de personajes
- Librería UUID
- Librería JSON
- Librería Time
Vamos a comenzar a programar nuestro creador de personajes y para ello vamos a crear un archivo llamado pj.py y agregaremos el siguiente diccionario:
personajes = { "Raza": { "Humanos": { "Clase": { "Guerrero": { "Stats": { "Vida": "1200", "Mana": "850" } }, "Jinete": { "Stats": { "Vida": "1350", "Mana": "925" } }, "Mago": { "Stats": { "Vida": "975", "Mana": "1100" } } } }, "Orcos": { "Clase": { "Guerrero": { "Stats": { "Vida": "1350", "Mana": "425" } }, "Chamán": { "Stats": { "Vida": "725", "Mana": "895" } }, "Jinete": { "Stats": { "Vida": "1500", "Mana": "325" } } } } } }
Este diccionario definirá la raza, la clase y los atributos como la vida y el mana de cada personaje, entonces ahora solo nos falta programar la clase que dará vida al personaje, creemos otro archivo llamado creador_personajes.py
y dentro importaremos los personajes del archivo pj.py y crearemos un objeto Personaje que estará definido de la siguiente manera:
import uuid import json import time from pj import personajes class Personaje(): def __init__(self, nombre, raza, clase, vida, mana): self.nombre = nombre self.raza = raza self.clase = clase self.vida = vida self.mana = mana
Hemos creado una clase Personaje la cual recibe como atributos el nombre la raza la clase, la vida y el mana del personaje. Nuestro objetivo es hacer que esta creación del personaje sea totalmente personalizada por la persona que lo esta creando, es decir; que el pueda elegir entre todos los personajes disponibles ya sea humano o orco y asignarle un nombre, para ello vamos a programar un método llamado configurarPersonaje:
import uuid import json import time from pj import personajes class Personaje(): def __init__(self, nombre, raza, clase, vida, mana): self.nombre = nombre self.raza = raza self.clase = clase self.vida = vida self.mana = mana def configurarPersonaje(self): print("""\n ¿Qué personaje deseas crear?\n 1) Humanos 2) Orcos""") raza = input("\n> ") if raza == '1': raza = "Humanos" print("""\n¿Qué clase de humano deseas crear?\n 1) Guerrero 2) Jinete 3) Mago """) clase = input("\n>") if clase == '1': clase = 'Guerrero' #Llamaremos un método que cree el personaje elif clase == '2': clase = 'Jinete' #Llamaremos un método que cree el personaje elif clase == '3': clase = 'Mago' #Llamaremos un método que cree el personaje elif raza == '2': raza = "Orcos" print("""\n¿Qué clase de orco deseas crear?\n 1) Guerrero 2) Chamán 3) Jinete """) clase = input("\n> ;") if clase == '1': clase = 'Guerrero' #Llamaremos un método que cree el personaje elif clase == '2': clase = 'Chamán' #Llamaremos un método que cree el personaje elif clase == '3': clase = 'Jinete' #Llamaremos un método que cree el personaje else: print("\nHas introducido un comando inválido")
Hasta ahora tenemos la configuración del personaje, para que el usuario pueda elegir entre humanos y orcos e indistintamente de cual seleccione luego podrá selecciona su clase y a partir de aquí finalizaría el programa, pero fijate que si intentas ejecutar el código no podras iniciarlo. Eso es porque no tenémos instanciado el objeto para que se inicie y poder acceder a sus métodos y atributos.
Pero algo curioso es que el objeto Personaje simplemente es un objeto que se encargará de instanciar el personaje, configurarlo, crearlo y luego guardarlo, es decir; este objeto es la interfaz de nuestro programa por lo tanto para iniciarlo deberiamos de instanciar un personaje de forma manual nosotros mismos, ya que esta clase recibe atributos. Esto no es bueno porque la idea es que no tengamos que instanciar siempre el mismo personaje para poder iniciar el programa porque fijate que si esto se implementara en un juego real, siempre que alguien vaya a crear un personaje notará que ese personaje siempre estará allí y aunque lo borre se volverá a crear una y otra vez.
Para entender mejor a lo que me refiero vamos a instanciar un personaje en el objeto personaje, para ello vamos a instanciar dentro de una variable pj un personaje:
pj = Personaje("Forking2412", "Humano", "Guerrero", "1200", "850") pj.configurarPersonaje()
Ahora si ejecutamos el código verás que se iniciará nuestra interfaz, pero si te das cuenta hemos creado un personaje. Es decir que cada vez que se ejecute el programa se creará este personaje, por lo tanto esto no es bueno como lo dije anteriormente.
Para solucionar este problema lo que podemos hacer es crear una nueva clase llamada Iniciar y decirle que herede de la clase Personaje y en su constructor llamamos al método configurarPersonaje para que se inicie nuestro programa. En pocas palabras esta clase funciona como una especie de tunel hacia el método configurarPersonaje ya que podemos acceder a este metodo porque dicha clase hereda de la clase Personaje:
class Iniciar(Personaje): def __init__(self): self.configurarPersonaje() Iniciar()
Si ejecutamos el programa, verás que se inicia la configuración del personaje sin necesidad de instanciar nada dentro de la clase Personaje y esto es lo que necesitamos.
Hasta ahora podemos permitirle al usuario que configure su personaje, podrá elegir entre humanos y orcos y luego seleccionar la clase del personaje y a partir de este punto debemos programar otro metodo en la clase Personaje que le permita al usuario introducir un nombre y que automaticamente se le asignen los atributos de vida y mana dependiendo de lo que haya elegido.
Vamos a crear un nuevo método llamado crearPersonaje(self, raza, clase) este metodo recibe dos argumentos, la raza y la clase del personaje que ya habrá seleccionado el usuario anteriormente:
class Personaje(): # ... def crearPersonaje(self, raza_seleccionada, clase_seleccionada): vida = personajes['Raza'][raza_seleccionada]['Clase'][clase_seleccionada]['Stats']['Vida'] mana = personajes['Raza'][raza_seleccionada]['Clase'][clase_seleccionada]['Stats']['Mana'] nombre = input("\nIntroduce el nombre de tu personaje > ") nuevo_pj = Personaje( nombre=nombre, raza=raza_seleccionada, clase=clase_seleccionada, vida=vida, mana=mana) datos = { "id": str(uuid.uuid4()), "Nombre": nuevo_pj.nombre, "Raza": nuevo_pj.raza, "Clase": nuevo_pj.clase, "Vida": nuevo_pj.vida, "Mana": nuevo_pj.mana } print("\nEl personaje \"{}\" ha sido creado".format(datos["Nombre"]))
Creación del personaje:
- La variable vida accede a la clave del diccionario personajes hazta acceder al valor de la vida del personaje que ha seleccionado el usuario
- La variable mana accede a la clave del diccionario personajes hazta acceder al valor de el mana del personaje que ha seleccionado el usuario
- La variable nombre captura el nombre que le dará el usuario a su personaje a través de la entrada por teclado
- La variable datos contiene el diccionario con las características del personaje que ha creado el usuario
- id contiene el id que generaremos con la librería uuid y la convertimos en un string.
- Nombre contiene el nombre del personaje instanciado por el usuario
- Raza contiene la raza del personaje instanciado por el usuario
- Clase contiene la clase del personaje instanciado por el usuario
- Vida contiene la vida del personaje instanciado por el usuario
- Mana contiene el mana del personaje instanciado por el usuario
Hasta este punto ya tenémos la lógica del personaje, para que luego pueda ser guardados en una base de datos. Pero en este punto es muy temprano para trabajar con bases de datos. Lo mejor es que usemos un archivo JSON para guardar a los personajes ya que estamos guardando sus datos como diccionarios y especificamente los archivos JSON contienen esa misma estructura.
Entonces vamos a crear un método que nos permita guardar el personaje en dicho archivo, a este método lo llamaremos guardarPersonaje(self, datos) y recibira los datos del personaje como argumento estos datos son el diccionario que acabo de explicar anteriormente que contiene los datos del personaje creado por el usuario luego de crear este método, lo vamos a llamar dentro del método crearPersonaje justo debajo del print y le pasamos como argumentos los datos del personaje que ha creado el usuario:
def crearPersonaje(self, raza, clase): vida = personajes['Raza'][raza]['Clase'][clase]['Stats']['Vida'] mana = personajes['Raza'][raza]['Clase'][clase]['Stats']['Mana'] nombre = input("\nIntroduce el nombre de tu personaje > ") nuevo_pj = Personaje( nombre=nombre, raza=raza, clase=clase, vida=vida, mana=mana) datos = { "id": str(uuid.uuid4()), "Nombre": nuevo_pj.nombre, "Raza": nuevo_pj.raza, "Clase": nuevo_pj.clase, "Vida": nuevo_pj.vida, "Mana": nuevo_pj.mana } print("\nEl personaje \"{}\" ha sido creado".format(datos["Nombre"])) self.guardarPersonaje(datos) def guardarPersonaje(self, datos): data['personajes_creados'].append(datos) pjs = data['personajes_creados'] archivo = open('Personajes.json', 'w') json.dump(pjs, archivo, indent=4)
Ahora también vamos a crear un diccionario en una variable global llamada datos y le asignaremos a ese diccionario una clave llamada personajes_creados con una lista vacía, nuestro archivo creador_personajes.py
debería de tener todo este código si haz seguido esta lección al pie de la letra:
import uuid import json import time from pj import personajes data = {} data['personajes_creados'] = [] class Personaje(): def __init__(self, nombre, raza, clase, vida, mana): self.nombre = nombre self.raza = raza self.clase = clase self.vida = vida self.mana = mana def configurarPersonaje(self): print("""\n ¿Qué personaje deseas crear?\n 1) Humanos 2) Orcos""") raza_seleccionada = input("\n> ") if raza_seleccionada == '1': raza_seleccionada = "Humanos" print("""\n¿Qué clase de humano deseas crear?\n 1) Guerrero 2) Jinete 3) Mago """) clase_seleccionada = input("\n>") if clase_seleccionada == '1': clase_seleccionada = 'Guerrero' self.crearPersonaje(raza_seleccionada, clase_seleccionada) elif clase_seleccionada == '2': clase_seleccionada = 'Jinete' self.crearPersonaje(raza_seleccionada, clase_seleccionada) elif clase_seleccionada == '3': clase_seleccionada = 'Mago' self.crearPersonaje(raza_seleccionada, clase_seleccionada) elif raza_seleccionada == '2': raza_seleccionada = "Orcos" print("""\n¿Qué clase de orco deseas crear?\n 1) Guerrero 2) Chamán 3) Jinete """) clase_seleccionada = input("\n> ") if clase_seleccionada == '1': clase_seleccionada = 'Guerrero' self.crearPersonaje(raza_seleccionada, clase_seleccionada) elif clase_seleccionada == '2': clase_seleccionada = 'Chamán' self.crearPersonaje(raza_seleccionada, clase_seleccionada) elif clase_seleccionada == '3': clase_seleccionada = 'Jinete' self.crearPersonaje(raza_seleccionada, clase_seleccionada) else: print("\nHas introducido un comando inválido") def crearPersonaje(self, raza_seleccionada, clase_seleccionada): vida = personajes['Raza'][raza_seleccionada]['Clase'][clase_seleccionada]['Stats']['Vida'] mana = personajes['Raza'][raza_seleccionada]['Clase'][clase_seleccionada]['Stats']['Mana'] nombre = input("\nIntroduce el nombre de tu personaje > ") nuevo_pj = Personaje( nombre=nombre, raza=raza_seleccionada, clase=clase_seleccionada, vida=vida, mana=mana) datos = { "id": str(uuid.uuid4()), "Nombre": nuevo_pj.nombre, "Raza": nuevo_pj.raza, "Clase": nuevo_pj.clase, "Vida": nuevo_pj.vida, "Mana": nuevo_pj.mana } print("\nEl personaje \"{}\" ha sido creado".format(datos["Nombre"])) self.guardarPersonaje(datos) def guardarPersonaje(self, datos): data['personajes_creados'].append(datos) pjs = data['personajes_creados'] archivo = open('Personajes.json', 'w') json.dump(pjs, archivo, indent=4) class Iniciar(Personaje): def __init__(self): self.configurarPersonaje() Iniciar() print(data['personajes_creados'])
A partir de aquí debemos hacer uso del dicionario data para agregar todos los personajes que se vayan creando en la llave personajes_creados que tiene como valor una lista vacia. En dicha lista guardaremos los personajes creados por el usuario y esto es lo que precisamente estamos haciendo con el método, de hecho voy a imprimir dicha lista para ver si esta guardando correctamente el personaje, ejecutemos el código y creemos un personaje y al final deberíamos de obtener el personaje creado:
¿Qué personaje deseas crear? 1) Humanos 2) Orcos > 1 ¿Qué clase de humano deseas crear? 1) Guerrero 2) Jinete 3) Mago >1 Introduce el nombre de tu personaje > Forking2412 El personaje "Forking2412" ha sido creado [{'id': 'b43a67a1-0bb6-4dd6-b74c-703e1075d47b', 'Nombre': 'Forking2412', 'Raza': 'Humanos', 'Clase': 'Guerrero', 'Vida': '1200', 'Mana': '850'}]
Al seguir la secuencia del programa se crea el personaje y lo agrega a la lista que se encuentra en el diccionario data especificamente en su clave personajes_creados el cual tiene como valor la lista que se encaragará de guardar todos los personajes.
Ahora lo que necesito es hacer que estos personajes se guarden en un archivo JSON a parte, que hará la función de la base de datos. Pero si analizamos bien; también necesito que los personajes creados anteriormente se mantengan y es por eso que agregamos los personajes a esta lista personajes_creados.
La lógica principal es que usaremos esa misma lista para cargar los personajes que ya fueron creados anteriormente y que ya se encuentran en el archivo JSON, entonces para esto necesitaremos crear un método que se llamará cargarPersonajes:
class Personaje(): # ... def cargarPersonajes(self): try: archivo = open('Personajes.json') data['personajes_creados'] = json.load(archivo) except FileNotFoundError: print("\nCreando registro de personajes...") time.sleep(1) archivo = open('Personajes.json', 'a+') except json.decoder.JSONDecodeError: print("\nNo hay personajes creados, crea nuevos personajes a partir de ahora;D")
Este método utiliza una declaración try para capturar los posibles errores, ya que si analizamos el código primero se tratará de abrir un archivo llamado Personajes.json al crear dicho archivo y tratar de leerlo con json.load() si no existe arrojará un error del tipo FileNotFoundError y si el archivo existe pero está vacio arrojará un error del tipo json.decoder.JSONDecodeError. Esto lo se porque previamente al ejecutar el código los errores fueron arrojado y lo único que hicimos fue manejarlo a través de la declaración try except.
Por lo tanto, cuando no se encuentre el archivo estarémos mostrando por pantalla “Creando registo de personajes” para referinos a que se esta creando el archivo Personajes.json y luego lo creamos con la función open y le pasamos como parámetro el nombre del archivo y el modo en que queremos abrirlo, en este caso le he dicho que lo abra en modo append de escritura y lectura .
Cuando el archivo exista pero se encuentre vacio estarémos mostrando por pantalla “No hay persoanjes creados, crea nuevos personajes a partir de ahora;D” para referirnos a que el archivo esta creado pero se encuentra vacio.”
A partir de este punto, vamos a ejecuta el código y ver si se crea dicho archivo y si guarda en él los personajes. Probaré a crear dos personajes un humano y un orco siguiendo la secuencia de nuestro programa.
Pero antes es muy importante llamar al método cargarPersonajes ya que se encargará de cargar los personajes que ya hayan sido creados anteriormente, porque si no lo llamamos no guardará los personajes anteriores, para llamarlo lo hacemos desde el constructor de la clase Iniciar y luego ejecutamos el programa.
class Iniciar(Personaje): def __init__(self): self.cargarPersonajes() self.configurarPersonaje()
Entonces al ejecutar el programa y crear los personajes se nos creará el archivo Personajes.json con los dos personajes creados y si creamos otros veremos que se iran agregando uno debajo de otro.
Ahora tenemos la lógica, pero quiero hacer más dinámica la interfaz de nuestro programa. Entonces lo que voy a hacer es crear un nuevo método llamado interfaz:
class Personaje(): #... def interfaz(self): i = 0 while True: print("""\n=== Bienvenido al creador de personajes humanos y orcos ===\n ¿Qué deseas hacer?\n 1) Crear un personaje 2) Ver los personajes creados 3) Salir del programa\n""") opcion = input("> ") if opcion == '1': self.configurarPersonaje() elif opcion == '2': if data['personajes_creados'] == []: print("\nNo se encuentran personajes registrados") for personaje in data['personajes_creados']: print("""\nID: {} Nombre: {} Raza: {} Clase: {} Vida: {} Mana: {}\n """.format(personaje['id'], personaje['Nombre'], personaje['Raza'], personaje['Clase'], personaje['Vida'], personaje['Mana'])) elif opcion == '3': print("\nGracias por usar el programa;)") time.sleep(2) quit() else: print("\nHas introducido un comando inválido")
La interfaz no es más que código para imprimir por pantalla con entradas por teclado y llamará al método configurarPersonaje, donde todo ese código está dentro de un while para que se repita una y otra vez hasta que el usuario decida salir al persionar el número 3 y es allí donde se ejecuta quit() para salir del programa pasados 2 segundos, ahora necesitamos hacer unos cambios en la clase Iniciar y en donde llamabamos al método configurarPersonaje lo remplazaremos por el método interfaz, ya que el método interfaz se encarga de llamar al método configurarPersonaje como expliqué anteriormente.
Hasta ahora nuestro archivo creador_personajes.py
debe tener como código final todo lo siguiente:
import uuid import json import time from pj import personajes data = {} data['personajes_creados'] = [] class Personaje(): def __init__(self, nombre, raza, clase, vida, mana): self.nombre = nombre self.raza = raza self.clase = clase self.vida = vida self.mana = mana def configurarPersonaje(self): print("""\n ¿Qué personaje deseas crear?\n 1) Humanos 2) Orcos""") raza = input("\n> ") if raza == '1': raza = "Humanos" print("""\n¿Qué clase de humano deseas crear?\n 1) Guerrero 2) Jinete 3) Mago """) clase = input("\n>") if clase == '1': clase = 'Guerrero' self.crearPersonaje(raza, clase) elif clase == '2': clase = 'Jinete' self.crearPersonaje(raza, clase) elif clase == '3': clase = 'Mago' self.crearPersonaje(raza, clase) elif raza == '2': raza = "Orcos" print("""\n¿Qué clase de orco deseas crear?\n 1) Guerrero 2) Chamán 3) Jinete """) clase = input("\n>") if clase == '1': clase = 'Guerrero' self.crearPersonaje(raza, clase) elif clase == '2': clase = 'Chamán' self.crearPersonaje(raza, clase) elif clase == '3': clase = 'Jinete' self.crearPersonaje(raza, clase) else: print("\nHas introducido un comando inválido") def crearPersonaje(self, raza, clase): vida = personajes['Raza'][raza]['Clase'][clase]['Stats']['Vida'] mana = personajes['Raza'][raza]['Clase'][clase]['Stats']['Mana'] nombre = input("\nIntroduce el nombre de tu personaje > ") nuevo_pj = Personaje( nombre=nombre, raza=raza, clase=clase, vida=vida, mana=mana) datos = { "id": str(uuid.uuid4()), "Nombre": nuevo_pj.nombre, "Raza": nuevo_pj.raza, "Clase": nuevo_pj.clase, "Vida": nuevo_pj.vida, "Mana": nuevo_pj.mana } self.guardarPersonaje(datos) print("\nEl personaje \"{}\" ha sido creado".format(datos["Nombre"])) def guardarPersonaje(self, datos): data['personajes_creados'].append(datos) pjs = data['personajes_creados'] archivo = open('Personajes.json', 'w') json.dump(pjs, archivo, indent=4) def cargarPersonajes(self): try: archivo = open('Personajes.json') data['personajes_creados'] = json.load(archivo) except FileNotFoundError: print("\nCreando registro de personajes...") time.sleep(1) archivo = open('Personajes.json', 'a+') except json.decoder.JSONDecodeError: print("\nNo hay personajes creados, crea nuevos personajes a partir de ahora;D") pass def interfaz(self): i = 0 while True: print("""\n=== Bienvenido al creador de personajes humanos y orcos ===\n ¿Qué deseas hacer?\n 1) Crear un personaje 2) Ver los personajes creados 3) Salir del programa\n""") opcion = input("> ") if opcion == '1': self.configurarPersonaje() elif opcion == '2': if data['personajes_creados'] == []: print("\nNo se encuentran personajes registrados") for personaje in data['personajes_creados']: print("""\nID: {} Nombre: {} Raza: {} Clase: {} Vida: {} Mana: {}\n """.format(personaje['id'], personaje['Nombre'], personaje['Raza'], personaje['Clase'], personaje['Vida'], personaje['Mana'])) elif opcion == '3': print("\nGracias por usar el programa;)") time.sleep(2) quit() else: print("\nHas introducido un comando inválido") class Iniciar(Personaje): def __init__(self): self.cargarPersonajes() self.interfaz() Iniciar()
Listo ya tenemos nuestro creador de personajes programado en python utilizando la programación orientada a objetos, como consejo te animo a seguir prácticando la POO ya que es lo que más se usa hoy en día en las distintas aplicaciones que se desarrollan y no solo en python si no también en cualquier otro lenguaje.
➡ ¿Quieres saber mas de Python? Te invitamos al Curso de Python para Principiantes:
[…] Si has llegado hasta aquí enorabuena 🏅 pasa a la siguiente lección ➡ Programación orientada a objetos en python […]
[…] Si has llegado hasta aquí enorabuena 🏅 pasa a la siguiente lección ➡ Programación orientada a objetos en python […]
[…] ➡ Programación orientada a objetos en python […]
[…] Empezaremos hablando sobre la importancia de python y porque cada vez este programa esta ganando mas popularidad en todo el mundo. Seguidamente instalaremos Python en el sistema operativo que usemos (Window, OS o Linux). Explicaremos los conceptos fundamentales sobre los tipos de datos y variables en Python asi como las sentencias y bucles, los cuales son usados no solo en python sino por cualquier lenguaje de programación. Aprenderemos a depurar nuestros programas y a reconocer los típicos errores al ejecutar nuestros programas. La penúltima parte del curso esta enfocada en realizar operaciones de algebra, cálculos y en como graficar los resultados obtenidos. Por último nos enfocaremos en una introduccion a la programación orientada a objetos con python. […]
[…] ¿Confundido por “clase” o programación orientada a objetos? OOP ( programación orientada a objetos) es muy usado cuando se crean se crean cosas como GUIs interactivas (interfaces gráficas de usuario) o juegos. Si está confundido acerca de OOP, consulta esta intro a la programación orientado a objetos en Python. […]
[…] Si está confundido acerca de OOP, consulta esta intro a la programación orientado a objetos en Python. […]
[…] ➡ Programación orientada a objetos en python […]
[…] cosa a través de las clases, para entender mas a fondo se recomienda leer un poco sobre la Programación Orientada a Objetos, que dentro del mundo informático ha representado una verdadera revolución en el desarrollo de […]
Muchas gracias por el curso de Python desde cero, ahora entiendo mejor y puedo decir que conozco la programación en Python. Sin duda, seguiré con los demás cursos para seguir mejorando en este magnífico lenguaje de programación.
Saludos desde España.
Muchas gracias por el curso me sirvió mucho el curso, fue divertido y entretenido muy buena forma de enseñar.
[…] cosa a través de las clases, para entender mas a fondo se recomienda leer un poco sobre la Programación Orientada a Objetos, que dentro del mundo informático ha representado una verdadera revolución en el desarrollo de […]
[…] ¿Confundido por “clase” o programación orientada a objetos? OOP ( programación orientada a objetos) es muy usado cuando se crean se crean cosas como GUIs interactivas (interfaces gráficas de usuario) o juegos. Si está confundido acerca de OOP, consulta esta intro a la programación orientado a objetos en Python. […]
[…] Si está confundido acerca de POO, consulta la intro de programación orientado a objetos en Python. […]