Menu iconMenu icon
Python Programming Unlocked for Beginners

Chapter 8: Object-Oriented Programming

8.5: Encapsulación

La encapsulación es uno de los principios fundamentales de la programación orientada a objetos. Es el proceso de combinar datos (atributos) y métodos que operan sobre esos datos dentro de una sola unidad, generalmente una clase, para crear un sistema cohesivo y bien organizado.

Al usar la encapsulación, podemos limitar el acceso a ciertas partes del objeto, lo que puede ayudar a prevenir la interferencia o modificación no deseada del estado interno del objeto. Esto puede ser especialmente útil en sistemas grandes y complejos, donde mantener un registro del estado de diferentes objetos puede resultar difícil. Además, la encapsulación puede facilitar la modificación y actualización del código, ya que los cambios en una parte del código tendrán menos impacto en el resto del sistema. En general, la encapsulación es una técnica poderosa que puede ayudar a crear código más robusto y mantenible.

En Python, la encapsulación se logra utilizando modificadores de acceso privados y protegidos para atributos y métodos. Si bien no existe un concepto estricto de miembros privados o protegidos, se siguen ciertas convenciones para indicar el nivel de acceso previsto:

8.5.1: Miembros públicos:

De forma predeterminada, todos los miembros de una clase son públicos, lo que significa que se puede acceder a ellos desde cualquier lugar dentro y fuera de la clase. Sin embargo, es importante tener en cuenta que hacer que todos los miembros sean públicos puede conducir a posibles problemas de seguridad, ya que la información sensible puede ser accedida o modificada por usuarios no autorizados. Para mitigar este riesgo, se recomienda utilizar modificadores de acceso como privado o protegido para miembros sensibles, y solo proporcionar acceso público a los miembros necesarios. Además, el uso de técnicas de encapsulación como getters y setters puede ayudar a garantizar que los datos se accedan y modifiquen de manera controlada y segura.

8.5.2: Miembros protegidos:

Si un miembro está destinado a ser accedido solo desde dentro de la clase y sus subclases, su nombre debe prefijarse con un guion bajo único (_). Esto se conoce como una convención y se usa ampliamente en Python. Sin embargo, es importante tener en cuenta que esta convención no impide realmente el acceso al miembro desde fuera de la clase o sus subclases. En tales casos, se recomienda usar la técnica de "name mangling" (alteración de nombres), que agrega un prefijo al nombre del miembro para dificultar el acceso desde fuera de la clase. El name mangling se logra prefijando el nombre del miembro con dos guiones bajos (__) y un sufijo de uno o más guiones bajos. Por ejemplo, un miembro llamado "my_var" se convertiría en "_MyClass__my_var" en la clase llamada "MyClass". Tenga en cuenta que esta técnica debe usarse con precaución, ya que puede dificultar la lectura y el mantenimiento del código.

8.5.3: Miembros privados:

Si un miembro está destinado a ser accedido solo dentro de la clase (ni siquiera por subclases), su nombre debe comenzar con dos guiones bajos (__). Python proporciona una forma limitada de privacidad mediante la alteración de nombres (name mangling), lo que dificulta, pero no imposibilita, el acceso al miembro desde fuera de la clase.

Es importante comprender que la alteración de nombres no es una forma de seguridad. Es simplemente una convención utilizada para desalentar el acceso accidental a miembros privados. De hecho, la alteración de nombres se puede eludir fácilmente accediendo al miembro usando su nombre modificado.

Además de los miembros privados, Python también permite miembros protegidos, que pueden ser accedidos por subclases pero no desde fuera de la clase. Estos miembros se marcan con un único guion bajo (_).

Cabe señalar que el uso de miembros privados y protegidos no es necesario en todos los casos. En muchas situaciones, es perfectamente aceptable hacer que todos los miembros sean públicos. Sin embargo, en proyectos más grandes o con múltiples desarrolladores, el uso de miembros privados y protegidos puede ayudar a prevenir modificaciones no deseadas en partes críticas del código.

Ejemplo:

class BankAccount:
    def __init__(self, account_number, balance):
        self._account_number = account_number
        self.__balance = balance

    def deposit(self, amount):
        self.__balance += amount

    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient funds")

    def get_balance(self):
        return self.__balance

account = BankAccount("12345", 1000)
account.deposit(500)
account.withdraw(300)

print(account.get_balance())  # Output: 1200

En este ejemplo, la clase BankAccount tiene un atributo __balance que está pensado como privado. Se han proporcionado métodos como deposit(), withdraw() y get_balance() para manipular el saldo, evitando así el acceso directo al atributo. Observe que _account_number es un miembro protegido, lo cual no es estrictamente forzado por Python, pero indica que debería tratarse como protegido por convención.

Ejercicio 8.5.1: Crear una clase simple de Empleado

En este ejercicio, crearás una clase simple de Empleado que utiliza la encapsulación para proteger sus miembros de datos.

