0% encontró este documento útil (0 votos)
114 vistas76 páginas

Poo Con Python Educative - Io

Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
114 vistas76 páginas

Poo Con Python Educative - Io

Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 76

Una breve introducción

Esta lección le da la bienvenida al mundo de la programación orientada a objetos.

Programación procedimental #
Si está aquí, probablemente ya esté familiarizado con los conceptos básicos de la
programación y haya utilizado métodos en sus programas en algún momento.

La programación procedimental es un paradigma de programación entre muchos otros.

En la programación de procedimientos, un programa se divide en partes más pequeñas llamadas


métodos. Estos métodos son las entidades básicas que se utilizan para construir un
programa. Una de las principales ventajas de la programación procedimental es la reutilización
del código. Sin embargo, la implementación de un escenario complejo del mundo real se
convierte en una tarea difícil y difícil de manejar.

Programación orientada a objetos #


La programación orientada a objetos, también conocida como POO , es un paradigma de
programación que incluye, o se basa, en el concepto de clases y objetos .

Las entidades básicas en la programación orientada a objetos son clases y objetos.

La programación no es de mucha utilidad si no puede modelar escenarios del mundo real usando
código, ¿verdad? Aquí es donde viene la programación orientada a objetos.
La idea básica de POO es dividir un programa sofisticado en varios objetos que se comunican
entre sí.

Los objetos de un programa suelen representar objetos del mundo real.


También es posible que los objetos sirvan a la lógica de la aplicación y no tengan paralelos
directos en el mundo real. Administran cosas como autenticación, plantillas, manejo de
solicitudes o cualquiera de las otras innumerables funciones necesarias para una aplicación
práctica.

Anatomía de objetos y clases #


Los objetos pueden contener datos en forma de campos (variables) y métodos para operar
con esos datos.

Piense en los objetos del mundo real que le rodean. ¿Cuáles son las características de estos
objetos ? Tomemos el ejemplo de una bombilla . Tiene un estado , lo que significa que
está encendido o apagado . También tiene un comportamiento , lo que significa que cuando se
enciende se enciende, y cuando se apaga, no produce ninguna luz. Para concluir esto, se puede
decir:
Los objetos son una colección de datos y sus comportamientos .

Pero, ¿de dónde proceden estos objetos?

La respuesta a la pregunta anterior son las clases .


Se puede pensar en una clase como un modelo para crear objetos.
La siguiente ilustración muestra cómo LightBulbdebería verse una clase:
En la ilustración anterior, puede ver que el estado del objeto generalmente se modela
usando variables en una clase, y el comportamiento se modela usando métodos .
Puede haber muchos objetos diferentes de la misma clase. Cada uno puede estar en un estado
independiente, pero todos compartirán el mismo comportamiento y características.

Tipos de datos definidos por el usuario #


Se puede inferir a partir de la discusión anterior que las clases son los tipos de datos definidos
por el usuario implementado utilizando tipos de datos primitivos, por ejemplo, boolean, int, char,
etc. Mientras que los tipos de datos primitivos sólo se centran en el modelado de la estado
del objeto, tipos de datos definidos por el usuario pueden encapsular el estado y sus
comportamientos en una unidad.

En la próxima lección, discutiremos una variedad de lenguajes de programación orientados a


objetos, incluido Python.

Lenguajes modernos orientados a objetos


Conozca los lenguajes de programación orientados a objetos más populares y lo conveniente que es Python.

Resumen de idiomas #
Hoy en día, hay muchos lenguajes de programación orientados a objetos que se utilizan en
todo el mundo, y Python es uno de ellos.
Cada idioma tiene sus pros y sus contras. Se elige un idioma en función de la naturaleza de la
tarea a realizar. A continuación, puede encontrar una lista de los lenguajes de programación
más populares con sus fortalezas principales y aplicaciones comunes:


• Python: ciencia de datos, aprendizaje automático e inteligencia artificial
• Java: se utiliza principalmente para el desarrollo de software de nivel empresarial y
Android
• JavaScript: desarrollo web rápido y productivo
• Ruby: desarrollo de aplicaciones web
• C ++: desarrollo de software del sistema
• C #: desarrollo de juegos, formularios web y desarrollo de aplicaciones web

Comparación de código en varios idiomas #


Supongamos que queremos escribir una función simple que suma dos números y devuelve su
suma. Escribiremos esta función en cuatro lenguajes de uso común.

Para Python , simplemente escribimos la addfunción sin crear ninguna clase o importar ninguna
biblioteca y la llamamos en nuestra función principal, como en un lenguaje de procedimiento.

Sin embargo, para C ++ , Java y C # , el procedimiento es un poco más complejo y extenso. Si


intenta eliminar algunas palabras clave como Class, staticy inten estos códigos, generará un
error y los códigos no se compilarán. Por lo tanto, incluso si tiene la intención de escribir una
función simple de suma o suma en C ++ , C # o Java , siempre se enfrentará a un nivel básico
de complejidad.

Por tanto, la elección del idioma depende del programador y del problema.

Java y C # han surgido como los lenguajes orientados a objetos dominantes, pero un número
considerable de empleadores requiere el dominio de Python . También es un componente
fundamental en el ámbito académico.

A pesar de su popularidad, algunas personas se muestran escépticas sobre el uso de lenguajes


orientados a objetos. Creen que hace que el tamaño general del programa sea más
complejo. Como vimos en el ejemplo anterior, incluso escribir una función de suma simple puede
convertirse en una tarea complicada. También argumentan que los detalles de la programación
orientada a objetos, su sintaxis y peculiaridades son difíciles de aprender para el programador
y, por lo tanto, resulta en una curva de aprendizaje empinada.

No obstante, el paradigma orientado a objetos es una práctica de programación bien


establecida, y este curso está diseñado para que se familiarice con él en uno de los lenguajes
más fáciles de usar, Python.

Introducción a objetos y clases


Un breve encuentro #
Vemos objetos por todas partes en nuestro entorno. Estos objetos tienen ciertas
propiedades que los definen. Hay ciertos comportamientos que estos objetos realizan por sí
mismos y hay acciones que se pueden realizar en ellos.

Tomemos el ejemplo de un empleado de la empresa. Un empleado tiene las siguientes


propiedades o atributos :

• IDENTIFICACIÓN
• Salario
• Departamento

Las siguientes acciones o comportamientos se pueden realizar en un empleado:

• Cálculo del impuesto sobre el salario


• Cálculo de salario por día
En una empresa, cada trabajador tiene un nombre, salario y departamento diferente, pero el
tipo de trabajador es empleado . Entonces, existe un plan genérico para cada trabajador que
trabaja en la empresa, pero cada uno de ellos tiene diferentes atributos.

Una clase tiene un plano singular y los objetos son parte de una clase y se diferencian por sus
propiedades distintas.

Objetos y clases #
Supongamos que hay dos empleados en Educative, Mark y Chris . Las propiedades de Mark y
Chris se dan en la siguiente imagen:
Propiedades #

Las propiedades son variables que contienen información sobre el objeto de una clase. Un
objeto de empleado tendrá an ID, a salaryy the departmentcomo propiedades . Se pueden agregar
nuevas propiedades para que formen parte de un objeto de la clase de empleado.
Los atributos también se denominan propiedades o miembros. Para mantener la coherencia,
usaremos propiedades a lo largo del curso.

Métodos #

Los métodos son como funciones que tienen acceso a propiedades (y otros métodos) de una
clase. Los métodos pueden aceptar parámetros y devolver valores. Se utilizan para realizar
una acción sobre un objeto de una clase. En el ejemplo anterior,
tenemos tax()y salaryPerDay()como métodos de clase.

Los comportamientos también se denominan funciones o métodos miembro. Para mantener la


coherencia, utilizaremos métodos a lo largo del curso.
Beneficios de los objetos y clases #
Los objetos y las clases nos permiten crear aplicaciones complejas en Python. Por eso se
consideran los componentes básicos de los principios de la programación orientada a objetos.

• Los objetos y las clases también son fundamentales para la compartimentación del
código. Los diferentes componentes pueden convertirse en clases separadas que
interactuarían a través de interfaces. Estos componentes prefabricados también
estarán disponibles para su uso en aplicaciones futuras.

• El uso de clases facilita el mantenimiento de diferentes partes de una aplicación, ya


que es más fácil realizar cambios en las clases (más sobre esto más adelante).

En la próxima lección, discutiremos cómo declarar una clase en Python.

Declarar una clase en Python


Aprenda a declarar clases en Python.

Declaración #
En Python, las clases se definen de la siguiente manera:
class ClassName:
pass

La classpalabra clave le dice al compilador que estamos creando una clase personalizada,
seguida del nombre de la clase y el :signo.

Todas las propiedades y métodos de la clase se definirán dentro del alcance de la clase.

Reglas de nomenclatura #

Se deben cumplir las siguientes reglas al nombrar clases:

1. Debe comenzar con una letra o un guión bajo


2. Solo debe estar compuesto por números , letras o guiones bajos

Creando un objeto de clase #


El nombre de la clase, MyClassse utilizará para crear una instancia de un objeto de la clase en
nuestro programa principal. Podemos crear un objeto de una clase simplemente usando el
nombre de la clase seguido de un par de paréntesis. Es similar a llamar a una función, pero
Python puede distinguir entre los dos y crea un nuevo objeto de la clase correspondiente. A
continuación se muestra un ejemplo de esto:
class MyClass:
pass

obj = MyClass() # creating a MyClass Object


print(obj)

¡Bien hecho! Ha creado su primer programa orientado a objetos en Python. Al imprimir este
objeto, objse mostrará la dirección de memoria en la que se almacena este objeto.

Esta es solo una implementación básica de una clase de Python y no tiene ningún propósito en
particular, ya que no contiene propiedades ni métodos.

En las próximas lecciones, aprenderemos la implementación de las clases de Python en detalle.

Implementar propiedades en una clase


Obtenga información sobre cómo crear propiedades, tanto dentro como fuera de las clases, y cómo acceder a ellas.

Basándonos en los conceptos de la lección anterior, implementaremos una clase Employee, en


Python.

Implementación de la Employee class#


