logo cosasdedevs

Programaci贸n Orientada a Objetos (POO) en Python

Programaci贸n Orientada a Objetos (POO) en Python

My Profile
Ago 13, 2020

¡Hola! Cuando se publique este post puede que muchos estéis de vacaciones (disfrutad, ya me tocará a mí 馃槇) pero en cosasdedevs no paramos en agosto y, ya que no paramos toca nuevo tutorial como todas las semanas 馃帀. Esta vez toca hablar de la POO en PYTHON.

¿Qué es la Programación Orientada a Objetos (POO)?

Para el que no lo sepa, la POO como bien dice la Wikipedia, es un paradigma de programación que viene a innovar la forma de obtener resultados. Los objetos manipulan los datos de entrada para la obtención de datos de salida específicos, donde cada objeto ofrece una funcionalidad especial.

¿Esto que quiere decir?

Bien, nosotros al crear un objeto, podremos pasar unos parámetros de configuración al inicializarlo o en métodos del objeto y este podrá devolvernos uno o varios resultados según nuestras necesidades.

Una vez aclarado esto, vamos a ver como crear una clase y posteriormente objeto en Python.

Clases en Python

Para definir una clase en Python lo haremos de la siguiente forma:

class PrimeraClase:
    pass

Solo necesitamos escribir class y el nombre que le queremos dar a la clase que por convención es CamelCase. Por último para que no falle, ya que no hemos añadido ninguna función ni variable, añadimos pass (una operación nula).

Ahora vamos a darle más funcionalidad a nuestra clase. También podemos añadir constructores y destructores como en otros lenguajes con __init__ como constructor y __del__ para el destructor:

class PrimeraClase:
    
    def __init__(self):
        print('Estoy vivo')

    def __del__(self):
        print('Este es mi fin')

Observad que tanto para el __init__ como el __del__ estamos pasando el parámetro self, este parámetro también conocido como this en otros lenguajes se debe pasar obligatoriamente como primer parámetro en todos los métodos que creemos dentro de la clase y lo utilizaremos para acceder a todos los atributos de la clase.

Crear un objeto

Crear un objeto en Python es muy sencillo, solamente tenemos que llamar a la clase para crear el objeto y listo:

pc = PrimeraClase()

Al hacer esto en nuestro ejemplo, se llamaría al constructor, lo que haría mostrar el mensaje 'Estoy vivo', al finalizar la ejecución del script se llamaría al destructor y veríamos el mensaje 'Este es mi fin'.

Si nuestra clase tuviera parámetros, solo tendríamos que pasarlos al crear el objeto:

pc = PrimeraClase(param1, param2, 'test')

Ahora que hemos visto el primer ejemplo es hora de profundizar en la POO con Python 馃殌.

Abstracción

La abstracción cuando queremos trabajar con POO digamos que sería descomponer el objeto que queremos crear para quedarnos con la información relevante y separar la información importante de los detalles secundarios. Para realizar esto podemos utilizar variables y métodos privados y públicos.

¿Como declarar métodos/variables privados y protegidos en Python?

Los métodos y variables en Python siempre serán públicos pero existen una serie de convenciones para hacerlos privados. Para declarar métodos o variables privados/protegidos lo haremos añadiendo al principio de la definición del método/variable un guion bajo (_) para los protegidos y para los privados con dos guiones (__):

class SegundaClase:
    
    _variable_protegida = 1
    __variable_privada = 2

if __name__ == "__main__":
    sc = SegundaClase()
    print(sc._variable_protegida)
    print(sc.__variable_privada)

Si intentamos acceder a la primera variable lo podremos hacer sin problema, sin embargo en la segunda se mostrará un error. Esto es porque el intérprete de Python al leer una variable o método con __ lo renombra para evitar colisiones con subclases añadiendo al inicio un guion y el nombre de clase. Para hacer la "trampa" y acceder, tendremos hacerlo de la siguiente forma:

class SegundaClase:
    
    _variable_protegida = 1
    __variable_privada = 2

if __name__ == "__main__":
    sc = SegundaClase()
    print(sc._variable_protegida)
    print(sc._SegundaClase__variable_privada)

Una vez aclarado esto, vamos a explicar que es la encapsulación y como controlar el acceso a nuestras variables privadas o protegidas.

Encapsulación

La encapsulación nos permite agrupar datos y controlar su comportamiento en nuestra clase. También nos permite controlar el acceso a nuestros datos y prevenir modificaciones no autorizadas.

Si por ejemplo, queremos controlar el acceso a una de nuestras variables o realizar una acción adicional al intentar acceder a un dato lo podremos hacer con los getters y setters.

Getters y Setters en Python

Los getters serían las funciones que nos permiten acceder a una variable privada. En Python se declaran creando una función con el decorador @property.