Instrucciones:

  1. Crea una clase llamada Empleado.
  2. Define los siguientes atributos privados: __nombre, __apellido y __salario.
  3. Crea un constructor que tome nombre, apellido y salario como parámetros e inicialice los atributos privados.
  4. Crea métodos públicos get_nombre(), get_apellido() y get_salario() que devuelvan los atributos respectivos.
  5. Crea un método get_nombre_completo() que devuelva el nombre completo del empleado, que es la combinación del nombre y el apellido.

Solución:

class Employee:
    def __init__(self, first_name, last_name, salary):
        self.__first_name = first_name
        self.__last_name = last_name
        self.__salary = salary

    def get_first_name(self):
        return self.__first_name

    def get_last_name(self):
        return self.__last_name

    def get_salary(self):
        return self.__salary

    def get_full_name(self):
        return self.__first_name + " " + self.__last_name

employee = Employee("John", "Doe", 50000)
print(employee.get_full_name())  # Output: John Doe

Ejercicio 8.5.2: Implementar una clase Círculo

En este ejercicio, crearás una clase Círculo que utiliza la encapsulación para proteger sus miembros de datos y proporcionar métodos para manipularlos.

Instrucciones:

  1. Crea una clase llamada Círculo.
  2. Define los siguientes atributos privados: __radio y __pi (usa el valor 3.14159 para pi).
  3. Crea un constructor que tome el radio como parámetro e inicialice el atributo privado __radio.
  4. Crea métodos públicos get_radio() y get_pi() que devuelvan los atributos respectivos.
  5. Crea métodos calculate_area() y calculate_circumference() que devuelvan el área y la circunferencia del círculo, respectivamente.

Solución:

class Circle:
    def __init__(self, radius):
        self.__radius = radius
        self.__pi = 3.14159

    def get_radius(self):
        return self.__radius

    def get_pi(self):
        return self.__pi

    def calculate_area(self):
        return self.__pi * self.__radius ** 2

    def calculate_circumference(self):
        return 2 * self.__pi * self.__radius

circle = Circle(5)
print(circle.calculate_area())         # Output: 78.53975
print(circle.calculate_circumference())  # Output: 31.4159

Ejercicio 8.5.3: Creación de una cuenta protegida con contraseña

En este ejercicio, crearás una clase Cuenta que utiliza la encapsulación para proteger sus miembros de datos y requiere una contraseña para acceder a ciertos métodos.

Instrucciones:

  1. Crea una clase llamada Cuenta.
  2. Define los siguientes atributos privados: __numero_cuenta, __saldo y __contraseña.
  3. Crea un constructor que tome numero_cuenta, saldo y contraseña como parámetros e inicialice los atributos privados.
  4. Crea métodos públicos get_numero_cuenta() y get_saldo() que devuelvan los atributos respectivos.
  5. Crea un método validar_contraseña(self, contraseña) que devuelva True si la contraseña dada coincide con la contraseña de la cuenta, y False de lo contrario.
  6. Crea un método retirar(self, monto, contraseña) que verifique si la contraseña es correcta usando validar_contraseña(), y si es así, resta el monto dado del saldo.

Solución:

class Account:
    def __init__(self, account_number, balance, password):
        self.__account_number = account_number
        self.__balance = balance
        self.__password = password

    def get_account_number(self):
        return self.__account_number

    def get_balance(self):
        return self.__balance

    def validate_password(self, password):
        return self.__password == password

    def withdraw(self, amount, password):
        if self.validate_password(password):
            self.__balance -= amount
            return True
        return False

account = Account("123456", 1000, "secret123")
print(account.get_account_number())  # Output: 123456
print(account.get_balance())         # Output: 1000

if account.withdraw(200, "secret123"):
    print("Withdrawal successful!")
    print("New balance:", account.get_balance())  # Output: 800
else:
    print("Incorrect password or insufficient funds.")

En este ejercicio, has creado una clase Cuenta que demuestra el concepto de encapsulación al proteger sus miembros de datos y requerir una contraseña para ciertas operaciones.

Al concluir el capítulo 8, es importante reconocer la importancia de la programación orientada a objetos (OOP) en Python. A través de la OOP, podemos crear código más organizado, mantenible y escalable. En este capítulo, hemos explorado los conceptos clave de la OOP:

  1. Clases y objetos: Los fundamentos para crear tipos de datos personalizados e instancias en Python.
  2. Atributos y métodos: Cómo almacenar datos y definir comportamientos dentro de las clases.
  3. Herencia: Una forma de crear nuevas clases a partir de clases existentes, promoviendo la reutilización del código.
  4. Polimorfismo: Aprovechar el poder de la herencia y la anulación de métodos para crear código flexible que pueda manejar diferentes tipos de objetos.
  5. Encapsulación: Proteger el estado interno y la implementación de una clase, proporcionando una interfaz bien definida para interactuar con ella.

Al aplicar estos principios en tus programas Python, puedes crear código que sea más fácil de entender, depurar y ampliar. No olvides practicar la implementación de estos conceptos a través de ejercicios y proyectos del mundo real para obtener una comprensión más profunda de la OOP en Python.

A medida que avanzas, recuerda que Python es un lenguaje versátil que admite múltiples paradigmas de programación. La combinación de la OOP con otros enfoques, como la programación funcional, puede ayudarte a refinar y adaptar aún más tu código a diversas situaciones y requisitos. ¡Nos vemos en el próximo capítulo. Feliz programación!

