Poo Con Python Educative - Io
Poo Con Python Educative - Io
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 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í.
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 .
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
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.
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.
• IDENTIFICACIÓN
• Salario
• Departamento
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 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.
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 #
¡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.
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.
object.property
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.
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
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
• 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 .
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.
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
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.
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.
• Como puede ver en la línea 8 , cada vez que se crea un nuevo objeto, se agrega su
nombre teamMembers.
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.
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.
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.
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.
def tax(self):
return (self.salary * 0.2)
def salaryPerDay(self):
return (self.salary / 30)
Sobrecarga de método #
La sobrecarga se refiere a hacer que un método realice diferentes operaciones según la
naturaleza de sus argumentos.
# 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 salaryPerDay(self):
return (self.salary / 30)
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.
Un beneficio obvio es que el código se vuelve simple y limpio. No tenemos que hacer un
seguimiento de los diferentes métodos.
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
@classmethod
def getTeamName(cls):
return cls.teamName
print(Player.getTeamName())
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")
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.
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)
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.
• Para asegurarse de que nadie del exterior conozca esta propiedad privada , el error no
revela su identidad.
Métodos privados #
• IDes una propiedad pública , por lo que se puede acceder desde fuera y desde dentro
de la clase.
• 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í.
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
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.
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.
1. Encapsulamiento
2. Abstracció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.
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 #
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.
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 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.
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.
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 .
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
# 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
Explicación #
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 .
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:
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)
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 .
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.
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.
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)
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")
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.
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.
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)
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:
• 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
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.
class ElectricEngine():
def setChargeCapacity(self, chargeCapacity):
self.chargeCapacity = 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
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:
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 .
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 .
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.
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
class Circle():
# initializer
def __init__(self, radius=0):
self.radius = radius
self.sides = 0
• 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.
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 #
def getArea(self):
pass
Clase de rectángulo #
Clase circular #
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
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.
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:
¡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
• 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 '+'
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.
obj1 = Com(3, 7)
obj2 = Com(2, 5)
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 .
• 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.
• 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.
Escritura dinámica #
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 #
• 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.
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.
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
@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
@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.
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.
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.
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.
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()
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
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.
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 #
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 .