Implementemos la Employeeclase ilustrada a continuación. Comenzaremos con solo agregar las
propiedades de la clase y luego la ampliaremos agregando métodos en ella.

Implementemos la Employeeclase ilustrada a continuación. Comenzaremos con solo agregar las


propiedades de la clase y luego la ampliaremos agregando métodos en ella.
# this code will compile
class Employee:
# defining the properties and assigning them none
ID = None
salary = None
department = None

Hemos definido tres propiedades como variables de clase ID , salaryy department, para la
clase Employee. Discutiremos el concepto de variables de clase más adelante. Por ahora,
centrémonos en la sintaxis.
Tenga en cuenta que no inicializa los valores de las propiedades, el código Python no se
compilará . Es necesario inicializar los valores de las propiedades dentro de la clase.

El código de la segunda pestaña no se compilará porque las propiedades de la clase no se han


inicializado.

Acceder a propiedades y asignar valores #

Para acceder a las propiedades de un objeto, se utiliza la notación de puntos :

object.property

Hay dos formas de asignar valores a las propiedades de una clase.

1. Asignar valores al definir la clase.


2. Asignar valores en el código principal.
3. class Employee:
4. # defining the properties and assigning them None
5. ID = None
6. salary = None
7. department = None
8.
9. # cerating an object of the Employee class
10. Steve = Employee()
11.
12. # assigning values to properties of Steve - an object of the Employee class
13. Steve.ID = 3789
14. Steve.salary = 2500
15. Steve.department = "Human Resources"
16.
17. # Printing properties of Steve
18. print("ID =", Steve.ID)
19. print("Salary", Steve.salary)
20. print("Department:", Steve.department)

Creando propiedades fuera de una clase #


Python, al ser un lenguaje particularmente fácil de usar, proporciona al usuario una
característica que la mayoría de los lenguajes no suelen ofrecer. Es decir, crear propiedades
de un objeto fuera de la clase. Veamos un ejemplo de esto ampliando el ejemplo de
la Employeeclase que discutimos anteriormente:
Nota: La propiedad title,, solo se agregará a Steve y todos los demás objetos futuros solo tendrán
las propiedades declaradas en la clase.
class Employee:
# defining the properties and assigning them None
ID = None
salary = None
department = None

# cerating an object of the Employee class


Steve = Employee()

# assigning values to properties of Steve - an object of the Employee class


Steve.ID = 3789
Steve.salary = 2500
Steve.department = "Human Resources"
# creating a new attribute for Steve
Steve.title = "Manager"

# Printing properties of Steve


print("ID =", Steve.ID)
print("Salary", Steve.salary)
print("Department:", Steve.department)
print("Title:", Steve.title)

Inicializar objetos
Explore el mundo de los inicializadores y aprenda por qué son necesarios para una clase.

¿Qué es un inicializador? #
Como sugiere el nombre, el inicializador se usa para inicializar un objeto de una clase. Es un
método especial que describe los pasos que se realizan cuando se crea un objeto de una clase
en el programa. Se utiliza para definir y asignar valores a variables de instancia . Discutiremos
el concepto de variables de instancia en la próxima lección. Por ahora, céntrese en la sintaxis.
El método de inicialización es similar a otros métodos, pero tiene un nombre predefinido, __init__.

Los guiones bajos dobles significan que este es un método especial que el intérprete de Python
tratará como un caso especial.

El inicializador es un método especial porque no tiene un tipo de retorno . El primer parámetro


de __init__es self, que es una forma de referirse al objeto que se está inicializando. Discutiremos
esto más tarde .

Siempre es una buena práctica definirlo como el método del primer miembro en la definición
de clase.

Definición de inicializadores #
Los inicializadores se llaman cuando se crea un objeto de una clase. Vea el ejemplo a
continuación:
class Employee:
# defining the properties and assigning them None
def __init__(self, ID, salary, department):
self.ID = ID
self.salary = salary
self.department = department

# creating an object of the Employee class with default parameters


Steve = Employee(3789, 2500, "Human Resources")

# Printing properties of Steve


print("ID :", Steve.ID)
print("Salary :", Steve.salary)
print("Department :", Steve.department)

El inicializador se llama automáticamente cuando se crea un objeto de la clase. Ahora que


usaremos inicializadores para crear objetos, una buena práctica sería inicializar todas las
propiedades del objeto al definir el inicializador.
Es importante definir el inicializador con parámetros completos para evitar errores. De manera
similar a los métodos, los inicializadores también tienen la provisión de parámetros opcionales.

Inicializador con parámetros opcionales #


De manera similar a los métodos, también podemos definir inicializadores con parámetros
opcionales. Como se discutió anteriormente, es necesario asignar valores iniciales a las
propiedades de la clase al definir la clase. Entonces, al definir un inicializador con parámetros
opcionales, es esencial asignar valores predeterminados a las propiedades.
También puede tener un inicializador predeterminado que tenga todas las propiedades como
opcionales. En este caso, todos los objetos nuevos se crearán utilizando las propiedades
inicializadas en la definición del inicializador.

A continuación se muestra un ejemplo en el que Employeese crea un objeto de clase sin los
parámetros del inicializador y otro con los parámetros del inicializador.
class Employee:
# defining the properties and assigning None to them
def __init__(self, ID=None, salary=0, department=None):
self.ID = ID
self.salary = salary
self.department = department

# creating an object of the Employee class with default parameters


Steve = Employee()
Mark = Employee("3789", 2500, "Human Resources")

# Printing properties of Steve and Mark


print("Steve")
print("ID :", Steve.ID)
print("Salary :", Steve.salary)
print("Department :", Steve.department)
print("Mark")
print("ID :", Mark.ID)
print("Salary :", Mark.salary)
print("Department :", Mark.department)

Variables de clase e instancia


Aprenda los conceptos de variables de instancia y variables de clase, y cómo usar las variables de clase correctamente.

En Python, las propiedades se pueden definir en dos partes:

• Variables de clase
• Variables de instancia
Definiciones #
Variables de clase #

Las variables de clase son compartidas por todas las instancias u objetos de las clases. Un
cambio en la variable de clase cambiará el valor de esa propiedad en todos los objetos de la
clase.

Variables de instancia #

Las variables de instancia son únicas para cada instancia u objeto de la clase. Un cambio en
la variable de instancia cambiará el valor de la propiedad en ese objeto específico solamente .

Definición de variables de clase y variables de instancia #


Las variables de clase se definen fuera del inicializador y las variables de instancia se definen
dentro del inicializador.
class Player:
teamName = 'Liverpool' # class variables

def __init__(self, name):


self.name = name # creating instance variables

p1 = Player('Mark')
p2 = Player('Steve')

print("Name:", p1.name)
print("Team Name:", p1.teamName)
print("Name:", p2.name)
print("Team Name:", p2.teamName)

En la línea 2 , hemos creado una variable de clase y en la línea 5 , hemos creado una variable
de instancia.

Uso incorrecto de variables de clase #

Es imperativo utilizar las variables de clase correctamente, ya que son compartidas por todos
los objetos de la clase y pueden modificarse utilizando cualquiera de ellas. A continuación se
muestra un ejemplo de uso indebido de variables de clase:
class Player:
formerTeams = [] # class variables
teamName = 'Liverpool'
def __init__(self, name):
self.name = name # creating instance variables

p1 = Player('Mark')
p2 = Player('Steve')

p1 = Player('Mark')
p1.formerTeams.append('Barcelona') # wrong use of class variable
p2 = Player('Steve')
p2.formerTeams.append('Chelsea') # wrong use of class variable

print("Name:", p1.name)
print("Team Name:", p1.teamName)
print(p1.formerTeams)
print("Name:", p2.name)
print("Team Name:", p2.teamName)
print(p2.formerTeams)

En el ejemplo anterior, mientras que la variable de instancia namees única para todos y cada
uno de los objetos de la Playerclase, formerTeamscualquier objeto de la clase puede acceder
a la variable de clase, y se actualiza en todo momento. Estamos almacenando a todos los
jugadores que juegan actualmente para el mismo equipo, pero es posible que cada jugador del
equipo haya jugado para diferentes equipos anteriores. Para evitar este problema, la correcta
implementación del ejemplo anterior será la siguiente:
class Player:
teamName = 'Liverpool' # class variables

def __init__(self, name):


self.name = name # creating instance variables
self.formerTeams = []

p1 = Player('Mark')
p1.formerTeams.append('Barcelona')
p2 = Player('Steve')
p2.formerTeams.append('Chelsea')

print("Name:", p1.name)
print("Team Name:", p1.teamName)
print(p1.formerTeams)
print("Name:", p2.name)
print("Team Name:", p2.teamName)
print(p2.formerTeams)

Ahora la propiedad formerTeamses única para cada Playerobjeto de clase y solo ese objeto único
puede acceder a ella.

Usando variables de clase inteligentemente #


Las variables de clase son útiles al implementar propiedades que deberían ser comunes y
accesibles para todos los objetos de clase. Veamos un ejemplo de esto:
class Player:
teamName = 'Liverpool' # class variables
teamMembers = []

def __init__(self, name):


self.name = name # creating instance variables
self.formerTeams = []
self.teamMembers.append(self.name)

p1 = Player('Mark')
p2 = Player('Steve')

print("Name:", p1.name)
print("Team Members:")
print(p1.teamMembers)
print("")
print("Name:", p2.name)
print("Team Members:")
print(p2.teamMembers)
Explicación #

• En el ejemplo anterior, hemos definido una variable de clase teamMembers, que es una lista
que será compartida por todos los objetos de la clase Player.

• Esta lista teamMemberscontendrá los nombres de todas las instancias creadas de


la Playerclase.

• Como puede ver en la línea 8 , cada vez que se crea un nuevo objeto, se agrega su
nombre teamMembers.

• En las líneas 16 y 20 , podemos ver que teamMembersse accede


por p1y p2respectivamente, y ambas producen la misma salida.

Implementar métodos en una clase


Conozca el papel de los métodos en las clases y qué es la sobrecarga de métodos.

Ahora que hemos aprendido a agregar propiedades a una clase, avanzaremos hacia la
interacción entre estas propiedades y otros objetos. Aquí es donde entran en juego los
métodos. Hay tres tipos de métodos en Python:

1. métodos de instancia

2. métodos de clase

3. métodos estáticos

Vamos a discutir los métodos de instancia en esta lección ya que se utilizan más en Python
programación orientada a objetos.
Nota: Usaremos el término métodos como métodos de ejemplo en nuestro curso, ya que son los
más utilizados. Los métodos de clase y los métodos estáticos se nombrarán explícitamente como
son.

El propósito de los métodos #


Los métodos actúan como una interfaz entre un programa y las propiedades de una clase en
el programa.

Estos métodos pueden alterar el contenido de las propiedades o utilizar sus valores para
realizar un cálculo en particular.
Definición y declaración #
Un método es un grupo de declaraciones que realiza algunas operaciones y puede o no devolver
un resultado.

Ampliaremos el Employeeejemplo de clase que estudiamos en la lección anterior añadiéndole


métodos.

Parámetros del método #


Los parámetros del método permiten pasar valores al método. En Python, el primer parámetro
del método SIEMPRE debe ser self(discutido a continuación) y cuál debe ser seguido por los
parámetros restantes.

Declaración de devolución #
La declaración de retorno permite obtener el valor del método.
No es necesario especificar el tipo de retorno ya que los tipos de datos no se especifican en
Python.

La declaración de devolución debe ir seguida inmediatamente del valor de devolución.

El selfargumento #
Una de las principales diferencias entre funciones y métodos en Python es el primer argumento
en la definición del método. Convencionalmente, esto se llama self. El usuario también puede
usar diferentes nombres, pero selfcasi todos los desarrolladores que trabajan en Python lo
usan. También usaremos esta convención para facilitar la comprensión.

Esta pseudovariable proporciona una referencia al objeto de llamada, que es el objeto al que
pertenece el método o la propiedad. Si el usuario no menciona selfcomo primer argumento, el
primer parámetro se tratará como referencia al objeto.
Nota: El selfargumento solo debe pasarse en la definición del método y no cuando se llama al
método.

A continuación se muestra un ejemplo de implementación de métodos en una clase:


class Employee:
# defining the initializer
def __init__(self, ID=None, salary=None, department=None):
self.ID = ID
self.salary = salary
self.department = department

def tax(self):
return (self.salary * 0.2)

def salaryPerDay(self):
return (self.salary / 30)

# initializing an object of the Employee class


Steve = Employee(3789, 2500, "Human Resources")

# Printing properties of Steve


print("ID =", Steve.ID)
print("Salary", Steve.salary)
print("Department:", Steve.department)
print("Tax paid by Steve:", Steve.tax())
print("Salary per day of Steve", Steve.salaryPerDay())

Sobrecarga de método #
La sobrecarga se refiere a hacer que un método realice diferentes operaciones según la
naturaleza de sus argumentos.

A diferencia de otros lenguajes de programación, los métodos no pueden sobrecargarse


explícitamente en Python, pero pueden sobrecargarse implícitamente.

Para incluir argumentos opcionales, asignamos valores predeterminados a esos argumentos


en lugar de crear un método duplicado con el mismo nombre. Si el usuario elige no asignar un
valor al parámetro opcional, se asignará automáticamente un valor predeterminado a la
variable.
class Employee:
# defining the properties and assigning them None to the
def __init__(self, ID=None, salary=None, department=None):
self.ID = ID
self.salary = salary
self.department = department

# method overloading
def demo(self, a, b, c, d=5, e=None):
print("a =", a)
print("b =", b)
print("c =", c)
print("d =", d)
print("e =", e)

def tax(self, title=None):


return (self.salary * 0.2)

def salaryPerDay(self):
return (self.salary / 30)

# cerating an object of the Employee class


Steve = Employee()

# Printing properties of Steve


print("Demo 1")
Steve.demo(1, 2, 3)
print("\n")

print("Demo 2")
Steve.demo(1, 2, 3, 4)
print("\n")

print("Demo 3")
Steve.demo(1, 2, 3, 4, 5)

En el código anterior, vemos que el mismo método se comporta de manera diferente cuando
se encuentran diferentes tipos de entradas.

Si redefinimos un método varias veces y le damos diferentes argumentos, Python usa la última
definición de método para su implementación.

Ventajas de la sobrecarga de métodos #


Uno podría preguntarse que simplemente podríamos crear nuevos métodos para realizar
diferentes trabajos en lugar de sobrecargar el mismo método. Sin embargo, bajo el capó, la
sobrecarga nos ahorra memoria en el sistema. Crear nuevos métodos es más costoso que
sobrecargar uno solo.
Dado que son eficientes en la memoria, los métodos sobrecargados se compilan más rápido
en comparación con diferentes métodos, especialmente si la lista de métodos es larga.

Un beneficio obvio es que el código se vuelve simple y limpio. No tenemos que hacer un
seguimiento de los diferentes métodos.

El polimorfismo es un concepto muy importante en la programación orientada a objetos. La


sobrecarga de métodos juega un papel vital en su implementación. El concepto surgirá más
adelante en el curso.

Métodos de clase y métodos estáticos


Discutiremos métodos de clase y métodos estáticos.

Métodos en una clase de Python #


En las clases de Python, tenemos tres tipos de métodos: métodos de instancia, métodos de
clase y métodos estáticos. En la lección anterior, discutimos los métodos de instancia. En esta
lección, nos centraremos en los métodos de clase y los métodos estáticos .
Métodos de clase #
Los métodos de clase funcionan con variables de clase y se puede acceder a ellos utilizando
el nombre de la clase en lugar de su objeto. Dado que todos los objetos de clase comparten
las variables de clase, los métodos de clase se utilizan para acceder y modificar las variables
de clase.
Se accede a los métodos de clase utilizando el nombre de la clase y se puede acceder sin crear
un objeto de clase.

Sintaxis #

Para declarar un método como método de clase, usamos el decorador @classmethod. clsse usa
para referirse a la clase al igual que selfse usa para referirse al objeto de la clase. Puede usar
cualquier otro nombre en lugar de cls, pero clsse usa según la convención, y continuaremos
usando esta convención en nuestro curso.
Nota: Al igual que los métodos de instancia, todos los métodos de la clase tienen al menos
un argumento, cls.

class MyClass:
classVariable = 'educative'

@classmethod
def demo(cls):
return cls.classVariable

A continuación se muestra una implementación de código para métodos de clase:


class Player:
teamName = 'Liverpool' # class variables

def __init__(self, name):


self.name = name # creating instance variables

@classmethod
def getTeamName(cls):
return cls.teamName

print(Player.getTeamName())

En la línea 7 , usamos el @classmethoddecorador para definir getTeamNamecomo un método de clase


y en la línea 12 , estamos llamando a ese método, usando el nombre de la clase.

Métodos estáticos #
Los métodos estáticos son métodos que generalmente se limitan solo a la clase y no a sus
objetos. No tienen relación directa con variables de clase o variables de instancia. Se utilizan
como funciones de utilidad dentro de la clase o cuando no queremos que las clases heredadas
(que estudiaremos más adelante ) modifiquen la definición de un método.

Se puede acceder a los métodos estáticos utilizando el nombre de la clase o el nombre del objeto.

Sintaxis #

Para declarar un método como método estático, usamos el decorador @staticmethod. No usa una
referencia al objeto o clase, por lo que no tenemos que usar selfo cls. Podemos pasar tantos
argumentos como queramos y usar este método para realizar cualquier función sin interferir
con las variables de instancia o clase.

class MyClass:

@staticmethod
def demo()
print("I am a static method")

A continuación se muestra una implementación de código para métodos estáticos:


class Player:
teamName = 'Liverpool' # class variables

def __init__(self, name):


self.name = name # creating instance variables
@staticmethod
def demo():
print("I am a static method.")

p1 = Player('lol')
p1.demo()
Player.demo()

Los métodos estáticos no saben nada sobre el estado de la clase, es decir, no pueden
modificar los atributos de la clase. El propósito de un método estático es utilizar sus
parámetros y producir un resultado útil.

Suponga que hay una clase, que BodyInfocontiene información sobre los atributos físicos de una
persona. Podemos hacer un método estático para calcular el IMC de cualquier peso y estatura:
class BodyInfo:

@staticmethod
def bmi(weight, height):
return weight / (height**2)

weight = 75
height = 1.8
print(BodyInfo.bmi(weight, height))

Modificadores de acceso
Obtenga información sobre los miembros de datos privados, públicos y protegidos en Python.

En Python, podemos imponer restricciones de acceso a diferentes miembros de datos y


funciones de miembros. Las restricciones se especifican mediante modificadores de
acceso . Los modificadores de acceso son etiquetas que podemos asociar con cada miembro
para definir qué partes del programa pueden acceder directamente.

Hay dos tipos de modificadores de acceso en Python. Echemos un vistazo a ellos uno por uno.

Atributos públicos #
Los atributos públicos son aquellos a los que se puede acceder dentro y fuera de la clase.

Técnicamente, en Python, todos los métodos y propiedades de una clase están disponibles
públicamente de forma predeterminada. Si queremos sugerir que un método no debe usarse
públicamente, tenemos que declararlo como privado explícitamente.
A continuación, se muestra un ejemplo para implementar atributos públicos:
class Employee:
def __init__(self, ID, salary):
# all properties are public
self.ID = ID
self.salary = salary

def displayID(self):
print("ID:", self.ID)

Steve = Employee(3789, 2500)


Steve.displayID()
print(Steve.salary)

En el código anterior, las propiedades IDy salaryel método displayID()son públicos, ya que se
puede acceder a ellos tanto dentro como fuera de la clase.

Atributos privados #
No se puede acceder a los atributos privados directamente desde fuera de la clase, pero se puede
acceder desde dentro de la clase.

El objetivo es mantenerlo oculto a los usuarios y otras clases. A diferencia de muchos


lenguajes diferentes, no es una práctica generalizada en Python mantener la privacidad de los
miembros de los datos, ya que no queremos crear obstáculos para los usuarios. Podemos hacer
que los miembros sean privados usando el __prefijo de subrayado doble

Intentar acceder a atributos privados en el código principal generará un error . A continuación


se muestra un ejemplo de esto:
Propiedades privadas #