8.5: Encapsulación

La encapsulación es uno de los principios fundamentales de la programación orientada a objetos. Es el proceso de combinar datos (atributos) y métodos que operan sobre esos datos dentro de una sola unidad, generalmente una clase, para crear un sistema cohesivo y bien organizado.

Al usar la encapsulación, podemos limitar el acceso a ciertas partes del objeto, lo que puede ayudar a prevenir la interferencia o modificación no deseada del estado interno del objeto. Esto puede ser especialmente útil en sistemas grandes y complejos, donde mantener un registro del estado de diferentes objetos puede resultar difícil. Además, la encapsulación puede facilitar la modificación y actualización del código, ya que los cambios en una parte del código tendrán menos impacto en el resto del sistema. En general, la encapsulación es una técnica poderosa que puede ayudar a crear código más robusto y mantenible.

En Python, la encapsulación se logra utilizando modificadores de acceso privados y protegidos para atributos y métodos. Si bien no existe un concepto estricto de miembros privados o protegidos, se siguen ciertas convenciones para indicar el nivel de acceso previsto:

8.5.1: Miembros públicos:

De forma predeterminada, todos los miembros de una clase son públicos, lo que significa que se puede acceder a ellos desde cualquier lugar dentro y fuera de la clase. Sin embargo, es importante tener en cuenta que hacer que todos los miembros sean públicos puede conducir a posibles problemas de seguridad, ya que la información sensible puede ser accedida o modificada por usuarios no autorizados. Para mitigar este riesgo, se recomienda utilizar modificadores de acceso como privado o protegido para miembros sensibles, y solo proporcionar acceso público a los miembros necesarios. Además, el uso de técnicas de encapsulación como getters y setters puede ayudar a garantizar que los datos se accedan y modifiquen de manera controlada y segura.

8.5.2: Miembros protegidos:

Si un miembro está destinado a ser accedido solo desde dentro de la clase y sus subclases, su nombre debe prefijarse con un guion bajo único (_). Esto se conoce como una convención y se usa ampliamente en Python. Sin embargo, es importante tener en cuenta que esta convención no impide realmente el acceso al miembro desde fuera de la clase o sus subclases. En tales casos, se recomienda usar la técnica de "name mangling" (alteración de nombres), que agrega un prefijo al nombre del miembro para dificultar el acceso desde fuera de la clase. El name mangling se logra prefijando el nombre del miembro con dos guiones bajos (__) y un sufijo de uno o más guiones bajos. Por ejemplo, un miembro llamado "my_var" se convertiría en "_MyClass__my_var" en la clase llamada "MyClass". Tenga en cuenta que esta técnica debe usarse con precaución, ya que puede dificultar la lectura y el mantenimiento del código.

8.5.3: Miembros privados:

Si un miembro está destinado a ser accedido solo dentro de la clase (ni siquiera por subclases), su nombre debe comenzar con dos guiones bajos (__). Python proporciona una forma limitada de privacidad mediante la alteración de nombres (name mangling), lo que dificulta, pero no imposibilita, el acceso al miembro desde fuera de la clase.

Es importante comprender que la alteración de nombres no es una forma de seguridad. Es simplemente una convención utilizada para desalentar el acceso accidental a miembros privados. De hecho, la alteración de nombres se puede eludir fácilmente accediendo al miembro usando su nombre modificado.

Además de los miembros privados, Python también permite miembros protegidos, que pueden ser accedidos por subclases pero no desde fuera de la clase. Estos miembros se marcan con un único guion bajo (_).

Cabe señalar que el uso de miembros privados y protegidos no es necesario en todos los casos. En muchas situaciones, es perfectamente aceptable hacer que todos los miembros sean públicos. Sin embargo, en proyectos más grandes o con múltiples desarrolladores, el uso de miembros privados y protegidos puede ayudar a prevenir modificaciones no deseadas en partes críticas del código.

Ejemplo:

class BankAccount:
    def __init__(self, account_number, balance):
        self._account_number = account_number
        self.__balance = balance

    def deposit(self, amount):
        self.__balance += amount

    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient funds")

    def get_balance(self):
        return self.__balance

account = BankAccount("12345", 1000)
account.deposit(500)
account.withdraw(300)

print(account.get_balance())  # Output: 1200

En este ejemplo, la clase BankAccount tiene un atributo __balance que está pensado como privado. Se han proporcionado métodos como deposit(), withdraw() y get_balance() para manipular el saldo, evitando así el acceso directo al atributo. Observe que _account_number es un miembro protegido, lo cual no es estrictamente forzado por Python, pero indica que debería tratarse como protegido por convención.

Ejercicio 8.5.1: Crear una clase simple de Empleado

En este ejercicio, crearás una clase simple de Empleado que utiliza la encapsulación para proteger sus miembros de datos.

Instrucciones:

  1. Crea una clase llamada Empleado.
  2. Define los siguientes atributos privados: __nombre, __apellido y __salario.
  3. Crea un constructor que tome nombre, apellido y salario como parámetros e inicialice los atributos privados.
  4. Crea métodos públicos get_nombre(), get_apellido() y get_salario() que devuelvan los atributos respectivos.
  5. Crea un método get_nombre_completo() que devuelva el nombre completo del empleado, que es la combinación del nombre y el apellido.