Los setters serían las funciones que usamos para sobreescribir la información de una variable y se generan definiendo un método con el nombre de la variable sin guiones y utilizando como decorador el nombre de la variable sin guiones más ".setter".

Y para dejarlo claro del todo, vamos a verlo con el siguiente ejemplo:

class ListadoBebidas:

    def __init__(self):
        self._bebida = 'Test cola'
        self._bebidas_validas = ['Test cola', 'Cerveza']

    @property
    def bebida(self):
        return f'La bebida oficial es {self._bebida}'

    @bebida.setter
    def bebida(self, bebida):
        if bebida in self._bebidas_validas:
            self._bebida = bebida
        else:
            raise ValueError(f'La bebida {bebida} no est谩 en el listado de bebidas v谩lidas')

if __name__ == "__main__":
    bebidas = ListadoBebidas()
    print(bebidas.bebida)
    bebidas.bebida = 'Limonada'

En este ejemplo declaramos dos variables, una llamada _bebida y una lista llamada _bebidas_validas. Para recuperar la información de la variable _bebida tendremos que hacerlo con el objeto y el nombre de la función bebida. Como veis en el ejemplo, podemos manipular el resultado para mostrar la información como queramos.

Para modificar el valor de la variable, lo haremos de la siguiente forma bebidas.bebida = 'Limonada', esto llamará el setter y se ejecutará la función. En nuestro caso se lanzará una excepción, ya que la bebida que pasamos no está en el listado de bebidas válidas.

Herencia

La herencia nos permite generar una jerarquía de clases en las que podemos compartir funcionamientos comunes y en el que existirá una clase padre también conocida como superclase y una o varias clases hijas conocidas como subclases.

Para extender de una clase padre en Python solo tendremos que pasar como parámetro el nombre de la clase padre a la hija en su definición y ya podremos usar las funcionalidades de la clase padre.

Vamos a verlo con un ejemplo:

class Rectangulo:

    def __init__(self, base, altura):
        self.base = base
        self.altura = altura

    def area(self):
        return self.base * self.altura

class Cuadrado(Rectangulo):

    def __init__(self, lado):
        super().__init__(lado, lado)


if __name__ == '__main__':
    rectangulo = Rectangulo(3, 4)
    print(rectangulo.area())

    cuadrado = Cuadrado(5)
    print(cuadrado.area())

Para este ejemplo hemos creado una clase padre llamada Rectángulo, esta recibirá en el constructor la base y la altura y cuando llamemos al método área podremos recuperar el área del rectángulo.

Para realizar la herencia, hemos creado una clase hija llamada Cuadrado que extenderá de la clase Rectángulo como se ve en el ejemplo. Ya que para calcular el área del cuadrado solo necesitamos un valor, en este caso sobreescribimos el constructor esta vez enviando un solo parámetro y luego utilizamos super().__init__ para usar el constructor de la clase padre y así guardar la base y la altura que en este caso serán el mismo valor.

Una vez hecho esto no haría falta hacer nada más. Podríamos llamar al método área desde el objeto cuadrado y funcionaría perfectamente.

Polimorfismo

El polimorfismo en la POO nos permite modificar el comportamiento de una superclase para adaptarlo a las necesidades de una subclase. Esto nos ayudará a crear una clase general con unas definiciones por defecto que luego podremos ir adaptando según las necesidades de la clase hija. Ejemplo:

class Vehiculo:

    def __init__(self, nombre):
        self.nombre = nombre

    def num_ruedas(self):
        pass

class Motocicleta(Vehiculo):
    def __init__(self):
        super().__init__('Motocicleta')

    def num_ruedas(self):
        return 2

if __name__ == "__main__":
    moto = Motocicleta()
    print(moto.num_ruedas())

En este caso tenemos una clase padre Vehículo con un método num_ruedas y una clase hija Motocicleta que heredará de Vehículo. Ya que según el vehículo que estemos creando tendrá un número de ruedas u otro, sobreescribimos el método num_ruedas para dar el resultado que buscamos.

Conclusiones

Si ya habíais visto POO en otros lenguajes, veréis que al final no difiere mucho de un lenguaje a otro, si es la primera vez que lo veis y aún tenéis dudas, podéis escribirlas en los comentarios que estaré encantando de ayudaros 馃槈.

Como siempre, os recomiendo seguirme en Twitter para estar al tanto del nuevo material que voy subiendo 馃挭.

Nos leemos 馃憢.

342 vistas

Nos tomamos en serio tu privacidad

Utilizamos cookies propias y de terceros para mejorar la experiencia del usuario a trav茅s de su navegaci贸n. Si pulsas entendido aceptas su uso. Ver pol铆tica de cookies.

馃悕 S铆gueme en Twitter

Si te gusta el contenido que subo y no quieres perderte nada, s铆gueme en Twitter y te avisar茅 cada vez que cree contenido nuevo 馃挭
Luego 隆Te sigo!