Veamos un ejemplo de código para implementar propiedades privadas:


class Employee:
def __init__(self, ID, salary):
self.ID = ID
self.__salary = salary # salary is a private property

Steve = Employee(3789, 2500)


print("ID:", Steve.ID)
print("Salary:", Steve.__salary) # this will cause an error

Explicación del código #

• En el código anterior, IDes una propiedad pública , pero __salaryes


una propiedad privada , por lo que no se puede acceder a ella fuera de la clase.

• Cuando se intenta acceder fuera de la clase, se genera el siguiente error:


'Employee' object has no attribute '__salary'

• Para asegurarse de que nadie del exterior conozca esta propiedad privada , el error no
revela su identidad.

Métodos privados #

Veamos un ejemplo de código para implementar métodos privados:


class Employee:
def __init__(self, ID, salary):
self.ID = ID
self.__salary = salary # salary is a private property
def displaySalary(self): # displaySalary is a public method
print("Salary:", self.__salary)

def __displayID(self): # displayID is a private method


print("ID:", self.ID)

Steve = Employee(3789, 2500)


Steve.displaySalary()
Steve.__displayID() # this will generate an error

Explicación del código #

• IDes una propiedad pública , por lo que se puede acceder desde fuera y desde dentro
de la clase.

• propiedad privada , por lo que no se puede acceder desde fuera de la clase,


__salaryes una
pero se puede acceder desde dentro de la clase.

• El displaySalary()método es un método público , por lo que se puede acceder desde fuera


de la clase. Este método también puede acceder a la propiedad privada__salary .

• El __displayID()método es un método privado , por lo que no se puede acceder desde fuera


de la clase.

• Cuando intentas acceder displayID()desde fuera de la clase, se genera el siguiente error:


'Employee' object has no attribute '__displayID()'

• Para asegurarse de que nadie del exterior conozca esta propiedad privada , el error no
revela su identidad.
Nota: Los métodos suelen ser públicos, ya que proporcionan una interfaz para que las propiedades
de la clase y el código principal interactúen entre sí.

Accediendo a atributos privados en el código principal #

Como se discutió anteriormente, no es común tener variables privadas en Python.

Las propiedades y los métodos con el __prefijo suelen estar presentes para asegurarse de que
el usuario no acceda a ellos por descuido . Python permite que el usuario tenga las manos
libres para evitar futuras complicaciones en el código. Si el usuario cree que es absolutamente
necesario acceder a una propiedad privada o un método, puede acceder usando
el _<ClassName>prefijo de la propiedad o método. A continuación se muestra un ejemplo de esto:
class Employee:
def __init__(self, ID, salary):
self.ID = ID
self.__salary = salary # salary is a private property

Steve = Employee(3789, 2500)


print(Steve._Employee__salary) # accessing a private property

No tan protegido #

Las clases y sus subclases pueden acceder a las propiedades y métodos protegidos en otros
lenguajes, que se analizarán más adelante en el curso. Como hemos visto, Python no tiene una
regla estricta para acceder a propiedades y métodos, por lo que no tiene el modificador de
acceso protegido.
¿Qué es la ocultación de información?
Familiarícese con un aspecto muy importante de la programación orientada a objetos llamado ocultación de datos.

Introducción #
En el capítulo anterior, se familiarizó con el concepto de objetos y clases.

En POO, los objetos y las clases son las entidades fundamentales. Los objetos se crean
mediante clases. Se puede observar que las clases contienen propiedades y que se crean
objetos para manipular y acceder a estas propiedades. Para hacer que este sistema orientado
a objetos sea más confiable y libre de errores, es una buena práctica limitar el acceso a los
miembros de la clase en ocasiones.
La ocultación de información se refiere al concepto de ocultar el funcionamiento interno de una
clase y simplemente proporcionar una interfaz a través de la cual el mundo exterior puede
interactuar con la clase sin saber qué está pasando dentro.

El propósito es implementar clases de tal manera que las instancias (objetos) de estas clases
no puedan causar ningún acceso no autorizado o cambio en el contenido original de una
clase. Una clase no necesita saber nada sobre los algoritmos subyacentes de otra clase. Sin
embargo, los dos todavía pueden comunicarse.

Un ejemplo de la vida real #


Apliquemos esto a un escenario del mundo real. Tomemos el modelo médico-paciente. En el
caso de una enfermedad, el paciente consulta al médico, después de lo cual se le prescribe un
medicamento adecuado.
El paciente solo conoce el proceso de ir al médico. El paciente desconoce la lógica y el
razonamiento detrás de la prescripción del médico. Un paciente no comprenderá los detalles
médicos que utiliza el médico para tomar una decisión sobre el tratamiento.

Este es un ejemplo clásico de la clase de pacientes que interactúa con la clase de médicos sin
conocer el funcionamiento interno de la clase de médicos.
La transacción que se muestra arriba parece relativamente sencilla. La ocultación de datos
es útil porque puede aplicar la misma simplicidad a las transacciones entre objetos de
diferentes clases.

Componentes de la ocultación de datos #


La ocultación de datos se puede dividir en dos componentes principales:

1. Encapsulamiento
2. Abstracción

Discutiremos la abstracción en un capítulo posterior . En esta sección, cubriremos la parte de


Encapsulación en detalle.
Se trataba básicamente de ocultar información y las técnicas relacionadas. En la próxima
lección, aprenderá sobre la encapsulación.

Encapsulamiento
Familiarícese con uno de los componentes del ocultamiento de datos, la encapsulación.

Definición #
La encapsulación es una técnica de programación fundamental que se utiliza para lograr la
ocultación de datos en OOP.
La encapsulación en OOP se refiere a la vinculación de datos y los métodos para manipular esos
datos juntos en una sola unidad , es decir, una clase.

Dependiendo de esta unidad, se crean objetos. La encapsulación se realiza generalmente para


ocultar el estado y la representación de un objeto desde el exterior. Se puede pensar en una
clase como una cápsula que tiene métodos y propiedades en su interior.
Al encapsular clases, una buena convención es declarar todas las variables de una
clase private. Esto restringirá el acceso directo del código fuera de esa clase.
En este punto, se puede plantear una pregunta: si los métodos y las variables están
encapsulados en una clase, ¿cómo se pueden usar fuera de esa clase ?

La respuesta a esto es simple. Uno tiene que implementar publicmétodos para permitir que el
mundo exterior se comunique con esta clase. Estos métodos se
denominan getters y setters . También podemos implementar otros métodos personalizados.

Ventajas de la encapsulación #

• Las clases hacen que el código sea fácil de cambiar y mantener.


• Las propiedades que se van a ocultar se pueden especificar fácilmente.
• Decidimos qué clases o funciones externas pueden acceder a las propiedades de la
clase.

En la siguiente lección, aprenderemos sobre algunos métodos especiales llamados getters y


setters.

Getters y Setters
Más información sobre captadores y definidores en OOP.

Obtener y configurar #
Para permitir el acceso controlado a las propiedades desde fuera de la clase, se utilizan los
métodos getter y setter.
Un método getter permite leer el valor de una propiedad.

Un método setter permite modificar el valor de una propiedad.

Es una convención común escribir el nombre de los campos miembros correspondientes con el
comando get o set.

Ejemplo #
Escribamos métodos get y set para __usernameen nuestra Userclase.
class User:
def __init__(self, username=None): # defining initializer
self.__username = username

def setUsername(self, x):


self.__username = x

def getUsername(self):
return (self.__username)

Steve = User('steve1')
print('Before setting:', Steve.getUsername())
Steve.setUsername('steve2')
print('After setting:', Steve.getUsername())
En la clase anterior User, hemos definido una propiedad privada llamada __username, a la que el
código principal no puede acceder. También tenga en cuenta que hemos comenzado el nombre
de esta propiedad privada con __.

Para que esta propiedad interactúe con cualquier entorno externo, tenemos que usar las
funciones get y set. La función get getUsername(), devuelve el valor
de __usernamey setUsername(x)establece el valor de __username igual al parámetro xpasado.

Comprensión de la encapsulación mediante ejemplos


Obtenga una comprensión más sólida de la encapsulación en Python con la ayuda de ejemplos.

Como se discutió anteriormente, la encapsulación se refiere al concepto de vinculación


de datos y los métodos que operan en esos datos en una sola unidad, que también se denomina
clase.

El objetivo es evitar que estos datos vinculados tengan acceso no deseado por parte del
código fuera de esta clase. Entendamos esto usando un ejemplo de una Userclase muy básica .

Considere que estamos listos para diseñar una aplicación y estamos trabajando en modelar
el registro en parte de esa aplicación. Sabemos que un usuario necesita un nombre de
usuario y una contraseña para iniciar sesión en la aplicación.

Una Userclase de primaria se modelará como:

• Tener una propiedad userName


• Tener una propiedad password
• Un método llamado login()para otorgar acceso

Siempre que llega un nuevo usuario, se puede crear un nuevo objeto pasando
el userNamey passwordal constructor de esta clase.
Un mal ejemplo #
Ahora es el momento de implementar la Userclase discutida anteriormente .

El código de la ilustración anterior se proporciona a continuación:


class User:
def __init__(self, userName=None, password=None):
self.userName = userName
self.password = password

def login(self, userName, password):


if ((self.userName.lower() == userName.lower())
and (self.password == password)):
print("Access Granted!")
else:
print("Invalid Credentials!")

Steve = User("Steve", "12345")


Steve.login("steve", "12345")
Steve.login("steve", "6789")
Steve.password = "6789"
Steve.login("steve", "6789")

En el ejemplo de codificación anterior, podemos observar que cualquiera puede acceder,


cambiar o imprimir los campos passwordy userNamedirectamente desde el código principal. Esto
es peligroso en el caso de esta Userclase porque no hay encapsulación de las credenciales de
un usuario, lo que significa que cualquiera puede acceder a su cuenta manipulando los datos
almacenados. Por lo tanto, el código anterior no sigue las buenas prácticas de codificación.

Un buen ejemplo #
Pasemos a una mejor implementación de la Userclase.
En el código a continuación, se AttributeErrorlanzará un porque el código fuera de la Userclase
ha intentado acceder a una privatepropiedad.
class User:
def __init__(self, userName=None, password=None):
self.__userName = userName
self.__password = password