Solución:

class Employee:
    def __init__(self, first_name, last_name, salary):
        self.__first_name = first_name
        self.__last_name = last_name
        self.__salary = salary

    def get_first_name(self):
        return self.__first_name

    def get_last_name(self):
        return self.__last_name

    def get_salary(self):
        return self.__salary

    def get_full_name(self):
        return self.__first_name + " " + self.__last_name

employee = Employee("John", "Doe", 50000)
print(employee.get_full_name())  # Output: John Doe

Ejercicio 8.5.2: Implementar una clase Círculo

En este ejercicio, crearás una clase Círculo que utiliza la encapsulación para proteger sus miembros de datos y proporcionar métodos para manipularlos.

Instrucciones:

  1. Crea una clase llamada Círculo.
  2. Define los siguientes atributos privados: __radio y __pi (usa el valor 3.14159 para pi).
  3. Crea un constructor que tome el radio como parámetro e inicialice el atributo privado __radio.
  4. Crea métodos públicos get_radio() y get_pi() que devuelvan los atributos respectivos.
  5. Crea métodos calculate_area() y calculate_circumference() que devuelvan el área y la circunferencia del círculo, respectivamente.

Solución:

class Circle:
    def __init__(self, radius):
        self.__radius = radius
        self.__pi = 3.14159

    def get_radius(self):
        return self.__radius

    def get_pi(self):
        return self.__pi

    def calculate_area(self):
        return self.__pi * self.__radius ** 2

    def calculate_circumference(self):
        return 2 * self.__pi * self.__radius

circle = Circle(5)
print(circle.calculate_area())         # Output: 78.53975
print(circle.calculate_circumference())  # Output: 31.4159

Ejercicio 8.5.3: Creación de una cuenta protegida con contraseña

En este ejercicio, crearás una clase Cuenta que utiliza la encapsulación para proteger sus miembros de datos y requiere una contraseña para acceder a ciertos métodos.

Instrucciones:

  1. Crea una clase llamada Cuenta.
  2. Define los siguientes atributos privados: __numero_cuenta, __saldo y __contraseña.
  3. Crea un constructor que tome numero_cuenta, saldo y contraseña como parámetros e inicialice los atributos privados.
  4. Crea métodos públicos get_numero_cuenta() y get_saldo() que devuelvan los atributos respectivos.
  5. Crea un método validar_contraseña(self, contraseña) que devuelva True si la contraseña dada coincide con la contraseña de la cuenta, y False de lo contrario.
  6. Crea un método retirar(self, monto, contraseña) que verifique si la contraseña es correcta usando validar_contraseña(), y si es así, resta el monto dado del saldo.

Solución:

class Account:
    def __init__(self, account_number, balance, password):
        self.__account_number = account_number
        self.__balance = balance
        self.__password = password

    def get_account_number(self):
        return self.__account_number

    def get_balance(self):
        return self.__balance

    def validate_password(self, password):
        return self.__password == password

    def withdraw(self, amount, password):
        if self.validate_password(password):
            self.__balance -= amount
            return True
        return False

account = Account("123456", 1000, "secret123")
print(account.get_account_number())  # Output: 123456
print(account.get_balance())         # Output: 1000

if account.withdraw(200, "secret123"):
    print("Withdrawal successful!")
    print("New balance:", account.get_balance())  # Output: 800
else:
    print("Incorrect password or insufficient funds.")

En este ejercicio, has creado una clase Cuenta que demuestra el concepto de encapsulación al proteger sus miembros de datos y requerir una contraseña para ciertas operaciones.

Al concluir el capítulo 8, es importante reconocer la importancia de la programación orientada a objetos (OOP) en Python. A través de la OOP, podemos crear código más organizado, mantenible y escalable. En este capítulo, hemos explorado los conceptos clave de la OOP:

  1. Clases y objetos: Los fundamentos para crear tipos de datos personalizados e instancias en Python.
  2. Atributos y métodos: Cómo almacenar datos y definir comportamientos dentro de las clases.
  3. Herencia: Una forma de crear nuevas clases a partir de clases existentes, promoviendo la reutilización del código.
  4. Polimorfismo: Aprovechar el poder de la herencia y la anulación de métodos para crear código flexible que pueda manejar diferentes tipos de objetos.
  5. Encapsulación: Proteger el estado interno y la implementación de una clase, proporcionando una interfaz bien definida para interactuar con ella.

Al aplicar estos principios en tus programas Python, puedes crear código que sea más fácil de entender, depurar y ampliar. No olvides practicar la implementación de estos conceptos a través de ejercicios y proyectos del mundo real para obtener una comprensión más profunda de la OOP en Python.

A medida que avanzas, recuerda que Python es un lenguaje versátil que admite múltiples paradigmas de programación. La combinación de la OOP con otros enfoques, como la programación funcional, puede ayudarte a refinar y adaptar aún más tu código a diversas situaciones y requisitos. ¡Nos vemos en el próximo capítulo. Feliz programación!

