Programación Orientada a Objetos en Python

La Programación Orientada a Objetos (POO) es un paradigma de programación en el que los diferentes componentes de un programa de computadora se modelan a partir de objetos del mundo real. Un objeto es...

Introducción

La Programación Orientada a Objetos (POO) es un paradigma de programación donde los diferentes componentes de un programa de computadora se modelan a partir de objetos del mundo real. Un objeto es cualquier cosa que tiene unas características y puede realizar una función.

Considere un escenario en el que tiene que desarrollar un juego de carreras de autos de Fórmula 1 utilizando el enfoque de programación orientada a objetos. Lo primero que debe hacer es identificar objetos del mundo real en la carrera de Fórmula 1 real. ¿Cuáles son las entidades en una carrera de Fórmula 1 que tienen unas características y pueden realizar cualquier función? Una de las respuestas obvias a esta pregunta es el automóvil. Un automóvil puede tener características como la capacidad del motor, la marca, el modelo, el fabricante, etc. De manera similar, un automóvil puede arrancar, detenerse, acelerarse, etc. Un piloto puede ser un objeto más en una carrera de Fórmula 1. Un conductor tiene una nacionalidad, edad, sexo, etc., y puede realizar funciones como conducir el automóvil, mover el volante o cambiar la transmisión.

Al igual que en este ejemplo, en la programación orientada a objetos crearemos objetos para la entidad del mundo real correspondiente.

Es importante mencionar aquí que la programación orientada a objetos no es un concepto dependiente del lenguaje. Es un concepto de programación general y la mayoría de los lenguajes modernos, como Java, C#, C++ y Python, admiten la programación orientada a objetos. En este artículo veremos una introducción detallada a la Programación Orientada a Objetos en Python, pero antes veremos algunas de las ventajas y desventajas de la programación orientada a objetos.

Ventajas y desventajas de la programación orientada a objetos

Las siguientes son algunas de las ventajas de la programación orientada a objetos:

  • La programación orientada a objetos fomenta la reutilización. Un programa de computadora está escrito en forma de objetos y clases, que también pueden reutilizarse en otros proyectos.
  • El enfoque modular utilizado en la programación orientada a objetos da como resultado un código altamente mantenible.
  • En la programación orientada a objetos, cada clase tiene una tarea específica. Si ocurre un error en una parte del código, puede corregirlo localmente sin tener que afectar otras partes del código.
  • La encapsulación de datos (que estudiaremos más adelante en el artículo) agrega una capa adicional de seguridad al programa desarrollado utilizando el enfoque orientado a objetos.

Aunque la programación orientada a objetos tiene varias ventajas, como se mencionó, también tiene algunas desventajas, algunas de las cuales se enumeran a continuación:

  • Se necesita un conocimiento detallado del dominio del software que se está desarrollando para crear objetos. No todas las entidades del software son candidatas para ser implementadas como objetos. Puede ser difícil para los novatos identificar esta delgada línea.
  • A medida que agrega más y más clases al código, el tamaño y la complejidad del programa crece exponencialmente.

En la siguiente sección, veremos algunos de los conceptos más importantes de la programación orientada a objetos.

Como sugiere el nombre, la programación orientada a objetos tiene que ver con los objetos. Sin embargo, antes de que se pueda crear un objeto, debemos definir la clase para el objeto.

Clase

Una clase en programación orientada a objetos sirve como modelo para el objeto. Una clase puede considerarse como un mapa de la casa. Puede hacerse una idea de cómo es la casa simplemente viendo el mapa. Sin embargo, una clase en sí misma no es nada. Por ejemplo, un mapa no es una casa, solo explica cómo se verá la casa real.

La relación entre una clase y un objeto se puede entender observando la relación entre un automóvil y un Audi. Un Audi es en realidad un coche. Sin embargo, no existe tal cosa como un coche solamente. Un automóvil es un concepto abstracto, en realidad se implementa en forma de Toyota, Ferrari, Honda, etc.