def login(self, userName, password):


if ((self.__userName.lower() == userName.lower())
and (self.__password == password)):
print(
"Access Granted against username:",
self.__userName.lower(),
"and password:",
self.__password)
else:
print("Invalid Credentials!")

# created a new User object and stored the password and username
Steve = User("Steve", "12345")
Steve.login("steve", "12345") # Grants access because credentials are valid

# does not grant access since the credentails are invalid


Steve.login("steve", "6789")
Steve.__password # compilation error will occur due to this line

Si comenta la línea 24 , el programa funcionará.

Explicación #

• En el ejemplo anterior, los campos __userNamey __passwordse declaran de forma privada


utilizando el __prefijo.
• Podemos observar que nadie puede acceder, modificar o imprimir
los campos __passwordy __userNamedirectamente desde el código principal. Esta es una
implementación adecuada de encapsulación.
Nota: Para encapsular una clase, todas las propiedades deben ser privadas y cualquier acceso a
las propiedades debe ser a través de métodos como getters y setters .

Este es el concepto de encapsulación. Todas las propiedades que contienen datos son privadas
y los métodos proporcionan una interfaz para acceder a esas propiedades privadas.

¿Qué es la herencia?
Conozca la herencia, un concepto poderoso en programación orientada a objetos.

Ahora que está familiarizado con los conceptos de objetos y clases , analicemos la herencia ,
que es otro concepto clave en la programación orientada a objetos .

Definición #
La herencia proporciona una forma de crear una nueva clase a partir de una clase existente. La
nueva clase es una versión especializada de la clase existente, de modo que hereda todos
los campos ( variables ) y métodos no privados de la clase existente. La clase existente se
utiliza como punto de partida o como base para crear la nueva clase.

El IS A Relación #
Después de leer la definición anterior, la siguiente pregunta que me viene a la mente es la
siguiente: ¿ cuándo usamos la herencia? Dondequiera que nos encontremos con una relación IS
A entre objetos, podemos usar la herencia.

En la ilustración anterior, podemos ver que los objetos tienen una relación IS A entre
ellos. Podemos escribirlo como:
• El cuadrado ES una forma
• Python es un lenguaje de programación
• El coche es un vehículo

Entonces, a partir de las descripciones anteriores de herencia, podemos concluir que podemos
construir nuevas clases ampliando las clases existentes .

Averigüemos dónde no existe una relación IS A.

La ilustración anterior muestra que no podemos usar la herencia siempre que no


exista una relación IS A entre clases.

La clase de objeto Python #


El propósito principal de la programación orientada a objetos es permitir que un programador
modele los objetos del mundo real utilizando un lenguaje de programación.

En Python, siempre que creamos un class, es, por defecto, una subclase del Python
incorporado object class. Esto lo convierte en un excelente ejemplo de herencia en Python. Esta
clase tiene muy pocas propiedades y métodos, pero proporciona una base sólida para la
programación orientada a objetos en Python.
Pasemos a la siguiente lección donde discutiremos la sintaxis y terminologías relacionadas con
la herencia.

La sintaxis y terminologías
Aprenda a usar la herencia sintácticamente y las terminologías relacionadas con ella.

Las terminologías #
En herencia, para crear una nueva clase basada en una clase existente, usamos la siguiente
terminología:

• Clase principal (Superclase o Clase base): esta clase permite la reutilización de


sus propiedades públicas en otra clase.
• Clase secundaria (subclase o clase derivada): esta clase hereda o extiende la
superclase.

Una clase secundaria tiene todos los atributos públicos de la clase principal .

Sintaxis #
En Python, para implementar la herencia, la sintaxis es bastante similar a la definición de clase
básica. La sintaxis se da a continuación:
class ParentClass:
# attributes of the parent class

class ChildClass(ParentClass):
# attributes of the child class

El nombre de la clase principal se escribe entre paréntesis después del nombre de la clase
secundaria , seguido del cuerpo de la clase secundaria.

Ejemplo #
Tomemos un ejemplo de una Vehicleclase como clase padre e implementemos una Carclase que
se extenderá desde esta Vehicleclase. Debido a que un automóvil ES un vehículo , la
implementación de la relación de herencia entre estas clases seguirá siendo válida.
class Vehicle:
def __init__(self, make, color, model):
self.make = make
self.color = color
self.model = model

def printDetails(self):
print("Manufacturer:", self.make)
print("Color:", self.color)
print("Model:", self.model)

class Car(Vehicle):
def __init__(self, make, color, model, doors):
# calling the constructor from parent class
Vehicle.__init__(self, make, color, model)
self.doors = doors

def printCarDetails(self):
self.printDetails()
print("Doors:", self.doors)

obj1 = Car("Suzuki", "Grey", "2015", 4)


obj1.printCarDetails()

Explicación #

• En el código anterior, hemos definido una clase principal Vehicle, en la línea 1 y una clase
secundaria Car, en la línea 13 .

• Carhereda todas las propiedades y métodos de la Vehicleclase y puede acceder a ellos y


modificarlos.

• Por ejemplo, en la línea 20 de la Carclase, hemos llamado al printDetails()método, que en


realidad se definió en la Vehicleclase del printCarDetails()método.

Antes de implementar más la herencia, aprendamos otro concepto importante super(), en la


siguiente lección.

La superfunción
Conozca los usos de la superfunción en Python.
Cual es la super()funcion? #
El uso de super()entra en juego cuando implementamos la herencia. Se usa en una clase
secundaria para referirse a la clase principal sin nombrarla explícitamente. Hace que el código
sea más manejable y no es necesario conocer el nombre de la clase principal para acceder a
sus atributos.
Nota: asegúrese de agregar paréntesis al final para evitar un error de compilación.

Casos de uso de la super()función #


La superfunción se utiliza en tres contextos relevantes:

Accediendo a las propiedades de la clase principal #

Considere los campos nombrados fuelCapdefinidos dentro de una Vehicleclase para realizar un
seguimiento de la capacidad de combustible de un vehículo. Otra clase nombrada
se Car extiende a partir de esta Vehicleclase. Declaramos una propiedad de clase dentro de
la Carclase con el mismo nombre, es decir fuelCap, pero con un valor diferente. Ahora, si queremos
hacer referencia al fuelCapcampo de la clase principal dentro de la clase secundaria, tendremos
que usar la super()función.

Entendamos esto usando el siguiente código:


class Vehicle: # defining the parent class
fuelCap = 90

class Car(Vehicle): # defining the child class


fuelCap = 50

def display(self):
# accessing fuelCap from the Vehicle class using super()
print("Fuel cap from the Vehicle Class:", super().fuelCap)
# accessing fuelCap from the Car class using self
print("Fuel cap from the Car Class:", self.fuelCap)

obj1 = Car() # creating a car object


obj1.display() # calling the Car class method display()

Llamar a los métodos de la clase padre #

Al igual que las propiedades, super()también se usa con métodos. Siempre que una clase principal
y la clase secundaria inmediata tienen algún método con el mismo nombre, usamos super()para
acceder a los métodos de la clase principal dentro de la clase secundaria. Veamos un ejemplo:
class Vehicle: # defining the parent class
def display(self): # defining display method in the parent class
print("I am from the Vehicle Class")

class Car(Vehicle): # defining the child class


# defining display method in the child class
def display(self):
super().display()
print("I am from the Car Class")

obj1 = Car() # creating a car object


obj1.display() # calling the Car class method display()

Usando con inicializadores #

Otro uso esencial de la función super()es llamar al inicializador de la clase principal desde dentro
del inicializador de la clase secundaria.
Nota: Es no es necesario que la llamada a super()un método o un inicializador se hace en la primera
línea del método.

A continuación se muestra un ejemplo de uso super()en un inicializador dentro de la clase


secundaria.
class ParentClass():
def __init__(self, a, b):
self.a = a
self.b = b
class ChildClass(ParentClass):
def __init__(self, a, b, c):
super().__init__(a, b)
self.c = c

obj = ChildClass(1, 2, 3)
print(obj.a)
print(obj.b)
print(obj.c)

Como puede ver en ambas pestañas de código, cambiar el orden de la línea 9 y la línea 10 no
cambia la funcionalidad del código. Esto permite al usuario manipular los parámetros antes de
pasarlos al método de la clase principal.

Ahora, tomemos el ejemplo de la lección anterior y usemos super()para referirnos a la clase


principal:
class Vehicle:
def __init__(self, make, color, model):
self.make = make
self.color = color
self.model = model

def printDetails(self):
print("Manufacturer:", self.make)
print("Color:", self.color)
print("Model:", self.model)

class Car(Vehicle):
def __init__(self, make, color, model, doors):
Vehicle.__init__(self, make, color, model)
self.doors = doors

def printCarDetails(self):
self.printDetails()
print("Door:", self.doors)

obj1 = Car("Suzuki", "Grey", "2015", 4)


obj1.printCarDetails()

Como puede ver en los códigos anteriores, la línea 15 es intercambiable y produce el mismo
resultado, pero el uso super()hace que el código sea más manejable.
Así que esto se trataba básicamente de la super()función. En la próxima lección, discutiremos
los diferentes tipos de herencia.

Tipos de herencia
Obtenga más información sobre los distintos tipos de herencia en Python.

1. Single
2. Multi-level
3. Hierarchical
4. Multiple
5. Hybrid

Herencia única #
En herencia única, solo hay una clase que se extiende desde otra clase. Podemos tomar el
ejemplo de la Vehicleclase como clase principal y la Carclase como clase
secundaria. Implementemos estas clases a continuación:

class Vehicle: # parent class


def setTopSpeed(self, speed): # defining the set
self.topSpeed = speed
print("Top speed is set to", self.topSpeed)

class Car(Vehicle): # child class


def openTrunk(self):
print("Trunk is now open.")

corolla = Car() # creating an object of the Car class


corolla.setTopSpeed(220) # accessing methods from the parent class
corolla.openTrunk() # accessing method from its own class
Herencia multinivel #
Cuando una clase se deriva de una clase que a su vez se deriva de otra clase, se denomina
herencia multinivel. Podemos extender las clases a tantos niveles como queramos.