8.5: Encapsulación

La encapsulación es uno de los principios fundamentales de la programación orientada a objetos. Es el proceso de combinar datos (atributos) y métodos que operan sobre esos datos dentro de una sola unidad, generalmente una clase, para crear un sistema cohesivo y bien organizado.

Al usar la encapsulación, podemos limitar el acceso a ciertas partes del objeto, lo que puede ayudar a prevenir la interferencia o modificación no deseada del estado interno del objeto. Esto puede ser especialmente útil en sistemas grandes y complejos, donde mantener un registro del estado de diferentes objetos puede resultar difícil. Además, la encapsulación puede facilitar la modificación y actualización del código, ya que los cambios en una parte del código tendrán menos impacto en el resto del sistema. En general, la encapsulación es una técnica poderosa que puede ayudar a crear código más robusto y mantenible.

En Python, la encapsulación se logra utilizando modificadores de acceso privados y protegidos para atributos y métodos. Si bien no existe un concepto estricto de miembros privados o protegidos, se siguen ciertas convenciones para indicar el nivel de acceso previsto:

8.5.1: Miembros públicos:

De forma predeterminada, todos los miembros de una clase son públicos, lo que significa que se puede acceder a ellos desde cualquier lugar dentro y fuera de la clase. Sin embargo, es importante tener en cuenta que hacer que todos los miembros sean públicos puede conducir a posibles problemas de seguridad, ya que la información sensible puede ser accedida o modificada por usuarios no autorizados. Para mitigar este riesgo, se recomienda utilizar modificadores de acceso como privado o protegido para miembros sensibles, y solo proporcionar acceso público a los miembros necesarios. Además, el uso de técnicas de encapsulación como getters y setters puede ayudar a garantizar que los datos se accedan y modifiquen de manera controlada y segura.

8.5.2: Miembros protegidos:

Si un miembro está destinado a ser accedido solo desde dentro de la clase y sus subclases, su nombre debe prefijarse con un guion bajo único (_). Esto se conoce como una convención y se usa ampliamente en Python. Sin embargo, es importante tener en cuenta que esta convención no impide realmente el acceso al miembro desde fuera de la clase o sus subclases. En tales casos, se recomienda usar la técnica de "name mangling" (alteración de nombres), que agrega un prefijo al nombre del miembro para dificultar el acceso desde fuera de la clase. El name mangling se logra prefijando el nombre del miembro con dos guiones bajos (__) y un sufijo de uno o más guiones bajos. Por ejemplo, un miembro llamado "my_var" se convertiría en "_MyClass__my_var" en la clase llamada "MyClass". Tenga en cuenta que esta técnica debe usarse con precaución, ya que puede dificultar la lectura y el mantenimiento del código.

8.5.3: Miembros privados:

Si un miembro está destinado a ser accedido solo dentro de la clase (ni siquiera por subclases), su nombre debe comenzar con dos guiones bajos (__). Python proporciona una forma limitada de privacidad mediante la alteración de nombres (name mangling), lo que dificulta, pero no imposibilita, el acceso al miembro desde fuera de la clase.

Es importante comprender que la alteración de nombres no es una forma de seguridad. Es simplemente una convención utilizada para desalentar el acceso accidental a miembros privados. De hecho, la alteración de nombres se puede eludir fácilmente accediendo al miembro usando su nombre modificado.

Además de los miembros privados, Python también permite miembros protegidos, que pueden ser accedidos por subclases pero no desde fuera de la clase. Estos miembros se marcan con un único guion bajo (_).

Cabe señalar que el uso de miembros privados y protegidos no es necesario en todos los casos. En muchas situaciones, es perfectamente aceptable hacer que todos los miembros sean públicos. Sin embargo, en proyectos más grandes o con múltiples desarrolladores, el uso de miembros privados y protegidos puede ayudar a prevenir modificaciones no deseadas en partes críticas del código.

Ejemplo:

class BankAccount:
    def __init__(self, account_number, balance):
        self._account_number = account_number
        self.__balance = balance

    def deposit(self, amount):
        self.__balance += amount

    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient funds")

    def get_balance(self):
        return self.__balance

account = BankAccount("12345", 1000)
account.deposit(500)
account.withdraw(300)

print(account.get_balance())  # Output: 1200

En este ejemplo, la clase BankAccount tiene un atributo __balance que está pensado como privado. Se han proporcionado métodos como deposit(), withdraw() y get_balance() para manipular el saldo, evitando así el acceso directo al atributo. Observe que _account_number es un miembro protegido, lo cual no es estrictamente forzado por Python, pero indica que debería tratarse como protegido por convención.

Ejercicio 8.5.1: Crear una clase simple de Empleado

En este ejercicio, crearás una clase simple de Empleado que utiliza la encapsulación para proteger sus miembros de datos.

Instrucciones:

  1. Crea una clase llamada Empleado.
  2. Define los siguientes atributos privados: __nombre, __apellido y __salario.
  3. Crea un constructor que tome nombre, apellido y salario como parámetros e inicialice los atributos privados.
  4. Crea métodos públicos get_nombre(), get_apellido() y get_salario() que devuelvan los atributos respectivos.
  5. Crea un método get_nombre_completo() que devuelva el nombre completo del empleado, que es la combinación del nombre y el apellido.