La palabra clave clase se usa para crear una clase en Python. El nombre de la clase sigue a la palabra clave clase, seguida del carácter de dos puntos. El cuerpo de la clase comienza en una nueva línea, sangrada una pestaña desde la izquierda.

Veamos cómo podemos crear una clase muy básica en Python. Echa un vistazo al siguiente código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Creates class Car
class Car:

    # create class attributes
    name = "c200"
    make = "mercedez"
    model = 2008

    # create class methods
    def start(self):
        print ("Engine started")

    def stop(self):
        print ("Engine switched off")

En el ejemplo anterior, creamos una clase llamada Automóvil con tres atributos: nombre, marca y modelo. La clase car también contiene dos métodos: start() y stop().

Objetos

Anteriormente, dijimos que una clase proporciona un modelo. Sin embargo, para usar realmente los objetos y métodos de una clase, necesita crear un objeto a partir de esa clase. Hay pocos métodos y atributos de clase que se pueden usar sin un objeto, lo cual veremos en la sección posterior. Por ahora, solo tenga en cuenta que, de manera predeterminada, debemos crear un objeto de una clase antes de poder usar sus métodos y atributos.

Un objeto también se denomina instancia; por lo tanto, el proceso de creación de un objeto de una clase se denomina instanciación. En Python, para crear un objeto de una clase, simplemente necesitamos escribir el nombre de la clase seguido de paréntesis de apertura y cierre.

Vamos a crear un objeto de la clase Car que creamos en la última sección.

1
2
3
4
5
# Creates car_a object of Car class
car_a = Car()

# Creates car_b object of car class
car_b = Car()

En el script de arriba, creamos dos objetos de la clase car: car_a y car_b. Para verificar el tipo de los objetos que creamos, podemos usar el método type y pasarle el nombre de nuestro objeto. Ejecute el siguiente script:

1
print(type(car_b))

En la salida, verá:

1
<class '__main__.Car'>

Lo que dice que el tipo de objeto car_b es una clase Car.

En este punto hemos creado nuestra clase y los objetos correspondientes. Ahora es el momento de acceder a los atributos de clase y llamar al método de clase usando el objeto de clase. Para hacerlo, simplemente debe escribir el nombre del objeto, seguido del operador punto y el nombre del atributo o el método al que desea acceder o llamar, respectivamente. Echa un vistazo al siguiente ejemplo:

1
car_b.start()

En el script anterior, llamamos al método start() a través del objeto car_b. La salida será la siguiente:

1
Engine started

De manera similar, puede acceder a un atributo usando la siguiente sintaxis:

1
print(car_b.model)

En el resultado, verá el valor del atributo modelo, como se muestra a continuación:

1
2008

Atributos

En la sección anterior, vimos cómo podemos crear objetos de una clase y podemos usar esos objetos para acceder a los atributos de una clase.

En Python, cada objeto tiene algunos atributos y métodos predeterminados además de los atributos definidos por el usuario. Para ver todos los atributos y métodos de un objeto, se puede usar la función integrada dir(). Intentemos ver todos los atributos del objeto car_b que creamos en la última sección. Ejecute el siguiente script:

1
dir(car_b)

En la salida, verá los siguientes atributos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'make',
 'model',
 'name',
 'start',
 'stop']

Esta función incorporada es útil para inspeccionar todos los atributos y funciones de un objeto, especialmente cuando se usa a través de REPL de Python.

Clase frente a atributos de instancia

Los atributos se pueden categorizar ampliamente en dos tipos: atributos de clase y atributos de instancia. Los atributos de clase son compartidos por todos los objetos de una clase, mientras que los atributos de instancia son propiedad exclusiva de la instancia.

Recuerde, una instancia es solo otro nombre para el objeto. Los atributos de instancia se declaran dentro de cualquier método, mientras que los atributos de clase se declaran fuera de cualquier método. El siguiente ejemplo aclara la diferencia:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Car:

    # create class attributes
    car_count = 0

    # create class methods
    def start(self, name, make, model):
        print ("Engine started")
        self.name = name
        self.make = make
        self.model = model
        Car.car_count += 1