Implementemos las tres clases ilustradas arriba:

• A Car ES A Vehicle
• A Hybrid ES A Car
• class Vehicle: # parent class
• def setTopSpeed(self, speed): # defining the set
• self.topSpeed = speed
• print("Top speed is set to", self.topSpeed)

• class Car(Vehicle): # child class of Vehicle
• def openTrunk(self):
• print("Trunk is now open.")

• class Hybrid(Car): # child class of Car
• def turnOnHybrid(self):
• print("Hybrid mode is now switched on.")

• priusPrime = Hybrid() # creating an object of the Hybrid class
• priusPrime.setTopSpeed(220) # accessing methods from the parent class
• priusPrime.openTrunk() # accessing method from the parent class
• priusPrime.turnOnHybrid() # accessing method from the child class

Herencia jerárquica #
En la herencia jerárquica, más de una clase se extiende, según el requisito del diseño, desde
la misma clase base. Los atributos comunes de estas clases secundarias se implementan
dentro de la clase base.

Ejemplo:

• A Car ES A Vehicle
• A Truck ES A Vehicle

A continuación se muestra un ejemplo de código de herencia jerárquica.


class Vehicle: # parent class
def setTopSpeed(self, speed): # defining the set
self.topSpeed = speed
print("Top speed is set to", self.topSpeed)

class Car(Vehicle): # child class of Vehicle


pass

class Truck(Vehicle): # child class of Vehicle


pass

corolla = Car() # creating an object of the Car class


corolla.setTopSpeed(220) # accessing methods from the parent class

volvo = Truck() # creating an object of the Truck class


volvo.setTopSpeed(180) # accessing methods from the parent class

Herencia múltiple #
Cuando una clase se deriva de más de una clase base, es decir, cuando una clase tiene más
de una clase padre inmediata, se denomina herencia múltiple.
Ejemplo:

• HybridEngine ES UN ElectricEngine .
• HybridEngine ES A CombustionEngine también.

A continuación se muestra un ejemplo de código de herencia múltiple.


class CombustionEngine():
def setTankCapacity(self, tankCapacity):
self.tankCapacity = tankCapacity

class ElectricEngine():
def setChargeCapacity(self, chargeCapacity):
self.chargeCapacity = chargeCapacity

# Child class inherited from CombustionEngine and ElectricEngine


class HybridEngine(CombustionEngine, ElectricEngine):
def printDetails(self):
print("Tank Capacity:", self.tankCapacity)
print("Charge Capacity:", self.chargeCapacity)

car = HybridEngine()
car.setChargeCapacity("250 W")
car.setTankCapacity("20 Litres")
car.printDetails()

Herencia híbrida #
Un tipo de herencia que es una combinación de herencia múltiple y multinivel se
denomina herencia híbrida .

• CombustionEngine ES UN Engine .
• ElectricEngine ES UN Engine .
• HybridEngine ES A ElectricEngine y a CombustionEngine.
A continuación se muestra la implementación de código de un ejemplo de herencia híbrida.
class Engine: # Parent class
def setPower(self, power):
self.power = power

class CombustionEngine(Engine): # Child class inherited from Engine


def setTankCapacity(self, tankCapacity):
self.tankCapacity = tankCapacity

class ElectricEngine(Engine): # Child class inherited from Engine


def setChargeCapacity(self, chargeCapacity):
self.chargeCapacity = chargeCapacity

# Child class inherited from CombustionEngine and ElectricEngine

class HybridEngine(CombustionEngine, ElectricEngine):


def printDetails(self):
print("Power:", self.power)
print("Tank Capacity:", self.tankCapacity)
print("Charge Capacity:", self.chargeCapacity)

car = HybridEngine()
car.setPower("2000 CC")
car.setChargeCapacity("250 W")
car.setTankCapacity("20 Litres")
car.printDetails()
Ventajas de la herencia
Conozca las ventajas de utilizar la herencia.

Reutilización #
La herencia hace que el código sea reutilizable. Considere que está preparado para diseñar un
sistema bancario usando clases. Su modelo puede tener estos:

• Una clase para padres: BankAccount


• Una clase infantil: SavingsAccount
• Otra clase infantil: CheckingAccount

En el ejemplo anterior, no es necesario duplicar el código de


los métodos deposit()y withdraw()dentro de las clases secundarias, es
decir, SavingsAccounty CheckingAccount. De esta forma, puede evitar la duplicación de código.
Modificación de código #
Suponga que coloca el mismo código en diferentes clases, pero ¿qué sucede cuando tiene que
realizar cambios en una función y en varios lugares? Existe una alta probabilidad de que olvide
algunos lugares y se introduzcan errores. Puede evitar esto con la herencia, lo que garantizará
que todos los cambios estén localizados y se eviten las incoherencias.

Extensibilidad #
Usando la herencia, se puede extender la clase base según los requisitos de la clase
derivada. Proporciona una manera fácil de actualizar o mejorar partes específicas de un
producto sin cambiar los atributos principales. Una clase existente puede actuar como una
clase base a partir de la cual se puede derivar una nueva clase con características mejoradas.

En el ejemplo anterior, se dará cuenta en un momento posterior de que debe diversificar esta
aplicación bancaria agregando otra clase para MoneyMarketAccount. Entonces, en lugar de
implementar esta clase desde cero, puede extenderla desde la BankAccountclase existente como
punto de partida. También puede reutilizar sus atributos que son comunes
con MoneyMarketAccount.

Ocultación de datos #
La clase base puede mantener algunos datos privados para que la clase derivada no pueda
alterarlos. Este concepto se llama encapsulación y ya se discutió en el capítulo anterior.
¿Qué es el polimorfismo?
Se explicará el concepto de polimorfismo, que es un concepto importante en POO.

Definición #
La palabra polimorfismo es una combinación de dos palabras griegas, Poly
que significa muchas y Morph que significa formas .

En programación, el polimorfismo se refiere al mismo objeto que exhibe diferentes formas y


comportamientos.

Por ejemplo, tome la clase Shape. La forma exacta que elijas puede ser cualquier cosa. Puede
ser un rectángulo, un círculo, un polígono o un diamante. Si bien todas estas son formas, sus
propiedades son diferentes. A esto se le llama polimorfismo .

Una breve introducción #


Supongamos que tenemos una clase padre nombrado Shapea partir del cual las clases
hijas Rectangle, Circle, Polygon, y Diamondse derivan.
Suponga que su aplicación necesitará métodos para calcular el área de cada forma
específica. El área para cada forma se calcula de manera diferente, por lo que no puede tener
una sola implementación. Podría incluir métodos separados en cada clase (por
ejemplo getSquareArea(), getCircleArea()etc.). Pero esto hace que sea más difícil recordar el nombre
de cada método.

Simplifique las cosas con polimorfismo #

Sería mejor si todos los métodos de cálculo de áreas específicas pudieran llamarse getArea
(). Solo tendría que recordar un nombre de método y cuando llame a ese método, se llamará
al método específico de ese objeto. Esto se puede lograr en la programación orientada a
objetos utilizando polimorfismo. La clase base declara una función sin proporcionar una
implementación. Cada clase derivada hereda la declaración de función y puede proporcionar su
propia implementación
Considere que la clase Shape tiene un método llamado getArea(),que es heredado por todas las
subclases mencionadas. Con el polimorfismo, cada subclase puede tener su propia forma de
implementar el método. Por ejemplo, cuando getArea()se llama al método en un objeto de
la Rectangleclase, el método responderá mostrando el área del rectángulo. Por otro lado, cuando
se invoca el mismo método en un objeto de la Circleclase, el área del círculo se calculará y se
mostrará en la pantalla.

¿Qué logra el polimorfismo? #

En efecto, el polimorfismo reduce el trabajo del desarrollador. Cuando llega el momento de


crear subclases más específicas con ciertos atributos y comportamientos únicos, el
desarrollador puede alterar el código en las áreas específicas donde las respuestas
difieren. Todas las demás partes del código se pueden dejar intactas.

En la próxima lección, aprenderemos cómo implementar polimorfismo en OOP.

Implementación de polimorfismo mediante métodos


Implementar polimorfismo usando métodos.

Hemos aprendido cómo el polimorfismo es útil para hacer que el código sea manejable. En esta
lección, aprenderemos cómo implementar polimorfismo usando métodos. En la próxima lección ,
lo implementaremos usando herencia.
Ejemplo #
Considere dos formas que se definen como clases: Rectángulo y Círculo . Estas clases
contienen el método getArea () que calcula el área para la forma respectiva dependiendo de
los valores de sus propiedades.

class Rectangle():

# initializer
def __init__(self, width=0, height=0):
self.width = width
self.height = height
self.sides = 4

# method to calculate Area


def getArea(self):
return (self.width * self.height)

class Circle():
# initializer
def __init__(self, radius=0):
self.radius = radius
self.sides = 0

# method to calculate Area


def getArea(self):
return (self.radius * self.radius * 3.142)

shapes = [Rectangle(6, 10), Circle(7)]


print("Sides of a rectangle are", str(shapes[0].sides))
print("Area of rectangle is:", str(shapes[0].getArea()))

print("Sides of a circle are", str(shapes[1].sides))


print("Area of circle is:", str(shapes[1].getArea()))
Explicación #

• En la función principal, en la línea 25 , hemos declarado una lista que


tiene dos objetos.

• El primero objeto es un rectángulo con width6 y height10, y el segundo objeto es


un Circlede radius7.

• En las líneas 10 y 21, ambas clases tienen el método, getArea()pero la ejecución de este
método es diferente para cada clase.

• Las llamadas a métodos en las líneas 27 y 30 parecen idénticas, pero se llaman a


métodos diferentes. Así, hemos logrado el polimorfismo.

Esta fue una forma de lograr el polimorfismo. En la siguiente lección, implementaremos el


polimorfismo usando un enfoque más eficiente y de uso común: el polimorfismo usando
herencia .

Implementación de polimorfismo mediante herencia


Implemente el polimorfismo utilizando los conceptos de programación orientada a objetos.