Solución:

class Employee:
    def __init__(self, first_name, last_name, salary):
        self.__first_name = first_name
        self.__last_name = last_name
        self.__salary = salary

    def get_first_name(self):
        return self.__first_name

    def get_last_name(self):
        return self.__last_name

    def get_salary(self):
        return self.__salary

    def get_full_name(self):
        return self.__first_name + " " + self.__last_name

employee = Employee("John", "Doe", 50000)
print(employee.get_full_name())  # Output: John Doe

Ejercicio 8.5.2: Implementar una clase Círculo

En este ejercicio, crearás una clase Círculo que utiliza la encapsulación para proteger sus miembros de datos y proporcionar métodos para manipularlos.

Instrucciones:

  1. Crea una clase llamada Círculo.
  2. Define los siguientes atributos privados: __radio y __pi (usa el valor 3.14159 para pi).
  3. Crea un constructor que tome el radio como parámetro e inicialice el atributo privado __radio.
  4. Crea métodos públicos get_radio() y get_pi() que devuelvan los atributos respectivos.
  5. Crea métodos calculate_area() y calculate_circumference() que devuelvan el área y la circunferencia del círculo, respectivamente.

Solución:

class Circle:
    def __init__(self, radius):
        self.__radius = radius
        self.__pi = 3.14159

    def get_radius(self):
        return self.__radius

    def get_pi(self):
        return self.__pi

    def calculate_area(self):
        return self.__pi * self.__radius ** 2

    def calculate_circumference(self):
        return 2 * self.__pi * self.__radius

circle = Circle(5)
print(circle.calculate_area())         # Output: 78.53975
print(circle.calculate_circumference())  # Output: 31.4159

Ejercicio 8.5.3: Creación de una cuenta protegida con contraseña

En este ejercicio, crearás una clase Cuenta que utiliza la encapsulación para proteger sus miembros de datos y requiere una contraseña para acceder a ciertos métodos.

Instrucciones:

  1. Crea una clase llamada Cuenta.
  2. Define los siguientes atributos privados: __numero_cuenta, __saldo y __contraseña.
  3. Crea un constructor que tome numero_cuenta, saldo y contraseña como parámetros e inicialice los atributos privados.
  4. Crea métodos públicos get_numero_cuenta() y get_saldo() que devuelvan los atributos respectivos.
  5. Crea un método validar_contraseña(self, contraseña) que devuelva True si la contraseña dada coincide con la contraseña de la cuenta, y False de lo contrario.
  6. Crea un método retirar(self, monto, contraseña) que verifique si la contraseña es correcta usando validar_contraseña(), y si es así, resta el monto dado del saldo.

Solución:

class Account:
    def __init__(self, account_number, balance, password):
        self.__account_number = account_number
        self.__balance = balance
        self.__password = password

    def get_account_number(self):
        return self.__account_number

    def get_balance(self):
        return self.__balance

    def validate_password(self, password):
        return self.__password == password

    def withdraw(self, amount, password):
        if self.validate_password(password):
            self.__balance -= amount
            return True
        return False

account = Account("123456", 1000, "secret123")
print(account.get_account_number())  # Output: 123456
print(account.get_balance())         # Output: 1000

if account.withdraw(200, "secret123"):
    print("Withdrawal successful!")
    print("New balance:", account.get_balance())  # Output: 800
else:
    print("Incorrect password or insufficient funds.")

En este ejercicio, has creado una clase Cuenta que demuestra el concepto de encapsulación al proteger sus miembros de datos y requerir una contraseña para ciertas operaciones.

Al concluir el capítulo 8, es importante reconocer la importancia de la programación orientada a objetos (OOP) en Python. A través de la OOP, podemos crear código más organizado, mantenible y escalable. En este capítulo, hemos explorado los conceptos clave de la OOP:

  1. Clases y objetos: Los fundamentos para crear tipos de datos personalizados e instancias en Python.
  2. Atributos y métodos: Cómo almacenar datos y definir comportamientos dentro de las clases.
  3. Herencia: Una forma de crear nuevas clases a partir de clases existentes, promoviendo la reutilización del código.
  4. Polimorfismo: Aprovechar el poder de la herencia y la anulación de métodos para crear código flexible que pueda manejar diferentes tipos de objetos.
  5. Encapsulación: Proteger el estado interno y la implementación de una clase, proporcionando una interfaz bien definida para interactuar con ella.

Al aplicar estos principios en tus programas Python, puedes crear código que sea más fácil de entender, depurar y ampliar. No olvides practicar la implementación de estos conceptos a través de ejercicios y proyectos del mundo real para obtener una comprensión más profunda de la OOP en Python.

A medida que avanzas, recuerda que Python es un lenguaje versátil que admite múltiples paradigmas de programación. La combinación de la OOP con otros enfoques, como la programación funcional, puede ayudarte a refinar y adaptar aún más tu código a diversas situaciones y requisitos. ¡Nos vemos en el próximo capítulo. Feliz programación!