En el script anterior, creamos una clase Car con un atributo de clase car_count y tres atributos de instancia name, make y mode. La clase contiene un método start() que contiene los tres atributos de instancia. Los valores de los atributos de instancia se pasan como argumentos al método start(). Dentro del método start, el atributo car_count se incrementa en uno.

Es importante mencionar que dentro del método, los atributos de instancia son referidos usando la palabra clave self, mientras que los atributos de clase son referidos por el nombre de clase.

Vamos a crear un objeto de la clase Car y llamar al método start().

1
2
3
4
car_a = Car()
car_a.start("Corrola", "Toyota", 2015)
print(car_a.name)
print(car_a.car_count)

En el script anterior imprimimos el atributo de instancia name y el atributo de clase car_count. Verá en el resultado que el atributo car_count tendrá un valor de 1, como se muestra a continuación:

1
2
3
Engine started
Corrola
1

Ahora, creemos otro objeto de la clase car y llamemos al método start().

1
2
3
4
car_b = Car()
car_b.start("City", "Honda", 2013)
print(car_b.name)
print(car_b.car_count)

Ahora, si imprime el valor del atributo car_count, verá 2 en la salida. Esto se debe a que el atributo car_count es un atributo de clase y, por lo tanto, se comparte entre las instancias. El objeto car_a incrementó su valor a 1, mientras que el objeto car_b lo incrementó nuevamente, por lo que el valor final se convirtió en 2. La salida se ve así:

1
2
3
Engine started
City
2

Métodos

Como describimos anteriormente, en la programación orientada a objetos, los métodos se utilizan para implementar las funcionalidades de un objeto. En la sección anterior, creamos los métodos start() y stop() para la clase Car. Hasta ahora, hemos estado usando los objetos de una clase para llamar a los métodos. Sin embargo, existe un tipo de método al que se puede llamar directamente usando el nombre de la clase. Tal método se llama método estático.

Métodos estáticos

Para declarar un método estático, debe especificar el descriptor @staticmethod antes del nombre del método, como se muestra a continuación:

1
2
3
4
5
6
7
class Car:

    @staticmethod
    def get_class_details():
        print ("This is a car class")

Car.get_class_details()

En el script anterior, creamos una clase Car con un método estático get_class_details(). Llamemos a este método usando el nombre de la clase.

1
Car.get_class_details()

Puede ver que no necesitábamos crear una instancia de la clase Car para llamar al método get_class_details(), sino que simplemente usamos el nombre de la clase. Es importante mencionar que los métodos estáticos solo pueden acceder a los atributos de clase en Python.

Devolver múltiples valores de un método

Una de las mejores características del lenguaje Python es la capacidad de los métodos de clase para devolver múltiples valores. Echa un vistazo al siguiente ejemplo:

1
2
3
4
5
6
7
class Square:

    @staticmethod
    def get_squares(a, b):
        return a*a, b*b

print(Square.get_squares(3, 5))

En el script anterior, creamos una clase llamada Square con un método estático get_squares(). El método toma dos parámetros; multiplica cada parámetro por sí mismo y devuelve ambos resultados usando la instrucción return. En el resultado del script anterior, verá los cuadrados de 3 y 5.

El método str

Hasta ahora hemos estado imprimiendo atributos usando el método print(). Veamos qué pasa si imprimimos el objeto de una clase.

Para hacerlo, crearemos una clase Car simple con un método e intentaremos imprimir el objeto de la clase en la consola. Ejecute el siguiente script:

1
2
3
4
5
6
7
8
class Car:

    # create class methods
    def start(self):
        print ("Engine started")

car_a = Car()
print(car_a)

En el script anterior, creamos el objeto car_a de la clase Car e imprimimos su valor en la pantalla. Básicamente aquí estamos tratando el objeto car_a como una cadena. La salida se ve así:

1
<__main__.Car object at 0x000001CCCF4335C0>

La salida muestra la ubicación de memoria donde está almacenado nuestro objeto. Cada objeto de Python tiene un método __str__ por defecto. Cuando usa el objeto como una cadena, se llama al método __str__, que por defecto imprime la ubicación de memoria del objeto. Sin embargo, también puede proporcionar su propia definición para el método __str__. Por ejemplo, mira el siguiente ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Creates class Car
class Car:

    # create class methods

    def __str__(self):
        return "Car class Object"

    def start(self):
        print ("Engine started")

car_a = Car()
print(car_a)

En el script anterior, anulamos el método __str__ proporcionando nuestra propia definición personalizada para el método. Ahora, si imprime el objeto car_a, verá el mensaje "Objeto de clase de coche" en la consola. Este es el mensaje que imprimimos dentro de nuestro método personalizado __str__.

Con este método, puede crear descripciones personalizadas y más significativas para cuando se imprime un objeto. Incluso podría mostrar algunos de los datos dentro de la clase, como el nombre de una clase Persona.

Constructores

Un constructor es un método especial que se llama de forma predeterminada cada vez que crea un objeto de una clase.

Para crear un constructor, debe crear un método con la palabra clave __init__. Echa un vistazo al siguiente ejemplo:

1
2
3
4
5
6
7
8
9
class Car:

    # create class attributes
    car_count = 0

    # create class methods
    def __init__(self):
        Car.car_count +=1
        print(Car.car_count)

En el script anterior, creamos una clase Car con un atributo de clase car_count. La clase contiene un constructor que incrementa el valor de car_count e imprime el valor resultante en la pantalla.

Ahora, cada vez que se cree un objeto de la clase Car, se llamará al constructor, el valor de car_count se incrementará y se mostrará en la pantalla. Vamos a crear un objeto simple y ver qué sucede:

1
2
3
car_a = Car()
car_b = Car()
car_c = Car()

En la salida, verá un valor de 1, 2 y 3 impreso ya que con cada objeto el valor de la variable car_count se incrementa y se muestra en la pantalla.

Excepto por el nombre, el constructor se puede usar como un método ordinario. Puede pasar y recibir valores de un constructor. Por lo general, se usa de esta manera cuando desea inicializar los valores de los atributos al crear una instancia de la clase.

Variables locales frente a globales

Sabemos que hay dos tipos de atributos de Python, atributos de instancia y atributos de clase. Los atributos de una clase también se conocen como variables. Según el alcance, las variables también se pueden clasificar en dos tipos: variables locales y variables globales.

Variables locales

Una variable local en una clase es una variable a la que solo se puede acceder dentro del bloque de código donde está definida. Por ejemplo, si define una variable dentro de un método, no se puede acceder a ella desde ningún lugar fuera de ese método. Mira el siguiente guión:

1
2
3
4
5
# Creates class Car
class Car:
    def start(self):
        message = "Engine started"
        return message

En el script de arriba, creamos una variable local mensaje dentro del método start() de la clase Car. Ahora vamos a crear un objeto de la clase Car e intentar acceder a la variable local message como se muestra a continuación:

1
2
car_a = Car()
print(car_a.message)

El script anterior devolverá el siguiente error:

1
AttributeError: 'Car' object has no attribute 'message'

Esto se debe a que no podemos acceder a la variable local fuera del bloque en el que se define la variable local.