Hasta ahora, hemos aprendido que podemos agregar nuevos datos y métodos a una clase a
través de la herencia. Pero, ¿qué pasa si queremos que nuestra clase derivada herede un
método de la clase base y tenga una implementación diferente para él? Es entonces cuando
entra en juego el polimorfismo, un concepto fundamental en el paradigma OOP.

Ejemplo #
Considere el ejemplo de una Shapeclase, que es la clase base, mientras que muchas formas
como Rectangley que se Circleextienden desde la clase base son clases derivadas. Estas clases
derivadas heredan el getArea()método y proporcionan una implementación específica de forma,
que calcula su área.
Implementación #
Primero implementaremos la clase para padres y luego las clases para niños .

Clase de forma #

La Shapeclase solo tiene un método público llamado getArea().

Veamos la implementación de la Shapeclase:


class Shape:
def __init__(self):
self.sides = 0

def getArea(self):
pass

Clase de rectángulo #

Veamos la implementación de la Rectangleclase:


# Rectangle IS A Shape with a specific width and height
class Rectangle(Shape): # derived form Shape class
# initializer
def __init__(self, width, height):
self.width = width
self.height = height
self.sides = 4

# method to calculate Area


def getArea(self):
return (self.width * self.height)

La Rectangleclase se extiende desde la Shapeclase. Hereda la sidespropiedad de la Shapeclase y


define nuevas propiedades, widthy height. El método getArea() devuelve el área del rectángulo.

Clase circular #

Veamos la implementación de la Circleclase:


# Circle IS A Shape with a specific radius
class Circle(Shape): # derived form Shape class
# initializer
def __init__(self, radius):
self.radius = radius
self.sides = 0

# method to calculate Area


def getArea(self):
return (self.radius * self.radius * 3.142)

La Circleclase se extiende desde la Shapeclase. Se hereda la sidespropiedad de la Shapesclase y


define sólo una nueva propiedad , radius. El método getArea()devuelve el área del círculo.

Programa completo #
Vea lo que sucede, fusionando todas las clases y llamando al getArea()método:
class Shape:
def __init__(self): # initializing sides of all shapes to 0
self.sides = 0

def getArea(self):
pass

class Rectangle(Shape): # derived from Shape class


# initializer
def __init__(self, width=0, height=0):
self.width = width
self.height = height
self.sides = 4

# method to calculate Area


def getArea(self):
return (self.width * self.height)

class Circle(Shape): # derived from Shape class


# initializer
def __init__(self, radius=0):
self.radius = radius

# method to calculate Area


def getArea(self):
return (self.radius * self.radius * 3.142)

shapes = [Rectangle(6, 10), Circle(7)]


print("Area of rectangle is:", str(shapes[0].getArea()))
print("Area of circle is:", str(shapes[1].getArea()))

Explicación #

En la función principal, hemos declarado una lista que tiene dos objetos. El primer objeto es
a Rectanglecon width6 y height10. El segundo objeto es a Circlede radius7.

El getArea()método devuelve el área de la forma respectiva. Esto es polimorfismo : tener


implementaciones especializadas de los mismos métodos para cada clase.

En la próxima lección, aprenderemos sobre el proceso de anulación de métodos .

Method Overriding
Una breve introducción #
La anulación de método es el proceso de redefinir el método de una clase principal en una subclase.
En otras palabras, si una subclase proporciona una implementación específica de un método
que ya se ha definido en una de sus clases principales, se conoce como anulación de método .
En el ejemplo anterior , las clases Rectangle y Circle anulaban el getArea()método de la clase
Shape.

En este caso:

• El método de la clase principal se denomina método anulado.


• Los métodos de las clases secundarias se denominan métodos primordiales.

Ya hemos visto la implementación del getArea()método en la lección anterior , que describe el


concepto de anulación. Las partes resaltadas muestran dónde está sucediendo la anulación
del método.

¡Echemos un vistazo!

class Shape:
def __init__(self): # initializing sides of all shapes to 0
self.sides = 0

def getArea(self):
pass
class Rectangle(Shape): # derived form Shape class
# initializer
def __init__(self, width=0, height=0):
self.width = width
self.height = height
self.sides = 4

# method to calculate Area


def getArea(self):
return (self.width * self.height)

class Circle(Shape): # derived form Shape class


# initializer
def __init__(self, radius=0):
self.radius = radius

# method to calculate Area


def getArea(self):
return (self.radius * self.radius * 3.142)

shapes = [Rectangle(6, 10), Circle(7)]


print("Area of rectangle is:", str(shapes[0].getArea()))
print("Area of circle is:", str(shapes[1].getArea()))

Ventajas y características clave de la anulación de métodos #


• Las clases derivadas pueden dar sus propias implementaciones específicas a los
métodos heredados sin modificar los métodos de la clase principal.

• Para cualquier método, una clase secundaria puede usar la implementación en la clase
principal o hacer su propia implementación.

• La invalidación del método necesita herencia y debe haber al menos una clase derivada
para implementarla.

• Los métodos de las clases derivadas suelen tener una implementación diferente.

Operator Overloading
Operadores de sobrecarga en Python #
Los operadores en Python se pueden sobrecargar para operar de una determinada manera
definida por el usuario. Siempre que se usa un operador en Python, se invoca su método
correspondiente para realizar su función predefinida . Por ejemplo, cuando +se
llama al operador, invoca la función especial,, __add__en Python, pero este operador actúa de
manera diferente para diferentes tipos de datos. Por ejemplo, el +operador suma los números
cuando se usa entre dos inttipos de datos y fusiona dos cadenas cuando se usa
entre stringtipos de datos.

Ejecute el siguiente código para la implementación del +operador para enteros y cadenas.
print(5 + 3) # adding integers using '+'
print("money" + "maker") # merging strings using '+'

Operadores de sobrecarga para una clase definida por el usuario #


Cuando se define una clase, sus objetos pueden interactuar entre sí a través de los
operadores, pero es necesario definir el comportamiento de estos operadores mediante la
sobrecarga de operadores.

Vamos a implementar una clase que representa un número complejo. “Un número complejo
consta de una parte real y una parte imaginaria.

Cuando agregamos un número complejo, la parte real se agrega a la parte real y la parte
imaginaria se agrega a la parte imaginaria.

De manera similar, cuando restamos un número complejo, la parte real se resta de la parte
real y la parte imaginaria se resta de la parte imaginaria.

A continuación se muestra un ejemplo de esto:


Now, let’s implement the complex number class and overload the + and - operators below:
class Com:
def __init__(self, real=0, imag=0):
self.real = real
self.imag = imag

def __add__(self, other): # overloading the `+` operator


temp = Com(self.real + other.real, self.imag + other.imag)
return temp

def __sub__(self, other): # overloading the `-` operator


temp = Com(self.real - other.real, self.imag - other.imag)
return temp

obj1 = Com(3, 7)
obj2 = Com(2, 5)

obj3 = obj1 + obj2


obj4 = obj1 - obj2

print("real of obj3:", obj3.real)


print("imag of obj3:", obj3.imag)
print("real of obj4:", obj4.real)
print("imag of obj4:", obj4.imag)

Explicación #
• En el código anterior, hemos sobrecargado el método incorporado __add__( línea 6 )
y __sub__( línea 10 ) que se invocan cuando se usan +los -operadores y.

• Siempre que Comse agregan dos objetos de clase usando el +operador, __add__se llama al
método sobrecargado .

• Este método agrega la realpropiedad por separado y la imagpropiedad por separado y


luego devuelve un nuevo Comobjeto de clase que se inicializa con estas sumas.

• Tenga en cuenta que los métodos __add__y __sub__tienen dos parámetros de entrada. El
primero es self, que sabemos que es la referencia a la clase en sí. El segundo parámetro
es other. otheres una referencia a los otros objetos que interactúan con el objeto de la
clase.
• En la línea 18 , obj2se considerará el otro objeto, se llamará al operador en
el obj1objeto y el objeto devuelto se almacenará en obj3.

• En la línea 19 , obj2se considerará el otro objeto, se llamará al operador en


el obj1objeto y el objeto devuelto se almacenará en obj4.

• Otro tiene Comatributos de clase y, por tanto, tiene las propiedades realy imag.

Puede nombrar el segundo argumento de cualquier manera, pero según la convención, usaremos la
palabra otherpara hacer referencia al otro objeto.

De manera similar, siempre que Comse restan dos objetos de clase usando el -
operador, __sub__se llama al método sobrecargado . Este método resta las propiedades realy
las imagpropiedades por separado y luego devuelve un nuevo Comobjeto de clase que se
inicializa por estas diferencias.

Funciones especiales para algunos operadores comunes #


A continuación se muestran algunas funciones especiales comunes que pueden sobrecargarse
al implementar operadores para objetos de una clase.
Corresponde al usuario decidir cómo quiere que interactúen los objetos cuando un operador
opera sobre ellos, pero generalmente necesita asegurarse de que estas operaciones tengan
sentido. Por ejemplo, +no se utilizará para buscar productos de diferentes propiedades de una
clase.

Implementing Polymorphism Using Duck Typing


Duck typing es uno de los conceptos más útiles en la programación orientada a objetos en
Python. Usando la tipificación de pato, podemos implementar polimorfismo sin usar herencia.

¿Qué es la mecanografía de pato? #

SIEMPRE QUE DICE PATO ES DUCK


Decimos que si un objeto grazna como un pato, nada como un pato, come como un pato o, en
definitiva, actúa como un pato, ese objeto es un pato.

Escritura dinámica #

Duck typing amplía el concepto de escritura dinámica en Python.


La escritura dinámica significa que podemos cambiar el tipo de un objeto más adelante en el
código.
Debido a la naturaleza dinámica de Python, la escritura pato permite al usuario usar cualquier
objeto que proporcione el comportamiento requerido sin la restricción de que tiene que ser
una subclase. Consulte el código a continuación para comprender mejor la escritura dinámica
en Python:
x = 5 # type of x is an integer
print(type(x))

x = "Educative" # type of x is now string


print(type(x))

Implementing duck typing#


Let us see an example to implement this:
class Dog:
def Speak(self):
print("Woof woof")

class Cat:
def Speak(self):
print("Meow meow")

class AnimalSound:
def Sound(self, animal):
animal.Speak()

