#10 Programación Orientada a Objetos en Python

hace 2 años · Actualizado hace 1 año

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors

Como te decía en la primera parte de este curso, Python es un lenguaje multiparadigma, por lo tanto podemos implementar nuestros programas en prácticamente todos los paradigmas disponibles.

La Programación Orientada a Objetos (POO) es uno de los paradigmas más utilizados y hoy voy a hablarte de que es la POO, en qué se basa, como implementarlo en Python y cuales son las ventajas de utilizar POO, entre otras cosas.

Dado que la POO es un tema muy amplio, voy a cubrir la mayor parte de conceptos que pueda para que entiendas la orientación a objetos y seas capaz de crear tus propias implementaciones.

Entrar al curso completo de Python desde cero a experto

Índice
  1. Python es un lenguaje multiparadigma: también es orientado a objetos
    1. Principios de la programación orientada a objetos
  2. Objetos y Clases en Python
    1. Clases primitivas
    2. Constructor de clase en Python
    3. Atributos y métodos
  3. Herencia en Python
    1. Jerarquía de las clases
  4. Sobrecarga y Polimorfismo en Python

Python es un lenguaje multiparadigma: también es orientado a objetos

Como te decía, Python permite la programación orientada a objetos. Si sabes programar de manera funcional o secuencial no será muy complicado entender la POO, sólo deberás conocer los principios básicos de este tipo de paradigma y como se lleva a cabo. Y de eso es de lo que te voy a hablar hoy.

Principios de la programación orientada a objetos

La POO está basada en la reutilización de código elevado a la máxima potencia, con la idea de modelar diferentes elementos, que se llaman objetos, que tienen una serie de atributos y comportamientos en común.

Concretamente, la programación orientada a objetos se basa en los siguientes principios básicos:

  • Encapsulación de la información. Un objeto en la POO tiene una serie de atributos que representan los datos internos y una serie de métodos, que son funciones que operan con estos atributos para modelar el comportamiento. La idea de la encapsulación de los datos es que desde el exterior de un objeto no se puede acceder a los datos internos de forma directa, pero sí mediante métodos que permiten ver o modificar las características del objeto.
  • Abstracción de los datos internos. La POO se basa en la abstracción de la información y de los procedimientos. Es decir, la persona que utiliza un objeto, debe saber qué métodos tiene, qué hacen dichos métodos y qué parámetros son necesarios para invocarlos. Sin embargo, esta persona no necesita saber CÓMO se implementa internamente la lógica de cada método.
  • Herencia de comportamiento. Basándose en la idea de la reutilización de código y evitar tener duplicidades, se utiliza la herencia entre objetos que comparten comportamientos y atributos. Para ello, se genera una clase padre que tiene el comportamiento más genérico y las clases hijas heredan los comportamientos básicos, adaptando el resto de comportamientos a sus condiciones concretas.
  • Polimorfismo. De la mano de la herencia surge el polimorfismo, que se basa en las técnicas que utilizamos para modificar ciertos comportamientos de los objetos hijos de la clase padre, de forma que podamos especificar las diferencias entre ambas clases.

Objetos y Clases en Python

Una clase es una especie de modelo que sirve para crear objetos que comparten atributos y métodos. Dos objetos pueden ser de la misma clase pero no debe haber dos clases iguales.

Para crear una clase, debemos utilizar la palabra reservada class seguida del nombre que le vayamos a poner a la clase. Los nombre de las clases en Python deben tener la primera letra en mayúsculas.

Clases primitivas

Aunque no te hayas dado cuenta, en Python ya has utilizado objetos predefinidos: las clases primitivas. Las clases primitivas son Integer, String, Float, entre otras. Es decir, los tipos básicos que utilizamos en Python son clases primitivas y tienen sus métodos y atributos correspondientes.

Por eso, a la hora de trabajar con clases y objetos, debes tener en mente que se utilizan de la misma manera que estas clases primitivas.

Constructor de clase en Python

Para crear un objeto de una clase, o lo que llamamos instanciar un objeto debemos utilizar el constructor de clase. Para ello se utiliza el nombre que le hayamos asignado a la clase seguido de paréntesis:

obj = MiClase(parámetros)

De esta manera, creamos una nueva instancia llamada obj de la clase MiClase, que inicialmente no tiene ningún atributo asociado.

Atributos y métodos

Los atributos y métodos son elementos que forman las clases y se utilizan para asignarle una forma y comportamiento a las clases.

Qué son los atributos y métodos de una clase

Concretamente, los atributos sirven para aportar información concreta a la clase y poder diferenciarla de otras clases. Por ejemplo, si quisiéramos modelar la clase vehículo deberíamos tener atributos como: número de ruedas (número entero), motor (variable booleana), peso (número flotante), entre otros atributos.

De esta manera, cada instancia, u objeto, de la clase vehículo tendrá unos atributos que identifican el objeto como un vehículo pero que al mismo tiempo, el valor de dichos atributos lo diferencian del resto de objetos de la misma clase.

Por otro lado, los métodos son los elementos encargados de asignarle el comportamiento concreto a una clase. Para ello, se generan métodos que imiten el comportamiento de lo que queremos modelar. En el ejemplo del vehículo, deberíamos tener métodos que implementen el comportamiento de arrancar, desplazarse o frenar, entre otros.

Definición de métodos y de atributos

Debemos definir los métodos y atributos dentro de la clase en cuestión, usando la siguiente estructura:

class NombreClase():
    atributo1 = valor_atr1
    atributo2 = valor_atr2
    ...

    def metodo1 (self [, parametros]):
        acciones del metodo

    def metodo2 (self [, parametros]):
        acciones del metodo
    ...