Variable global {#variable global}

Una variable global se define fuera de cualquier bloque de código, por ejemplo, método, declaraciones if, etc. Se puede acceder a una variable global en cualquier parte de la clase. Echa un vistazo al siguiente ejemplo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Creates class Car
class Car:
    message1 = "Engine started"

    def start(self):
        message2 = "Car started"
        return message2

car_a = Car()
print(car_a.message1)

En el script anterior, creamos una variable global message1 e imprimimos su valor en la pantalla. En la salida, verá el valor de la variable message1, impresa sin error.

Es importante mencionar que existe una diferencia entre los atributos de clase e instancia, y las variables locales frente a las globales. Los atributos de clase e instancia difieren en la forma en que se accede, es decir, usando el nombre de la clase y el nombre de la instancia. Por otro lado, las variables locales vs globales difieren en su alcance, o en otras palabras, el lugar donde se puede acceder. Solo se puede acceder a una variable local dentro del método. Aunque en este artículo, tanto la variable local como los atributos de instancia se definen dentro del método, el atributo local se define con la palabra clave propia.

Modificadores de acceso {#modificadores de acceso}

Los modificadores de acceso en Python se utilizan para modificar el alcance predeterminado de las variables. Hay tres tipos de modificadores de acceso en Python: públicos, privados y protegidos.

Se puede acceder a las variables con los modificadores de acceso público desde cualquier lugar dentro o fuera de la clase, a las variables privadas solo se puede acceder dentro de la clase, mientras que a las variables protegidas se puede acceder dentro del mismo paquete.

Para crear una variable privada, debe anteponer guiones bajos dobles con el nombre de la variable. Para crear una variable protegida, debe anteponer un guión bajo con el nombre de la variable. Para las variables públicas, no es necesario que agregue ningún prefijo.

Veamos las variables públicas, privadas y protegidas en acción. Ejecute el siguiente script:

1
2
3
4
5
6
class Car:
    def __init__(self):
        print ("Engine started")
        self.name = "corolla"
        self.__make = "toyota"
        self._model = 1999

En el script de arriba, creamos una clase simple Car con un constructor y tres variables name, make y model. La variable name es pública, mientras que las variables make y model han sido declaradas privadas y protegidas, respectivamente.

Vamos a crear un objeto de la clase Car e intentar acceder a la variable name. Ejecute el siguiente script:

1
2
car_a = Car()
print(car_a.name)

Dado que name es una variable pública, podemos acceder a ella fuera de la clase. En la salida, verá el valor del “nombre” impreso en la consola.

Ahora intentemos imprimir el valor de la variable make. Ejecute el siguiente script:

1
print(car_a.make)

En la salida, verá el siguiente mensaje de error:

1
AttributeError: 'Car' object has no attribute 'make'

Hemos cubierto la mayoría de los conceptos básicos de programación orientada a objetos en las últimas secciones. Ahora, hablemos de los pilares de la programación orientada a objetos: polimorfismo, herencia y encapsulación, denominados colectivamente PIE.

Herencia

La herencia en la programación orientada a objetos es bastante similar a la herencia del mundo real, donde un niño hereda algunas de las características de sus padres, además de sus propias características únicas.

En la programación orientada a objetos, la herencia significa una relación IS-A. Por ejemplo, un automóvil es un vehículo. La herencia es uno de los conceptos más sorprendentes de la programación orientada a objetos, ya que fomenta la reutilización del código.

La idea básica de la herencia en la programación orientada a objetos es que una clase puede heredar las características de otra clase. La clase que hereda otra clase se denomina clase secundaria o clase derivada, y la clase que hereda otra clase se denomina clase principal o clase base.

Echemos un vistazo a un ejemplo muy simple de herencia. Ejecute el siguiente script:

1
2
3
4
5
6
7
8
9
# Create Class Vehicle
class Vehicle:
    def vehicle_method(self):
        print("This is parent Vehicle class method")

# Create Class Car that inherits Vehicle
class Car(Vehicle):
    def car_method(self):
        print("This is child Car class method")

En el script anterior, creamos dos clases: la clase Vehicle y la clase Car que hereda la clase Vehicle. Para heredar una clase, simplemente tiene que escribir el nombre de la clase principal dentro del paréntesis que sigue al nombre de la clase secundaria. La clase Vehicle contiene un método vehicle_method() y la clase secundaria contiene un método car_method(). Sin embargo, dado que la clase Car hereda la clase Vehicle, también heredará vehicle_method().

Veamos esto en acción. Ejecute el siguiente script:

1
2
car_a = Car()
car_a.vehicle_method() # Calling parent class method

En el script anterior, creamos un objeto de la clase Car y llamamos a vehicle_method() utilizando ese objeto de clase Car. Puedes ver que la clase Car no tiene ningún vehicle_method() pero como ha heredado la clase Vehicle que contiene vehicle_method(), la clase car también puede usarla. La salida se ve así:

1
This is parent Vehicle class method

En Python, una clase principal puede tener varios elementos secundarios y, de manera similar, una clase secundaria puede tener varias clases principales. Echemos un vistazo al primer escenario. Ejecute el siguiente script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Create Class Vehicle
class Vehicle:
    def vehicle_method(self):
        print("This is parent Vehicle class method")

# Create Class Car that inherits Vehicle
class Car(Vehicle):
    def car_method(self):
        print("This is child Car class method")

# Create Class Cycle that inherits Vehicle
class Cycle(Vehicle):
    def cycleMethod(self):
        print("This is child Cycle class method")

En el script anterior, la clase principal Vehicle es heredada por dos clases secundarias Car y Cycle. Ambas clases secundarias tendrán acceso a vehicle_method() de la clase principal. Ejecute el siguiente script para ver eso:

1
2
3
4
car_a = Car()
car_a.vehicle_method() # Calling parent class method
car_b = Cycle()
car_b.vehicle_method() # Calling parent class method

En la salida, verá la salida del método vehicle_method() dos veces como se muestra a continuación:

1
2
This is parent Vehicle class method
This is parent Vehicle class method

Puede ver cómo dos clases secundarias pueden heredar una clase principal. De la misma manera, un niño puede tener varios padres. Echemos un vistazo al ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Camera:
    def camera_method(self):
        print("This is parent Camera class method")

class Radio:
    def radio_method(self):
        print("This is parent Radio class method")

class CellPhone(Camera, Radio):
     def cell_phone_method(self):
        print("This is child CellPhone class method")

En el script anterior, creamos tres clases: Camera, Radio y CellPhone. La clase Cámara y las clases Radio son heredadas por la clase CellPhone, lo que significa que la clase CellPhone tendrá acceso a los métodos de las clases Cámara y Radio. El siguiente script verifica esto:

1
2
3
cell_phone_a = CellPhone()
cell_phone_a.camera_method()
cell_phone_a.radio_method()

La salida se ve así:

1
2
This is parent Camera class method
This is parent Radio class method

Polimorfismo

El término polimorfismo literalmente significa tener múltiples formas. En el contexto de la programación orientada a objetos, el polimorfismo se refiere a la capacidad de un objeto para comportarse de múltiples formas.

El polimorfismo en la programación se implementa mediante la sobrecarga de métodos y la anulación de métodos.

Sobrecarga de métodos {#sobrecarga de métodos}

La sobrecarga de métodos se refiere a la propiedad de un método de comportarse de diferentes maneras dependiendo del número o tipo de parámetros. Eche un vistazo a un ejemplo muy simple de sobrecarga de métodos. Ejecute el siguiente script:

1
2
3
4
5
6
7
# Creates class Car
class Car:
   def start(self, a, b=None):
        if b is not None:
            print (a + b)
        else:
            print (a)

En el script anterior, si se llama al método start() pasando un solo argumento, el parámetro se imprimirá en la pantalla. Sin embargo, si pasamos 2 argumentos al método start(), agregará ambos argumentos e imprimirá el resultado de la suma.

Intentemos primero con un solo argumento:

1
2
car_a = Car()
car_a.start(10)

En la salida, verá 10. Ahora intentemos pasar 2 argumentos:

1
car_a.start(10,20)

En la salida, verás 30.

Anulación de métodos

La anulación de métodos se refiere a tener un método con el mismo nombre en la clase secundaria que en la clase principal. La definición del método difiere en las clases principal y secundaria, pero el nombre sigue siendo el mismo. Tomemos un método de ejemplo simple anulando en Python.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Create Class Vehicle
class Vehicle:
    def print_details(self):
        print("This is parent Vehicle class method")

# Create Class Car that inherits Vehicle
class Car(Vehicle):
    def print_details(self):
        print("This is child Car class method")

# Create Class Cycle that inherits Vehicle
class Cycle(Vehicle):
    def print_details(self):
        print("This is child Cycle class method")

En el script anterior, las clases Car y Cycle heredan la clase Vehicle. La clase de vehículo tiene el método print_details(), que es anulado por las clases secundarias. Ahora, si llama al método print_details(), la salida dependerá del objeto a través del cual se llama al método. Ejecute el siguiente script para ver este concepto en acción:

1
2
3
4
5
6
7
8
car_a = Vehicle()
car_a. print_details()

car_b = Car()
car_b.print_details()

car_c = Cycle()
car_c.print_details()

La salida se verá así:

1
2
3
This is parent Vehicle class method
This is child Car class method
This is child Cycle class method

Puede ver que la salida es diferente, aunque el método print_details() se llama a través de clases derivadas de la misma clase base. Sin embargo, dado que las clases secundarias anularon el método de la clase principal, los métodos se comportan de manera diferente.

Encapsulación

La encapsulación es el tercer pilar de la programación orientada a objetos. La encapsulación simplemente se refiere a la ocultación de datos. Como principio general, en la programación orientada a objetos, una clase no debe tener acceso directo a los datos de la otra clase. Más bien, el acceso debe controlarse a través de métodos de clase.

Para proporcionar acceso controlado a los datos de clase en Python, se utilizan los modificadores de acceso y las propiedades. Ya hemos visto los modificadores de acceso, en esta sección veremos las propiedades en acción.

Supongamos que queremos asegurarnos de que el modelo de automóvil siempre debe estar entre 2000 y 2018. Si un usuario intenta ingresar un valor inferior a 2000 para el modelo de automóvil, el valor se establece automáticamente en 2000 y si el valor ingresado es mayor que 2018, debe establecerse en 2018. Si el valor está entre 2000 y 2018, no debe cambiarse. Podemos crear una propiedad para el atributo del modelo que implemente esta lógica de la siguiente manera:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Creates class Car
class Car:

    # Creates Car class constructor
    def __init__(self, model):
        # initialize instance variables
        self.model = model

    # Creates model property
    @property
    def model(self):
        return self.__model

    # Create property setter
    @model.setter
    def model(self, model):
        if model < 2000:
            self.__model = 2000
        elif model > 2018:
            self.__model = 2018
        else:
            self.__model = model

    def getCarModel(self):
        return "The car model is " + str(self.model)
    
carA = Car(2088)
print(carA.getCarModel())

Una propiedad tiene tres partes. Tienes que definir el atributo, que es modelo en el script anterior. A continuación, debe definir la propiedad para el atributo mediante @decorador de propiedades. Finalmente, debe crear un setter de propiedades que sea el descriptor @model.setter en el script anterior.

Ahora, si intenta ingresar un valor mayor que 2018 para el atributo del modelo, verá que el valor está establecido en 2018. Probemos esto. Ejecute el siguiente script:

1
2
car_a = Car(2088)
print(car_a.get_car_model())

Aquí estamos pasando 2088 como valor para modelo, sin embargo, si imprime el valor para el atributo modelo a través de la función get_car_model(), verá 2018 en la salida.

Conclusión

En este artículo, estudiamos algunos de los conceptos de programación orientada a objetos más importantes. La programación orientada a objetos es uno de los paradigmas de programación más famosos y comúnmente utilizados. La importancia de la programación orientada a objetos se refleja en el hecho de que la mayoría de los lenguajes de programación modernos están completamente orientados a objetos o admiten la programación orientada a objetos. os.

Licensed under CC BY-NC-SA 4.0