sound = AnimalSound()
dog = Dog()
cat = Cat()

sound.Sound(dog)
sound.Sound(cat)

Explicación #

• En la línea 13 , el tipo de animalno está definido en la definición del método Sound.

• El tipo de animalse determina cuando se llama al método, por lo que no importa qué tipo
de objeto esté pasando como parámetro en el Sound()método, lo que importa es que
el Speak()método debe estar definido en todas las clases cuyos objetos se pasan en
el Sound()método.
• Podemos usar cualquier propiedad o método de animalen la AnimalSoundclase siempre que
esté declarado en esa clase.

Conclusión #

Ahora, volviendo a por qué se llama Duck typing: entonces, si un pájaro habla como un pato,
nada como un pato y come como un pato, ese pájaro es un pato.
De manera similar, en el ejemplo anterior, el animalobjeto no importa en la definición
del Soundmétodo siempre que tenga el comportamiento asociado Speak(), definido en la definición
de clase del objeto. En términos simples, dado que tanto los animales, perros como gatos ,
pueden hablar como animales , ambos son animales . Así es como hemos logrado el
polimorfismo sin herencia.

Clases base abstractas


Aprenda qué son las clases base abstractas en Python y cómo usarlas.

¿Qué son las clases base abstractas? #


Duck typing es útil ya que simplifica el código y el usuario puede implementar las funciones sin
preocuparse por el tipo de datos. Pero este puede no ser el caso todo el tiempo. Es posible
que el usuario no siga las instrucciones para implementar los pasos necesarios para escribir
pato. Para atender este problema, Python introdujo el concepto de clases base abstractas
o ABC .
Las clases base abstractas definen un conjunto de métodos y propiedades que una clase debe
implementar para ser considerada una instancia de tipo pato de esa clase.

¿Por qué utilizar clases base abstractas? #


Veamos un ejemplo para entender por qué deberíamos usar clases base abstractas.
class Shape: # Shape is a child class of ABC
def area(self):
pass

def perimeter(self):
pass

class Square(Shape):
def __init__(self, length):
self.length = length

def area(self):
return (self.length * self.length)

def perimeter(self):
return (4 * self.length)

shape = Shape()
square = Square(4)

En el ejemplo anterior, puede ver que Shapese puede crear una instancia de aunque un objeto
de esta clase no pueda sostenerse por sí solo. La Squareclase, que es la clase hija de Shape,
realmente implementa los métodos area()y perimeter()de la Shapeclase. ShapeLa clase debe
proporcionar un plano para que sus clases secundarias implementen métodos en él. Para evitar
que el usuario cree un Shapeobjeto de clase, usamos clases base abstractas .

Sintaxis #

Para definir una clase base abstracta, usamos el abcmódulo. La clase base abstracta se hereda
de la ABCclase incorporada . Tenemos que usar el decorador @abstractmethodsobre el método que
queremos declarar como método abstracto.

from abc import ABC, abstractmethod

class ParentClass(ABC):

@abstractmethod
def method(self)

Ejemplo #
A continuación se muestra una implementación de código para clases base abstractas:
from abc import ABC, abstractmethod

class Shape(ABC): # Shape is a child class of ABC


@abstractmethod
def area(self):
pass

@abstractmethod
def perimeter(self):
pass

class Square(Shape):
def __init__(self, length):
self.length = length

shape = Shape()
# this code will not compile since Shape has abstract methods without
# method definitions in it

Como se puede ver arriba, el código no se compila ya que no hemos definido los métodos
abstractos, areay perimeter, dentro de la clase padre, Shapeo la clase hija, Square. Hagámoslo
y veamos qué pasa:
from abc import ABC, abstractmethod

class Shape(ABC): # Shape is a child class of ABC


@abstractmethod
def area(self):
pass

@abstractmethod
def perimeter(self):
pass

class Square(Shape):
def __init__(self, length):
self.length = length

def area(self):
return (self.length * self.length)

def perimeter(self):
return (4 * self.length)

shape = Shape()
# this code will not compile since Shape has abstract methods without
# method definitions in it
Explicación #

• Cuando definimos los métodos, areay perimeter, en la clase hija Square, Shapeno se puede
instanciar un objeto de, pero sí un objeto de Square.
• Permitimos que el usuario tenga las manos libres sobre la definición de los métodos y
al mismo tiempo nos aseguramos de que los métodos estén definidos.
Nota: Los métodos con @abstractmethoddecoradores deben definirse en la clase secundaria.

Mediante el uso de clases base abstractas, podemos controlar las clases cuyos objetos se
pueden o no se pueden crear.
Los métodos abstractos deben definirse en las clases secundarias para una implementación
adecuada de la herencia.

Una breve introduccion


Aprenda sobre las diferentes relaciones entre clases.

Interacción entre objetos de clase #


A estas alturas, hemos aprendido todo lo que necesitamos saber sobre la definición y el
comportamiento de una clase. Los conceptos de herencia y polimorfismo nos enseñaron cómo
crear clases dependientes a partir de una clase base. Si bien la herencia representa una
relación entre clases , hay situaciones en las que existen relaciones entre objetos .

El siguiente paso para nosotros es usar diferentes objetos de clase para crear el diseño de
una aplicación. Esto significa que los objetos de clase independientes tendrán que encontrar
una forma de interactuar entre sí.
Relaciones entre clases #
Hay tres relaciones de clase principales que debemos conocer. Hemos estudiado la relación IS
A en el capítulo Herencia . Estudiaremos los otros dos a continuación:

Parte de
En esta relación, un objeto de clase es un componente de otro objeto de clase. Dadas dos
clases, clase A y clase B , que están en una parte de relación Si una clase A objeto es
una parte de la clase B objeto, o vice-versa.

Una instancia de la clase de componente solo se puede crear dentro de la clase principal. En
el ejemplo de la derecha, la clase B y la clase C tienen sus propias implementaciones, pero sus
objetos solo se crean una vez que se crea un objeto de clase A. Por tanto, part-of es una
relación dependiente.
Tiene un
Esta es una relación un poco menos concreta entre dos clases. La clase A y la clase B tienen
una relación has-a si uno o ambos necesitan el objeto del otro para realizar una operación,
pero ambos objetos de clase pueden existir independientemente el uno del otro.

Esto implica que una clase tiene una referencia a un objeto de la otra clase pero no decide el
tiempo de vida del objeto referenciado de la otra clase.
Dado que Python se compila de arriba a abajo, asegúrese de haber definido la clase antes de crear
un objeto de esa clase.

Asociación #
En la programación orientada a objetos, la asociación es el término común para las
relaciones tiene-a y parte-de , pero no se limita a estas. Dos objetos están en una relación
de asociación es una declaración genérica, lo que significa que no nos preocupamos por la
dependencia de por vida entre los objetos.

En las próximas lecciones, nos sumergiremos en las formas especializadas de


asociación: agregación y composición .

Agregación
Familiarícese con una nueva forma de vincular diferentes clases.

La agregación sigue el modelo Has-A . Esto crea una relación padre-hijo entre dos clases,
donde una clase posee el objeto de otra.

Entonces, ¿qué hace que la agregación sea única?


Vidas independientes #
En conjunto , la vida útil del objeto de propiedad no depende de la vida útil del propietario.

El objeto propietario podría eliminarse, pero el objeto propietario puede seguir existiendo en
el programa. En conjunto, el padre solo contiene una referencia al hijo, lo que elimina la
dependencia del hijo.

Probablemente pueda adivinar a partir de la ilustración anterior que necesitaremos


referencias de objetos para implementar la agregación.

Ejemplo #
Tomemos el ejemplo de las personas y su país de origen. Cada persona está asociada con un
país, pero el país puede existir sin esa persona.
class Country:
def __init__(self, name=None, population=0):
self.name = name
self.population = population

def printDetails(self):
print("Country Name:", self.name)
print("Country Population", self.population)

class Person:
def __init__(self, name, country):
self.name = name
self.country = country

def printDetails(self):
print("Person Name:", self.name)
self.country.printDetails()

c = Country("Wales", 1500)
p = Person("Joe", c)
p.printDetails()

# deletes the object p


del p
print("")
c.printDetails()

Como podemos ver, el Countryobjeto cvive en incluso después de eliminar el Personobjeto p. Esto
crea una relación más débil entre las dos clases.

En la siguiente lección, aprenderá sobre composición, que es otra técnica para relacionar
objetos en Python.

Composición
Aprenda a lograr la composición en Python

La composición es la práctica de acceder a otros objetos de clase en su clase. En tal


escenario, la clase que crea el objeto de la otra clase se conoce como propietario y es
responsable de la vida útil de ese objeto.

Las relaciones de composición son relaciones de parte de donde la parte debe constituir un
segmento del objeto completo. Podemos lograr la composición agregando partes más
pequeñas de otras clases para hacer una unidad compleja.

Pero, ¿qué hace que la composición sea tan única?


En composición, la vida útil del objeto de propiedad depende de la vida útil del propietario.

Ejemplo #
A carestá compuesto por un motor , neumáticos y puertas . En este caso, una Carpropiedad de
estos objetos, por lo que una Cares un propietario de clase, y el tires, doorsy enginelas clases
se Owned clases.
Implementación #

Veamos la implementación de la Carclase para una mejor comprensión:

class Engine:
def __init__(self, capacity=0):
self.capacity = capacity

def printDetails(self):
print("Engine Details:", self.capacity)

class Tires:
def __init__(self, tires=0):
self.tires = tires

def printDetails(self):
print("Number of tires:", self.tires)

class Doors:
def __init__(self, doors=0):
self.doors = doors

def printDetails(self):
print("Number of doors:", self.doors)

class Car:
def __init__(self, eng, tr, dr, color):
self.eObj = Engine(eng)
self.tObj = Tires(tr)
self.dObj = Doors(dr)
self.color = color

def printDetails(self):
self.eObj.printDetails()
self.tObj.printDetails()
self.dObj.printDetails()
print("Car color:", self.color)

Hemos creado una Carclase que contiene los objetos de Engine, Tiresy Doorsclases. Carla clase
es responsable de su vida, es decir, cuando el automóvil muere, también lo hacen
los neumáticos , el motor y las puertas .

También podría gustarte