Chapter 8: Object-Oriented Programming
8.3: Herencia
Herencia es una de las características más poderosas de la programación orientada a objetos, y puede mejorar enormemente la eficiencia de tu código. Al permitir que una clase herede los atributos y métodos de otra clase, puedes crear nuevas clases que se basan en clases existentes, lo que puede ahorrar mucho tiempo y esfuerzo.
Ejemplo: Imagina que tienes una clase que representa un automóvil, con atributos como marca, modelo y año, y métodos como encender el motor y acelerar. Ahora, supongamos que deseas crear una nueva clase para un tipo específico de automóvil, como un automóvil deportivo. En lugar de comenzar desde cero y definir todos los atributos y métodos para el automóvil deportivo, puedes simplemente crear una nueva clase que herede de la clase de automóvil y luego agregar o modificar los atributos y métodos según sea necesario. De esta manera, puedes reutilizar gran parte del código que ya escribiste para la clase de automóvil y solo enfocarte en los cambios que son específicos del automóvil deportivo.
Implementación en Python: En Python, la herencia se implementa definiendo una nueva clase que toma la clase padre (base) como argumento. La nueva clase se llama clase hija (derivada) y la clase de la que hereda se llama clase padre (base). Al usar la herencia, puedes crear jerarquías de clases complejas que pueden ayudarte a organizar tu código y hacerlo más modular y reutilizable.
Ejemplo de Herencia: Considera una clase base llamada Animal
que representa un animal genérico, con atributos como name
, age
y un método llamado speak()
:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print(f"{self.name} makes a noise")
Ahora, queremos crear una clase Dog
que represente a un perro, que es un tipo específico de animal. En lugar de redefinir todos los atributos y métodos de la clase Animal
, podemos heredarlos usando la herencia:
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age)
self.breed = breed
def speak(self):
print(f"{self.name} barks")
En este ejemplo, definimos la clase Dog
y heredamos de la clase Animal. Usamos la función super() para llamar al método __init__
de la clase padre para inicializar los atributos name
y age
. También agregamos un nuevo atributo, breed
, específico de la clase Dog
.
También sobrescribimos el método speak()
de la clase Animal para adaptarlo mejor al comportamiento de un perro. Esto se llama sobrecarga de métodos y es una práctica común cuando se usa la herencia.
Ahora, cuando creamos un objeto Perro, tendrá acceso tanto a los atributos y métodos de la clase Animal, como a cualquier nuevo atributo o método que definamos en la clase Perro.
dog1 = Dog("Max", 3, "Labrador")
print(dog1.name) # Output: Max
print(dog1.age) # Output: 3
print(dog1.breed) # Output: Labrador
dog1.speak() # Output: Max barks
La herencia es un concepto fundamental en la programación orientada a objetos que permite la creación de clases más especializadas. Facilita la reutilización de código de clases más generales al mismo tiempo que brinda la capacidad de personalizar o extender su comportamiento según sea necesario. Esto convierte a la herencia en una herramienta poderosa para que los desarrolladores creen soluciones de software eficientes y efectivas.
Al utilizar la herencia, los desarrolladores pueden crear una jerarquía de clases que comparten características comunes, lo que permite la implementación de sistemas complejos. Además, la herencia ayuda a reducir la complejidad del código, haciéndolo más fácil de mantener y actualizar con el tiempo. Por lo tanto, es esencial tener una sólida comprensión de la herencia al desarrollar aplicaciones de software, ya que puede mejorar enormemente la calidad general del código y hacerlo más escalable.
Ejercicio 8.3.1: Herencia Simple
Implementa una jerarquía de clases para diferentes tipos de vehículos, utilizando herencia.
Instrucciones:
- Crea una clase base llamada Vehicle con los atributos make, model y year.
- Define un método vehicle_info() que imprima la marca, modelo y año del vehículo.
- Crea una clase Car que herede de Vehicle y tenga un atributo adicional doors.
- Crea una clase Motorcycle que herede de Vehicle y tenga un atributo adicional type.
- Crea instancias de Car y Motorcycle, y llama al método vehicle_info() para cada una.
Solución:
class Vehicle:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def vehicle_info(self):
print(f"{self.year} {self.make} {self.model}")
class Car(Vehicle):
def __init__(self, make, model, year, doors):
super().__init__(make, model, year)
self.doors = doors
class Motorcycle(Vehicle):
def __init__(self, make, model, year, bike_type):
super().__init__(make, model, year)
self.type = bike_type
car1 = Car("Toyota", "Camry", 2020, 4)
car1.vehicle_info() # Output: 2020 Toyota Camry
motorcycle1 = Motorcycle("Yamaha", "R1", 2019, "Sport")
motorcycle1.vehicle_info() # Output: 2019 Yamaha R1
Ejercicio 8.3.2: Herencia y Sobrecarga de Métodos
Crea clases que representen diferentes tipos de cuentas bancarias, utilizando herencia y sobrecarga de métodos.
Instrucciones:
- Crea una clase base llamada BankAccount con los atributos balance y un método deposit().
- Define un método withdraw() que compruebe si la cantidad a retirar es menor o igual al saldo, y actualice el saldo en consecuencia.
- Crea una clase SavingsAccount que herede de BankAccount y tenga un atributo adicional interest_rate.
- Sobrecarga el método withdraw() en SavingsAccount para incluir una tarifa de retiro del 1% de la cantidad retirada.
- Crea instancias de BankAccount y SavingsAccount, y prueba los métodos deposit() y withdraw().
Solución:
class BankAccount:
def __init__(self):
self.balance = 0
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
else:
print("Insufficient funds")
class SavingsAccount(BankAccount):
def __init__(self, interest_rate):
super().__init__()
self.interest_rate = interest_rate
def withdraw(self, amount):
fee = amount * 0.01
if amount + fee <= self.balance:
self.balance -= (amount + fee)
else:
print("Insufficient funds")
account1 = BankAccount()
account1.deposit(100)
account1.withdraw(50)
print(account1.balance) # Output: 50
savings1 = SavingsAccount(0.02)
savings1.deposit(100)
savings1.withdraw(50)
print(savings1.balance) # Output: 49.5
Ejercicio 8.3.3: Herencia Múltiple
Implementa una jerarquía de clases utilizando herencia múltiple.
Instrucciones:
- Crea una clase Person con los atributos first_name y last_name.
- Crea una clase Employee que herede de Person y tenga un atributo adicional employee_id.
- Crea una clase Student que herede de Person y tenga un atributo adicional student_id.
- Crea una clase TeachingAssistant que herede de ambas, Employee y Student.
- Define un método get_info() para cada clase que devuelva una cadena con la información de la persona.
- Crea una instancia de TeachingAssistant y llama al método get_info().
Solución:
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def get_info(self):
return f"{self.first_name} {self.last_name}"
class Employee(Person):
def __init__(self, first_name, last_name, employee_id):
super().__init__(first_name, last_name)
self.employee_id = employee_id
def get_info(self):
return f"{super().get_info()}, Employee ID: {self.employee_id}"
class Student(Person):
def __init__(self, first_name, last_name, student_id):
super().__init__(first_name, last_name)
self.student_id = student_id
def get_info(self):
return f"{super().get_info()}, Student ID: {self.student_id}"
class TeachingAssistant(Employee, Student):
def __init__(self, first_name, last_name, employee_id, student_id):
Employee.__init__(self, first_name, last_name, employee_id)
Student.__init__(self, first_name, last_name, student_id)
def get_info(self):
return f"{super().get_info()}, Employee ID: {self.employee_id}, Student ID: {self.student_id}"
ta1 = TeachingAssistant("John", "Doe", 1001, 2001)
print(ta1.get_info()) # Output: John Doe, Employee ID: 1001, Student ID: 2001
8.3: Herencia
Herencia es una de las características más poderosas de la programación orientada a objetos, y puede mejorar enormemente la eficiencia de tu código. Al permitir que una clase herede los atributos y métodos de otra clase, puedes crear nuevas clases que se basan en clases existentes, lo que puede ahorrar mucho tiempo y esfuerzo.
Ejemplo: Imagina que tienes una clase que representa un automóvil, con atributos como marca, modelo y año, y métodos como encender el motor y acelerar. Ahora, supongamos que deseas crear una nueva clase para un tipo específico de automóvil, como un automóvil deportivo. En lugar de comenzar desde cero y definir todos los atributos y métodos para el automóvil deportivo, puedes simplemente crear una nueva clase que herede de la clase de automóvil y luego agregar o modificar los atributos y métodos según sea necesario. De esta manera, puedes reutilizar gran parte del código que ya escribiste para la clase de automóvil y solo enfocarte en los cambios que son específicos del automóvil deportivo.
Implementación en Python: En Python, la herencia se implementa definiendo una nueva clase que toma la clase padre (base) como argumento. La nueva clase se llama clase hija (derivada) y la clase de la que hereda se llama clase padre (base). Al usar la herencia, puedes crear jerarquías de clases complejas que pueden ayudarte a organizar tu código y hacerlo más modular y reutilizable.
Ejemplo de Herencia: Considera una clase base llamada Animal
que representa un animal genérico, con atributos como name
, age
y un método llamado speak()
:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print(f"{self.name} makes a noise")
Ahora, queremos crear una clase Dog
que represente a un perro, que es un tipo específico de animal. En lugar de redefinir todos los atributos y métodos de la clase Animal
, podemos heredarlos usando la herencia:
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age)
self.breed = breed
def speak(self):
print(f"{self.name} barks")
En este ejemplo, definimos la clase Dog
y heredamos de la clase Animal. Usamos la función super() para llamar al método __init__
de la clase padre para inicializar los atributos name
y age
. También agregamos un nuevo atributo, breed
, específico de la clase Dog
.
También sobrescribimos el método speak()
de la clase Animal para adaptarlo mejor al comportamiento de un perro. Esto se llama sobrecarga de métodos y es una práctica común cuando se usa la herencia.
Ahora, cuando creamos un objeto Perro, tendrá acceso tanto a los atributos y métodos de la clase Animal, como a cualquier nuevo atributo o método que definamos en la clase Perro.
dog1 = Dog("Max", 3, "Labrador")
print(dog1.name) # Output: Max
print(dog1.age) # Output: 3
print(dog1.breed) # Output: Labrador
dog1.speak() # Output: Max barks
La herencia es un concepto fundamental en la programación orientada a objetos que permite la creación de clases más especializadas. Facilita la reutilización de código de clases más generales al mismo tiempo que brinda la capacidad de personalizar o extender su comportamiento según sea necesario. Esto convierte a la herencia en una herramienta poderosa para que los desarrolladores creen soluciones de software eficientes y efectivas.
Al utilizar la herencia, los desarrolladores pueden crear una jerarquía de clases que comparten características comunes, lo que permite la implementación de sistemas complejos. Además, la herencia ayuda a reducir la complejidad del código, haciéndolo más fácil de mantener y actualizar con el tiempo. Por lo tanto, es esencial tener una sólida comprensión de la herencia al desarrollar aplicaciones de software, ya que puede mejorar enormemente la calidad general del código y hacerlo más escalable.
Ejercicio 8.3.1: Herencia Simple
Implementa una jerarquía de clases para diferentes tipos de vehículos, utilizando herencia.
Instrucciones:
- Crea una clase base llamada Vehicle con los atributos make, model y year.
- Define un método vehicle_info() que imprima la marca, modelo y año del vehículo.
- Crea una clase Car que herede de Vehicle y tenga un atributo adicional doors.
- Crea una clase Motorcycle que herede de Vehicle y tenga un atributo adicional type.
- Crea instancias de Car y Motorcycle, y llama al método vehicle_info() para cada una.
Solución:
class Vehicle:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def vehicle_info(self):
print(f"{self.year} {self.make} {self.model}")
class Car(Vehicle):
def __init__(self, make, model, year, doors):
super().__init__(make, model, year)
self.doors = doors
class Motorcycle(Vehicle):
def __init__(self, make, model, year, bike_type):
super().__init__(make, model, year)
self.type = bike_type
car1 = Car("Toyota", "Camry", 2020, 4)
car1.vehicle_info() # Output: 2020 Toyota Camry
motorcycle1 = Motorcycle("Yamaha", "R1", 2019, "Sport")
motorcycle1.vehicle_info() # Output: 2019 Yamaha R1
Ejercicio 8.3.2: Herencia y Sobrecarga de Métodos
Crea clases que representen diferentes tipos de cuentas bancarias, utilizando herencia y sobrecarga de métodos.
Instrucciones:
- Crea una clase base llamada BankAccount con los atributos balance y un método deposit().
- Define un método withdraw() que compruebe si la cantidad a retirar es menor o igual al saldo, y actualice el saldo en consecuencia.
- Crea una clase SavingsAccount que herede de BankAccount y tenga un atributo adicional interest_rate.
- Sobrecarga el método withdraw() en SavingsAccount para incluir una tarifa de retiro del 1% de la cantidad retirada.
- Crea instancias de BankAccount y SavingsAccount, y prueba los métodos deposit() y withdraw().
Solución:
class BankAccount:
def __init__(self):
self.balance = 0
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
else:
print("Insufficient funds")
class SavingsAccount(BankAccount):
def __init__(self, interest_rate):
super().__init__()
self.interest_rate = interest_rate
def withdraw(self, amount):
fee = amount * 0.01
if amount + fee <= self.balance:
self.balance -= (amount + fee)
else:
print("Insufficient funds")
account1 = BankAccount()
account1.deposit(100)
account1.withdraw(50)
print(account1.balance) # Output: 50
savings1 = SavingsAccount(0.02)
savings1.deposit(100)
savings1.withdraw(50)
print(savings1.balance) # Output: 49.5
Ejercicio 8.3.3: Herencia Múltiple
Implementa una jerarquía de clases utilizando herencia múltiple.
Instrucciones:
- Crea una clase Person con los atributos first_name y last_name.
- Crea una clase Employee que herede de Person y tenga un atributo adicional employee_id.
- Crea una clase Student que herede de Person y tenga un atributo adicional student_id.
- Crea una clase TeachingAssistant que herede de ambas, Employee y Student.
- Define un método get_info() para cada clase que devuelva una cadena con la información de la persona.
- Crea una instancia de TeachingAssistant y llama al método get_info().
Solución:
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def get_info(self):
return f"{self.first_name} {self.last_name}"
class Employee(Person):
def __init__(self, first_name, last_name, employee_id):
super().__init__(first_name, last_name)
self.employee_id = employee_id
def get_info(self):
return f"{super().get_info()}, Employee ID: {self.employee_id}"
class Student(Person):
def __init__(self, first_name, last_name, student_id):
super().__init__(first_name, last_name)
self.student_id = student_id
def get_info(self):
return f"{super().get_info()}, Student ID: {self.student_id}"
class TeachingAssistant(Employee, Student):
def __init__(self, first_name, last_name, employee_id, student_id):
Employee.__init__(self, first_name, last_name, employee_id)
Student.__init__(self, first_name, last_name, student_id)
def get_info(self):
return f"{super().get_info()}, Employee ID: {self.employee_id}, Student ID: {self.student_id}"
ta1 = TeachingAssistant("John", "Doe", 1001, 2001)
print(ta1.get_info()) # Output: John Doe, Employee ID: 1001, Student ID: 2001
8.3: Herencia
Herencia es una de las características más poderosas de la programación orientada a objetos, y puede mejorar enormemente la eficiencia de tu código. Al permitir que una clase herede los atributos y métodos de otra clase, puedes crear nuevas clases que se basan en clases existentes, lo que puede ahorrar mucho tiempo y esfuerzo.
Ejemplo: Imagina que tienes una clase que representa un automóvil, con atributos como marca, modelo y año, y métodos como encender el motor y acelerar. Ahora, supongamos que deseas crear una nueva clase para un tipo específico de automóvil, como un automóvil deportivo. En lugar de comenzar desde cero y definir todos los atributos y métodos para el automóvil deportivo, puedes simplemente crear una nueva clase que herede de la clase de automóvil y luego agregar o modificar los atributos y métodos según sea necesario. De esta manera, puedes reutilizar gran parte del código que ya escribiste para la clase de automóvil y solo enfocarte en los cambios que son específicos del automóvil deportivo.
Implementación en Python: En Python, la herencia se implementa definiendo una nueva clase que toma la clase padre (base) como argumento. La nueva clase se llama clase hija (derivada) y la clase de la que hereda se llama clase padre (base). Al usar la herencia, puedes crear jerarquías de clases complejas que pueden ayudarte a organizar tu código y hacerlo más modular y reutilizable.
Ejemplo de Herencia: Considera una clase base llamada Animal
que representa un animal genérico, con atributos como name
, age
y un método llamado speak()
:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print(f"{self.name} makes a noise")
Ahora, queremos crear una clase Dog
que represente a un perro, que es un tipo específico de animal. En lugar de redefinir todos los atributos y métodos de la clase Animal
, podemos heredarlos usando la herencia:
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age)
self.breed = breed
def speak(self):
print(f"{self.name} barks")
En este ejemplo, definimos la clase Dog
y heredamos de la clase Animal. Usamos la función super() para llamar al método __init__
de la clase padre para inicializar los atributos name
y age
. También agregamos un nuevo atributo, breed
, específico de la clase Dog
.
También sobrescribimos el método speak()
de la clase Animal para adaptarlo mejor al comportamiento de un perro. Esto se llama sobrecarga de métodos y es una práctica común cuando se usa la herencia.
Ahora, cuando creamos un objeto Perro, tendrá acceso tanto a los atributos y métodos de la clase Animal, como a cualquier nuevo atributo o método que definamos en la clase Perro.
dog1 = Dog("Max", 3, "Labrador")
print(dog1.name) # Output: Max
print(dog1.age) # Output: 3
print(dog1.breed) # Output: Labrador
dog1.speak() # Output: Max barks
La herencia es un concepto fundamental en la programación orientada a objetos que permite la creación de clases más especializadas. Facilita la reutilización de código de clases más generales al mismo tiempo que brinda la capacidad de personalizar o extender su comportamiento según sea necesario. Esto convierte a la herencia en una herramienta poderosa para que los desarrolladores creen soluciones de software eficientes y efectivas.
Al utilizar la herencia, los desarrolladores pueden crear una jerarquía de clases que comparten características comunes, lo que permite la implementación de sistemas complejos. Además, la herencia ayuda a reducir la complejidad del código, haciéndolo más fácil de mantener y actualizar con el tiempo. Por lo tanto, es esencial tener una sólida comprensión de la herencia al desarrollar aplicaciones de software, ya que puede mejorar enormemente la calidad general del código y hacerlo más escalable.
Ejercicio 8.3.1: Herencia Simple
Implementa una jerarquía de clases para diferentes tipos de vehículos, utilizando herencia.
Instrucciones:
- Crea una clase base llamada Vehicle con los atributos make, model y year.
- Define un método vehicle_info() que imprima la marca, modelo y año del vehículo.
- Crea una clase Car que herede de Vehicle y tenga un atributo adicional doors.
- Crea una clase Motorcycle que herede de Vehicle y tenga un atributo adicional type.
- Crea instancias de Car y Motorcycle, y llama al método vehicle_info() para cada una.
Solución:
class Vehicle:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def vehicle_info(self):
print(f"{self.year} {self.make} {self.model}")
class Car(Vehicle):
def __init__(self, make, model, year, doors):
super().__init__(make, model, year)
self.doors = doors
class Motorcycle(Vehicle):
def __init__(self, make, model, year, bike_type):
super().__init__(make, model, year)
self.type = bike_type
car1 = Car("Toyota", "Camry", 2020, 4)
car1.vehicle_info() # Output: 2020 Toyota Camry
motorcycle1 = Motorcycle("Yamaha", "R1", 2019, "Sport")
motorcycle1.vehicle_info() # Output: 2019 Yamaha R1
Ejercicio 8.3.2: Herencia y Sobrecarga de Métodos
Crea clases que representen diferentes tipos de cuentas bancarias, utilizando herencia y sobrecarga de métodos.
Instrucciones:
- Crea una clase base llamada BankAccount con los atributos balance y un método deposit().
- Define un método withdraw() que compruebe si la cantidad a retirar es menor o igual al saldo, y actualice el saldo en consecuencia.
- Crea una clase SavingsAccount que herede de BankAccount y tenga un atributo adicional interest_rate.
- Sobrecarga el método withdraw() en SavingsAccount para incluir una tarifa de retiro del 1% de la cantidad retirada.
- Crea instancias de BankAccount y SavingsAccount, y prueba los métodos deposit() y withdraw().
Solución:
class BankAccount:
def __init__(self):
self.balance = 0
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
else:
print("Insufficient funds")
class SavingsAccount(BankAccount):
def __init__(self, interest_rate):
super().__init__()
self.interest_rate = interest_rate
def withdraw(self, amount):
fee = amount * 0.01
if amount + fee <= self.balance:
self.balance -= (amount + fee)
else:
print("Insufficient funds")
account1 = BankAccount()
account1.deposit(100)
account1.withdraw(50)
print(account1.balance) # Output: 50
savings1 = SavingsAccount(0.02)
savings1.deposit(100)
savings1.withdraw(50)
print(savings1.balance) # Output: 49.5
Ejercicio 8.3.3: Herencia Múltiple
Implementa una jerarquía de clases utilizando herencia múltiple.
Instrucciones:
- Crea una clase Person con los atributos first_name y last_name.
- Crea una clase Employee que herede de Person y tenga un atributo adicional employee_id.
- Crea una clase Student que herede de Person y tenga un atributo adicional student_id.
- Crea una clase TeachingAssistant que herede de ambas, Employee y Student.
- Define un método get_info() para cada clase que devuelva una cadena con la información de la persona.
- Crea una instancia de TeachingAssistant y llama al método get_info().
Solución:
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def get_info(self):
return f"{self.first_name} {self.last_name}"
class Employee(Person):
def __init__(self, first_name, last_name, employee_id):
super().__init__(first_name, last_name)
self.employee_id = employee_id
def get_info(self):
return f"{super().get_info()}, Employee ID: {self.employee_id}"
class Student(Person):
def __init__(self, first_name, last_name, student_id):
super().__init__(first_name, last_name)
self.student_id = student_id
def get_info(self):
return f"{super().get_info()}, Student ID: {self.student_id}"
class TeachingAssistant(Employee, Student):
def __init__(self, first_name, last_name, employee_id, student_id):
Employee.__init__(self, first_name, last_name, employee_id)
Student.__init__(self, first_name, last_name, student_id)
def get_info(self):
return f"{super().get_info()}, Employee ID: {self.employee_id}, Student ID: {self.student_id}"
ta1 = TeachingAssistant("John", "Doe", 1001, 2001)
print(ta1.get_info()) # Output: John Doe, Employee ID: 1001, Student ID: 2001
8.3: Herencia
Herencia es una de las características más poderosas de la programación orientada a objetos, y puede mejorar enormemente la eficiencia de tu código. Al permitir que una clase herede los atributos y métodos de otra clase, puedes crear nuevas clases que se basan en clases existentes, lo que puede ahorrar mucho tiempo y esfuerzo.
Ejemplo: Imagina que tienes una clase que representa un automóvil, con atributos como marca, modelo y año, y métodos como encender el motor y acelerar. Ahora, supongamos que deseas crear una nueva clase para un tipo específico de automóvil, como un automóvil deportivo. En lugar de comenzar desde cero y definir todos los atributos y métodos para el automóvil deportivo, puedes simplemente crear una nueva clase que herede de la clase de automóvil y luego agregar o modificar los atributos y métodos según sea necesario. De esta manera, puedes reutilizar gran parte del código que ya escribiste para la clase de automóvil y solo enfocarte en los cambios que son específicos del automóvil deportivo.
Implementación en Python: En Python, la herencia se implementa definiendo una nueva clase que toma la clase padre (base) como argumento. La nueva clase se llama clase hija (derivada) y la clase de la que hereda se llama clase padre (base). Al usar la herencia, puedes crear jerarquías de clases complejas que pueden ayudarte a organizar tu código y hacerlo más modular y reutilizable.
Ejemplo de Herencia: Considera una clase base llamada Animal
que representa un animal genérico, con atributos como name
, age
y un método llamado speak()
:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print(f"{self.name} makes a noise")
Ahora, queremos crear una clase Dog
que represente a un perro, que es un tipo específico de animal. En lugar de redefinir todos los atributos y métodos de la clase Animal
, podemos heredarlos usando la herencia:
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age)
self.breed = breed
def speak(self):
print(f"{self.name} barks")
En este ejemplo, definimos la clase Dog
y heredamos de la clase Animal. Usamos la función super() para llamar al método __init__
de la clase padre para inicializar los atributos name
y age
. También agregamos un nuevo atributo, breed
, específico de la clase Dog
.
También sobrescribimos el método speak()
de la clase Animal para adaptarlo mejor al comportamiento de un perro. Esto se llama sobrecarga de métodos y es una práctica común cuando se usa la herencia.
Ahora, cuando creamos un objeto Perro, tendrá acceso tanto a los atributos y métodos de la clase Animal, como a cualquier nuevo atributo o método que definamos en la clase Perro.
dog1 = Dog("Max", 3, "Labrador")
print(dog1.name) # Output: Max
print(dog1.age) # Output: 3
print(dog1.breed) # Output: Labrador
dog1.speak() # Output: Max barks
La herencia es un concepto fundamental en la programación orientada a objetos que permite la creación de clases más especializadas. Facilita la reutilización de código de clases más generales al mismo tiempo que brinda la capacidad de personalizar o extender su comportamiento según sea necesario. Esto convierte a la herencia en una herramienta poderosa para que los desarrolladores creen soluciones de software eficientes y efectivas.
Al utilizar la herencia, los desarrolladores pueden crear una jerarquía de clases que comparten características comunes, lo que permite la implementación de sistemas complejos. Además, la herencia ayuda a reducir la complejidad del código, haciéndolo más fácil de mantener y actualizar con el tiempo. Por lo tanto, es esencial tener una sólida comprensión de la herencia al desarrollar aplicaciones de software, ya que puede mejorar enormemente la calidad general del código y hacerlo más escalable.
Ejercicio 8.3.1: Herencia Simple
Implementa una jerarquía de clases para diferentes tipos de vehículos, utilizando herencia.
Instrucciones:
- Crea una clase base llamada Vehicle con los atributos make, model y year.
- Define un método vehicle_info() que imprima la marca, modelo y año del vehículo.
- Crea una clase Car que herede de Vehicle y tenga un atributo adicional doors.
- Crea una clase Motorcycle que herede de Vehicle y tenga un atributo adicional type.
- Crea instancias de Car y Motorcycle, y llama al método vehicle_info() para cada una.
Solución:
class Vehicle:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def vehicle_info(self):
print(f"{self.year} {self.make} {self.model}")
class Car(Vehicle):
def __init__(self, make, model, year, doors):
super().__init__(make, model, year)
self.doors = doors
class Motorcycle(Vehicle):
def __init__(self, make, model, year, bike_type):
super().__init__(make, model, year)
self.type = bike_type
car1 = Car("Toyota", "Camry", 2020, 4)
car1.vehicle_info() # Output: 2020 Toyota Camry
motorcycle1 = Motorcycle("Yamaha", "R1", 2019, "Sport")
motorcycle1.vehicle_info() # Output: 2019 Yamaha R1
Ejercicio 8.3.2: Herencia y Sobrecarga de Métodos
Crea clases que representen diferentes tipos de cuentas bancarias, utilizando herencia y sobrecarga de métodos.
Instrucciones:
- Crea una clase base llamada BankAccount con los atributos balance y un método deposit().
- Define un método withdraw() que compruebe si la cantidad a retirar es menor o igual al saldo, y actualice el saldo en consecuencia.
- Crea una clase SavingsAccount que herede de BankAccount y tenga un atributo adicional interest_rate.
- Sobrecarga el método withdraw() en SavingsAccount para incluir una tarifa de retiro del 1% de la cantidad retirada.
- Crea instancias de BankAccount y SavingsAccount, y prueba los métodos deposit() y withdraw().
Solución:
class BankAccount:
def __init__(self):
self.balance = 0
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
else:
print("Insufficient funds")
class SavingsAccount(BankAccount):
def __init__(self, interest_rate):
super().__init__()
self.interest_rate = interest_rate
def withdraw(self, amount):
fee = amount * 0.01
if amount + fee <= self.balance:
self.balance -= (amount + fee)
else:
print("Insufficient funds")
account1 = BankAccount()
account1.deposit(100)
account1.withdraw(50)
print(account1.balance) # Output: 50
savings1 = SavingsAccount(0.02)
savings1.deposit(100)
savings1.withdraw(50)
print(savings1.balance) # Output: 49.5
Ejercicio 8.3.3: Herencia Múltiple
Implementa una jerarquía de clases utilizando herencia múltiple.
Instrucciones:
- Crea una clase Person con los atributos first_name y last_name.
- Crea una clase Employee que herede de Person y tenga un atributo adicional employee_id.
- Crea una clase Student que herede de Person y tenga un atributo adicional student_id.
- Crea una clase TeachingAssistant que herede de ambas, Employee y Student.
- Define un método get_info() para cada clase que devuelva una cadena con la información de la persona.
- Crea una instancia de TeachingAssistant y llama al método get_info().
Solución:
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def get_info(self):
return f"{self.first_name} {self.last_name}"
class Employee(Person):
def __init__(self, first_name, last_name, employee_id):
super().__init__(first_name, last_name)
self.employee_id = employee_id
def get_info(self):
return f"{super().get_info()}, Employee ID: {self.employee_id}"
class Student(Person):
def __init__(self, first_name, last_name, student_id):
super().__init__(first_name, last_name)
self.student_id = student_id
def get_info(self):
return f"{super().get_info()}, Student ID: {self.student_id}"
class TeachingAssistant(Employee, Student):
def __init__(self, first_name, last_name, employee_id, student_id):
Employee.__init__(self, first_name, last_name, employee_id)
Student.__init__(self, first_name, last_name, student_id)
def get_info(self):
return f"{super().get_info()}, Employee ID: {self.employee_id}, Student ID: {self.student_id}"
ta1 = TeachingAssistant("John", "Doe", 1001, 2001)
print(ta1.get_info()) # Output: John Doe, Employee ID: 1001, Student ID: 2001