8.5: Encapsulación

La encapsulación es uno de los principios fundamentales de la programación orientada a objetos. Es el proceso de combinar datos (atributos) y métodos que operan sobre esos datos dentro de una sola unidad, generalmente una clase, para crear un sistema cohesivo y bien organizado.

Al usar la encapsulación, podemos limitar el acceso a ciertas partes del objeto, lo que puede ayudar a prevenir la interferencia o modificación no deseada del estado interno del objeto. Esto puede ser especialmente útil en sistemas grandes y complejos, donde mantener un registro del estado de diferentes objetos puede resultar difícil. Además, la encapsulación puede facilitar la modificación y actualización del código, ya que los cambios en una parte del código tendrán menos impacto en el resto del sistema. En general, la encapsulación es una técnica poderosa que puede ayudar a crear código más robusto y mantenible.

En Python, la encapsulación se logra utilizando modificadores de acceso privados y protegidos para atributos y métodos. Si bien no existe un concepto estricto de miembros privados o protegidos, se siguen ciertas convenciones para indicar el nivel de acceso previsto:

8.5.1: Miembros públicos:

De forma predeterminada, todos los miembros de una clase son públicos, lo que significa que se puede acceder a ellos desde cualquier lugar dentro y fuera de la clase. Sin embargo, es importante tener en cuenta que hacer que todos los miembros sean públicos puede conducir a posibles problemas de seguridad, ya que la información sensible puede ser accedida o modificada por usuarios no autorizados. Para mitigar este riesgo, se recomienda utilizar modificadores de acceso como privado o protegido para miembros sensibles, y solo proporcionar acceso público a los miembros necesarios. Además, el uso de técnicas de encapsulación como getters y setters puede ayudar a garantizar que los datos se accedan y modifiquen de manera controlada y segura.

8.5.2: Miembros protegidos:

Si un miembro está destinado a ser accedido solo desde dentro de la clase y sus subclases, su nombre debe prefijarse con un guion bajo único (_). Esto se conoce como una convención y se usa ampliamente en Python. Sin embargo, es importante tener en cuenta que esta convención no impide realmente el acceso al miembro desde fuera de la clase o sus subclases. En tales casos, se recomienda usar la técnica de "name mangling" (alteración de nombres), que agrega un prefijo al nombre del miembro para dificultar el acceso desde fuera de la clase. El name mangling se logra prefijando el nombre del miembro con dos guiones bajos (__) y un sufijo de uno o más guiones bajos. Por ejemplo, un miembro llamado "my_var" se convertiría en "_MyClass__my_var" en la clase llamada "MyClass". Tenga en cuenta que esta técnica debe usarse con precaución, ya que puede dificultar la lectura y el mantenimiento del código.

8.5.3: Miembros privados:

Si un miembro está destinado a ser accedido solo dentro de la clase (ni siquiera por subclases), su nombre debe comenzar con dos guiones bajos (__). Python proporciona una forma limitada de privacidad mediante la alteración de nombres (name mangling), lo que dificulta, pero no imposibilita, el acceso al miembro desde fuera de la clase.

Es importante comprender que la alteración de nombres no es una forma de seguridad. Es simplemente una convención utilizada para desalentar el acceso accidental a miembros privados. De hecho, la alteración de nombres se puede eludir fácilmente accediendo al miembro usando su nombre modificado.

Además de los miembros privados, Python también permite miembros protegidos, que pueden ser accedidos por subclases pero no desde fuera de la clase. Estos miembros se marcan con un único guion bajo (_).

Cabe señalar que el uso de miembros privados y protegidos no es necesario en todos los casos. En muchas situaciones, es perfectamente aceptable hacer que todos los miembros sean públicos. Sin embargo, en proyectos más grandes o con múltiples desarrolladores, el uso de miembros privados y protegidos puede ayudar a prevenir modificaciones no deseadas en partes críticas del código.

Ejemplo:

class BankAccount:
    def __init__(self, account_number, balance):
        self._account_number = account_number
        self.__balance = balance

    def deposit(self, amount):
        self.__balance += amount

    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient funds")

    def get_balance(self):
        return self.__balance

account = BankAccount("12345", 1000)
account.deposit(500)
account.withdraw(300)

print(account.get_balance())  # Output: 1200

En este ejemplo, la clase BankAccount tiene un atributo __balance que está pensado como privado. Se han proporcionado métodos como deposit(), withdraw() y get_balance() para manipular el saldo, evitando así el acceso directo al atributo. Observe que _account_number es un miembro protegido, lo cual no es estrictamente forzado por Python, pero indica que debería tratarse como protegido por convención.

Ejercicio 8.5.1: Crear una clase simple de Empleado

En este ejercicio, crearás una clase simple de Empleado que utiliza la encapsulación para proteger sus miembros de datos.

Instrucciones:

  1. Crea una clase llamada Empleado.
  2. Define los siguientes atributos privados: __nombre, __apellido y __salario.
  3. Crea un constructor que tome nombre, apellido y salario como parámetros e inicialice los atributos privados.
  4. Crea métodos públicos get_nombre(), get_apellido() y get_salario() que devuelvan los atributos respectivos.
  5. Crea un método get_nombre_completo() que devuelva el nombre completo del empleado, que es la combinación del nombre y el apellido.