La palabra reservada self

Dentro de una clase, podemos tener más variables o funciones, que no queremos que sean estrictamente atributos y métodos de la clase. Es decir, no queremos que se pueda acceder a ellos desde el exterior.

Para esto, se utiliza la palabra reservada self para definir todo aquello que pertenece a la clase y que se puede utilizar desde fuera. Por ejemplo, para nombrar o utilizar los atributos de una clase:

self.nombreAtr = valorAtr # inicializar el valor

Para crear o llamar a un método de una clase, usamos la siguiente estructura:

# crear:
def nombreMetodo (self, parametros):
   operaciones

# usar:
self.nombreMetodo(parametros)

El método __init__

Hay algunos métodos especiales, como el método __init__ o __str__, que están definidos por defecto, pero que es probable que queramos sobre-escribirlos (es decir, volver a implementarlos) para que funcionen como nosotros queramos.

Concretamente, el método __init__ es el inicializador de la clase, es el método al que internamente se llama cada vez que creamos una instancia de una clase, es decir, cuando hacemos:

nuevo_vehículo = Vehículo()

Al llamar a la clase Vehículo, se llama a la función __init__ dentro de la clase Vehículo. Como te decía, este método sirve para asignar los valores concretos que queramos asignar a los atributos. Por ejemplo, si tuviésemos el método __init__:

class Vehiculo():
    def __init__(self, ruedas, motor, peso):
        self.ruedas = ruedas
        self.motor = motor
        self.peso = peso

Este es el comportamiento más típico de la función __init__, donde se asignan los atributos de la clase (self. ...) a los valores que se pasan por parámetro cuando instanciamos la clase.

La función __init__ no devuelve nada, simplemente se encarga de asignar valores a los atributos de la clase.

Atributos de instancia vs atributos de clase

Una vez explicado lo anterior, y sabemos como se usa la palabra reservada self y el método __init__, has de saber que dependiendo de como se inicialice un atributo, puede ser de instancia o de clase.

La diferencia radica en si se usa como si fuera una constante de la clase, igual para todos las instancias de la clase, o como un valor variable, que puede variar para cada instancia, modificándose en el método __init__. Es decir, tomando como ejemplo lo siguiente:

class NombreClase():
    # atributos de clase
    self.atr1 = valor1 
    self.atr2 = valor2

    def __init__ (self, valor3, valor4):
        # atributos de instancia
        self.atr3 = valor3
        self.atr4 = valor4

En esta clase de ejemplo se tendrían los atributos atr1 y atr2 como atributos de clase, que no varían entre diferentes instancias y atr3 y atr4, que tendrán valores diferentes para cada instancia de la clase.

El método __str__

Otro método especial es el __str__ que se utiliza internamente cuando llamamos a la función print o str y como parámetro utilizamos la instancia de una clase.

De igual manera que al método __init__ podemos darle nosotros el comportamiento que nos interese para que cuando llamemos a la función str o print, veamos los datos en un formato concreto.

Por ejemplo, siguiendo con la clase Vehículo, imaginemos que queremos que cuando usemos print(coche), siendo coche una instancia de la clase vehículo, se muestre la información en formato resumen, podríamos definir el método __str__ de la siguiente manera:

def __str__ (self):
   
    motor = "" if self.motor else "no "
    
    return "El vehículo tiene {} ruedas, {}funciona con motor y pesa {} kg".format(self.ruedas, motor, self.peso)

Así se imprimiría un mensaje personalizado según cuáles sean los atributos concretos de la instancia u objeto.

Herencia en Python

A lo largo del tutorial te he hablado de la clase Vehículo y de como instanciar objetos, como por ejemplo el objeto coche de antes. A modo del ejemplo sirve para que me entiendas, pero técnicamente no deberías hacerlo así, para eso existe la herencia.

La herencia es un concepto asociado a la programación orientada a objetos y se utiliza cuando tenemos una clase genérica que puede tener diferentes tipos de sub-clases que comparten características. Me explico: con la clase"padre" Vehículo podríamos tener varias clases heredadas, como por ejemplo: Coche, Motocicleta, Bicicleta, etc, y cuando queramos tener un objeto coche, instanciamos la clase Coche, heredada de Vehículo.

Jerarquía de las clases

La herencia se basa en el concepto de reutilización de código, tanto de atributos como de métodos. Una clase heredada puede reutilizar los métodos declarados en la clase padre, pero también puede sobreescribir dicho comportamiento (te cuento más de esto en el siguiente apartado).

Como puede ver, cada clase puede tener varias clases herederas, que compartirán los métodos y atributos de la clase padre.

Sobrecarga y Polimorfismo en Python

Antes te iba diciendo que las clases hijas pueden sobreescribir el comportamiento heredado de las clases padre, así como sus atributos. A este comportamiento se le llama sobrecarga. Por otro lado, el comportamiento de que se llame al método del padre cuando se instancia un objeto padre y al método del hijo cuando se instancia dicho objeto, se conoce como polimorfismo.

Estos dos conceptos, como te habrás dado cuenta, van cogidos de la mano pero son cosas diferentes. La sobrecarga existe en diferentes lenguajes y se puede utilizar tanto como para modificar únicamente el comportamiento o para modificar también cuales son los parámetros de entra de los métodos sobrecargados.

Pionera del caos

Otras Entradas Relacionadas

Deja una respuesta

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



Para continuar es necesario que confirmes la política de privacidad de la web.

Subir