Solución:

class Employee:
    def __init__(self, first_name, last_name, salary):
        self.__first_name = first_name
        self.__last_name = last_name
        self.__salary = salary

    def get_first_name(self):
        return self.__first_name

    def get_last_name(self):
        return self.__last_name

    def get_salary(self):
        return self.__salary

    def get_full_name(self):
        return self.__first_name + " " + self.__last_name

employee = Employee("John", "Doe", 50000)
print(employee.get_full_name())  # Output: John Doe

Ejercicio 8.5.2: Implementar una clase Círculo

En este ejercicio, crearás una clase Círculo que utiliza la encapsulación para proteger sus miembros de datos y proporcionar métodos para manipularlos.

Instrucciones:

  1. Crea una clase llamada Círculo.
  2. Define los siguientes atributos privados: __radio y __pi (usa el valor 3.14159 para pi).
  3. Crea un constructor que tome el radio como parámetro e inicialice el atributo privado __radio.
  4. Crea métodos públicos get_radio() y get_pi() que devuelvan los atributos respectivos.
  5. Crea métodos calculate_area() y calculate_circumference() que devuelvan el área y la circunferencia del círculo, respectivamente.

Solución:

class Circle:
    def __init__(self, radius):
        self.__radius = radius
        self.__pi = 3.14159

    def get_radius(self):
        return self.__radius

    def get_pi(self):
        return self.__pi

    def calculate_area(self):
        return self.__pi * self.__radius ** 2

    def calculate_circumference(self):
        return 2 * self.__pi * self.__radius

circle = Circle(5)
print(circle.calculate_area())         # Output: 78.53975
print(circle.calculate_circumference())  # Output: 31.4159

Ejercicio 8.5.3: Creación de una cuenta protegida con contraseña

En este ejercicio, crearás una clase Cuenta que utiliza la encapsulación para proteger sus miembros de datos y requiere una contraseña para acceder a ciertos métodos.

Instrucciones:

  1. Crea una clase llamada Cuenta.
  2. Define los siguientes atributos privados: __numero_cuenta, __saldo y __contraseña.
  3. Crea un constructor que tome numero_cuenta, saldo y contraseña como parámetros e inicialice los atributos privados.
  4. Crea métodos públicos get_numero_cuenta() y get_saldo() que devuelvan los atributos respectivos.
  5. Crea un método validar_contraseña(self, contraseña) que devuelva True si la contraseña dada coincide con la contraseña de la cuenta, y False de lo contrario.
  6. Crea un método retirar(self, monto, contraseña) que verifique si la contraseña es correcta usando validar_contraseña(), y si es así, resta el monto dado del saldo.

Solución:

class Account:
    def __init__(self, account_number, balance, password):
        self.__account_number = account_number
        self.__balance = balance
        self.__password = password

    def get_account_number(self):
        return self.__account_number

    def get_balance(self):
        return self.__balance

    def validate_password(self, password):
        return self.__password == password

    def withdraw(self, amount, password):
        if self.validate_password(password):
            self.__balance -= amount
            return True
        return False

account = Account("123456", 1000, "secret123")
print(account.get_account_number())  # Output: 123456
print(account.get_balance())         # Output: 1000

if account.withdraw(200, "secret123"):
    print("Withdrawal successful!")
    print("New balance:", account.get_balance())  # Output: 800
else:
    print("Incorrect password or insufficient funds.")

En este ejercicio, has creado una clase Cuenta que demuestra el concepto de encapsulación al proteger sus miembros de datos y requerir una contraseña para ciertas operaciones.

Al concluir el capítulo 8, es importante reconocer la importancia de la programación orientada a objetos (OOP) en Python. A través de la OOP, podemos crear código más organizado, mantenible y escalable. En este capítulo, hemos explorado los conceptos clave de la OOP:

  1. Clases y objetos: Los fundamentos para crear tipos de datos personalizados e instancias en Python.
  2. Atributos y métodos: Cómo almacenar datos y definir comportamientos dentro de las clases.
  3. Herencia: Una forma de crear nuevas clases a partir de clases existentes, promoviendo la reutilización del código.
  4. Polimorfismo: Aprovechar el poder de la herencia y la anulación de métodos para crear código flexible que pueda manejar diferentes tipos de objetos.
  5. Encapsulación: Proteger el estado interno y la implementación de una clase, proporcionando una interfaz bien definida para interactuar con ella.

Al aplicar estos principios en tus programas Python, puedes crear código que sea más fácil de entender, depurar y ampliar. No olvides practicar la implementación de estos conceptos a través de ejercicios y proyectos del mundo real para obtener una comprensión más profunda de la OOP en Python.

A medida que avanzas, recuerda que Python es un lenguaje versátil que admite múltiples paradigmas de programación. La combinación de la OOP con otros enfoques, como la programación funcional, puede ayudarte a refinar y adaptar aún más tu código a diversas situaciones y requisitos. ¡Nos vemos en el próximo capítulo. Feliz programación!