Fundamentos de Programación y Python

1

¿Por qué aprender Python?

2

Introducción a Python

3

Conceptos Básicos de Programación

4

Práctica: Te doy la bienvenida a los ejercicios interactivos

5

Manipulación de Cadenas de Texto en Python

6

Enteros, Flotantes y Booleanos

7

Todo lo que Debes Saber sobre print en Python

8

Operaciones Matemáticas en Python

9

Operaciones de Entrada/Salida en Consola

Colección y Procesamiento de Datos en Python

10

Listas

11

Método slice

12

Listas de más dimensiones y Tuplas

13

Aplicación de Matrices

14

Diccionarios

15

Comprehension Lists en Python (CLASE NUEVA)

Control de Flujo en Python

16

Estructuras condicionales

17

Bucles y Control de Iteraciones

18

Generadores e Iteradores

Funciones y Manejo de Excepciones en Python

19

Uso de Funciones en Python

20

Funciones Lambda y Programación Funcional en Python

21

¿Cómo realizar una función recursiva en Python?

22

Manejo de Excepciones y Uso de Pass (CLASE NUEVA)

Programación Orientada a Objetos en Python

23

Fundamentos de Programación Orientada a Objetos en Python

24

Ejercicio Biblioteca con POO

25

Herencia en POO con Python

26

Objetos heredados

27

Los 4 pilares de la programacion orientada a objetos

28

Uso de super() en Python (CLASE NUEVA)

29

Superando los Fundamentos de Programación Orientada a Objetos en Python

Lectura y escritura de archivos

30

Manejo de Archivos .TXT (CLASE NUEVA)

31

Manejo de Archivos CSV (CLASE NUEVA)

32

Manejo de Archivos JSON (CLASE NUEVA)

Biblioteca estándar de Python

33

Biblioteca estándar en Python (CLASE NUEVA)

34

Librería Os, Math y Random (CLASE NUEVA)

35

Librería Statistics y Análisis Estadístico (CLASE NUEVA)

36

Proyecto final: Guerra naval

Conceptos avanzados de Python

37

Recapitulación de lo aprendido hasta ahora

38

Escribir código Pythonico y profesional

39

Comentarios y Docstrings en Python

40

Scope y closures: variables locales y globales

41

Anotaciones de tipo

42

Validación de tipos en métodos

43

Librería Collections y Enumeraciones

Decoradores

44

Decoradores en Python

45

Decoradores anidados y con parámetros

46

Uso de Decoradores en clases y métodos

Métodos y estructura de clases en Python

47

Métodos mágicos

48

Sobrecarga de operadores

49

Implementación de `if __name__ == "__main__":`

50

Metaprogramación en Python

51

Uso de *args y **kwargs

52

Métodos privados y protegidos

53

Gestión avanzada de propiedades

54

Métodos estáticos y de clase avanzados

Programación concurrente y asíncrona

55

Introducción a la concurrencia y paralelismo

56

Threading y multiprocessing en Python

57

Asincronismo con asyncio

58

Asincronismo y concurrencia

Creación de módulos y paquetes

59

Creación de módulos en Python

60

Gestión de paquetes

61

Publicación de paquetes en PyPI

Proyecto final

62

Implementación de un sistema completo

63

Implementación de un Sistema Completo

You don't have access to this class

Keep learning! Join and start boosting your career

Aprovecha el precio especial y haz tu profesión a prueba de IA

Antes: $249

Currency
$209
Suscríbete

Termina en:

1 Días
2 Hrs
9 Min
53 Seg
Curso de Python

Curso de Python

Carli Code

Carli Code

Uso de Decoradores en clases y métodos

46/63
Resources

How do decorators work in object-oriented programming?

Decorators in Python are a powerful element that adds additional functionality to methods or functions without modifying their internal structure. This concept is well known in the context of functions, but its application in object-oriented programming (OOP) further expands its possibilities. Here we explore its use within classes.

What is a static method?

Static methods do not depend on the class instance but belong to the class itself. You use the @staticmethod decorator when you want to create a method that does not need to access the class or modify its data.

class Calculator: @staticmethod def sum(a: int, b: int)-> int: return a + b
  • Advantage: Ideal for operations that do not require access to class properties or methods.

What does a class method do?

A class method is bound to the class and not to the instance. It uses the @classmethod decorator, and the first parameter is always cls, which represents the class.

class Counter: count = 0
 @classmethod def increment(cls): cls.count += 1
  • Common use: Modify the class state common to all instances.

How is the property decorator used?

The property decorator allows accessing a method as if it were an attribute. This improves encapsulation and maintains control over how the internal information of the class is manipulated.

Example of class Circle

class Circle: def __init__(self, radius: float): self._radius = radius    
 @property def area(self)-> float: return 3.1416 * self._radius ** 2    
 @property def radius(self)-> float: return self._radius
 @radius.setter def radius(self, value: float): if value < 0: raise ValueError("Radius cannot be negative") self._radius = value

Why use property?

  1. Access control: Access to sensitive attributes can be controlled without affecting the external interface.

  2. Flexibility: Attributes can be modifiable, which allows adding logic for validations or calculations when changing values.

  3. Encapsulation: Allows hiding the calculation logic or validations, delivering a clean interface to the user of the class.

Final thoughts on decorators in OOP

Decorators such as staticmethod, classmethod, and property are valuable tools that enrich class design in Python. They allow you to:

  • Add special functionality without modifying the code base.
  • Improve code readability and professionalism.
  • Maintain a disciplined and efficient object-oriented design.

Now it's up to you to apply them and discover their full potential in your projects! Remember: With great power comes great code responsibility!

Contributions 18

Questions 1

Sort by:

Want to see more contributions, questions and answers from the community?

En Python, los decoradores no solo se usan en funciones, también se pueden aplicar a clases y métodos para extender o modificar su comportamiento. Los decoradores en clases y métodos son especialmente útiles para aspectos transversales como la validación de permisos, el registro de accesos, el manejo de excepciones y la administración de cachés. Aquí tienes una guía rápida sobre cómo funcionan y un ejemplo detallado: \### Decoradores en Métodos de Clase Los decoradores pueden aplicarse a métodos específicos de una clase, y se comportan según el tipo de método: \- \*\*Métodos de instancia\*\* (los métodos normales que acceden a `self`). \- \*\*Métodos de clase\*\* (aquellos que usan `@classmethod` y acceden a `cls`). \- \*\*Métodos estáticos\*\* (usando `@staticmethod`, no acceden ni a `self` ni a `cls`). Los decoradores en métodos de clase pueden hacer cosas como: 1\. Controlar permisos antes de ejecutar el método. 2\. Hacer un registro (logging) cada vez que se llama al método. 3\. Manejar errores de forma consistente. \### Decoradores en Clases Un decorador en una clase modifica el comportamiento de la clase en su conjunto, normalmente para extenderla o agregarle nuevas funcionalidades. \### Ejemplo de Uso Imaginemos una clase `CuentaBancaria` donde queremos aplicar un decorador para registrar cada vez que se realiza una operación, y otro decorador para comprobar permisos de acceso a ciertas operaciones. ```python from functools import wraps import datetime \# Decorador para verificar permisos def verificar\_permisos(permiso\_necesario): def decorador\_metodo(func): @wraps(func) def envoltura(self, \*args, \*\*kwargs): if permiso\_necesario not in self.permisos: raise PermissionError(f"No tienes permisos para realizar esta operación: {permiso\_necesario}") return func(self, \*args, \*\*kwargs) return envoltura return decorador\_metodo \# Decorador para registrar en log def registrar\_operacion(func): @wraps(func) def envoltura(self, \*args, \*\*kwargs): resultado = func(self, \*args, \*\*kwargs) operacion = f"{func.\_\_name\_\_} ejecutado el {datetime.datetime.now()}" self.historial.append(operacion) print(f"Registro: {operacion}") return resultado return envoltura \# Clase CuentaBancaria usando decoradores en sus métodos class CuentaBancaria: def \_\_init\_\_(self, balance=0): self.balance = balance self.historial = \[] self.permisos = \['ver\_balance', 'retirar'] # permisos actuales del usuario @registrar\_operacion @verificar\_permisos('ver\_balance') def ver\_balance(self): print(f"Balance actual: ${self.balance}") return self.balance @registrar\_operacion @verificar\_permisos('retirar') def retirar(self, cantidad): if cantidad > self.balance: raise ValueError("Fondos insuficientes") self.balance -= cantidad print(f"Retiro exitoso: ${cantidad}. Nuevo balance: ${self.balance}") return self.balance \# Uso de la clase con decoradores cuenta = CuentaBancaria(1000) \# Operaciones cuenta.ver\_balance() cuenta.retirar(200) \# Intento de retiro sin permisos try: cuenta.permisos.remove('retirar') cuenta.retirar(100) except PermissionError as e: print(e) ``` \### Explicación 1\. \*\*`@verificar\_permisos`\*\*: Este decorador toma un parámetro, `permiso\_necesario`, y se asegura de que el usuario tenga el permiso adecuado antes de ejecutar el método. 2\. \*\*`@registrar\_operacion`\*\*: Este decorador registra cada operación realizada, almacenándola en el atributo `historial`. Este patrón es muy poderoso para clases que requieren varias verificaciones y registros en sus métodos, proporcionando una estructura de código limpia y modular.
creo, que solo lanza ejemplos muy al aire, tan solo no probo el ejemplo de calculadora... solo dejo al aire la idea. Parece que no hay una estructura en los conceptos que explica.
No termino de entender para qué usar decoradores, por ejemplo, en el ejemplo del área del círculo, por que no sencillamente no se crea un atributo como self.area = (radius\*\*2)\*3.1416 y ya, los atributos extienden la funcionalidad sin embargo no veo la razón de usarlos si simplemente se pueden dar esas características y funciones a la clase y ya.
alguien tiene informacion de los decoradores? no he entendido nada, como funcionan?
En Python, `property` y `setter` son decoradores que permiten gestionar el acceso a los atributos de una clase de forma controlada. - **`property`**: Este decorador permite acceder a un método como si fuera un atributo. Por ejemplo, si tenemos un método que calcula el área de un círculo, al usar `property`, podemos acceder a este método sin paréntesis, como si fuera un atributo. - **`setter`**: Se usa para definir un método que permite modificar el valor de un atributo de forma segura. Por ejemplo, puedes validar que el nuevo valor no sea negativo antes de asignarlo a un atributo. Estos decoradores ayudan a encapsular la lógica de la clase y proporcionan un control más estricto sobre cómo se accede y modifica la información.
Alguien me podría comentar la diferencia entre llamar al atributo self.radius y self.\_radius? No entiendo muy bien por qué se coloca el guion previo al nombre del atributo.
Decoradores predefinidos como el @staticmethod : existe incluso antes de cualquier instancia de la clase, @classmethod: hacen modificacion del estado de la clase ,@propperti : permite que los metodos se comporten como atributos aplicando encapsulamiento son especialmente decoradores de biblioteca como: @functools.wraps @lru\_cache @contextManager, @dataclass proporcionan funcionalidades adicionales y optimizaciones decoradores personalizados pueden usarse para registro validaciones o timers
Decoradores que incluye python: **Decoradores para Métodos en Clases** 1. `@staticmethod`pythonCopiar código`class MiClase`: ` @staticmethod` ` def metodo_estatico`(): ` print("Este es un método estático."`) MiClase.metodo\_estatico() * Convierte un método en un **método estático**. * El método estático no recibe ni `self` ni `cls` como argumento. * Se utiliza para definir funciones relacionadas con la clase, pero que no dependen de una instancia. **Ejemplo**: 2. `@classmethod`pythonCopiar código`class MiClase`: ` @classmethod` ` def metodo_clase(cls`): ` print("Este es un método de clase."`) MiClase.metodo\_clase() * Convierte un método en un **método de clase**. * Recibe `cls` como primer argumento, que representa la clase (no la instancia). * Se utiliza cuando se necesita acceder a atributos o métodos de clase. **Ejemplo**: 3. `@property`pythonCopiar código`class MiClase`: ` def __init__(self, valor`): self.\_valor = valor ` @property` ` def valor(self`): ` return` self.\_valor `obj = MiClase(10`) `print(obj.valor) # Accede al atributo con lógica adicional.` * Convierte un método en un **getter** para un atributo. * Se utiliza para acceder a atributos como si fueran propiedades, lo que permite definir lógica adicional al obtener un valor. **Ejemplo**: 4. `@<property>.setter`pythonCopiar código`class MiClase`: ` def __init__(self, valor`): self.\_valor = valor ` @property` ` def valor(self`): ` return` self.\_valor ` @valor.setter` ` def valor(self, nuevo_valor`): self.\_valor = nuevo\_valor `obj = MiClase(10`) `obj.valor = 20 # Cambia el valor con lógica adicional.` `print`(obj.valor) * Permite definir un **setter** para un atributo, utilizado para establecer valores con lógica adicional. **Ejemplo**: 5. `@<property>.deleter`pythonCopiar código`class MiClase`: ` def __init__(self, valor`): self.\_valor = valor ` @property` ` def valor(self`): ` return` self.\_valor ` @valor.deleter` ` def valor(self`): ` del` self.\_valor `obj = MiClase(10`) `del obj.valor # Lógica al eliminar.` * Permite definir un **deleter** para un atributo, utilizado para eliminar valores con lógica adicional. **Ejemplo**: ### **Decoradores para Corrutinas y Generadores** 1. `@asyncio.coroutine` *(obsoleto en Python 3.10)* * Usado para definir funciones generadoras asíncronas en versiones anteriores de Python. * Reemplazado por `async def`. 2. `@contextlib.contextmanager`pythonCopiar código`from contextlib import` contextmanager `@contextmanager` `def ejemplo_contexto`(): ` print("Entrando al contexto"`) ` yield` ` print("Saliendo del contexto"`) `with` ejemplo\_contexto(): ` print("Dentro del contexto"`) * Convierte una función en un **administrador de contexto** (usado con `with`). * Simplifica la creación de administradores de contexto personalizados. **Ejemplo**: ### **Decoradores para el Módulo** `functools` 1. `@functools.lru_cache`pythonCopiar código`from functools import` lru\_cache `@lru_cache(maxsize=3)` `def calcular(valor`): ` print(f"Calculando para {valor}"`) ` return valor ** 2` `print(calcular(2`)) `print(calcular(2)) # Usa el valor en caché.` * Implementa un caché para almacenar resultados de funciones. * Optimiza el rendimiento evitando cálculos repetitivos. **Ejemplo**: 2. `@functools.wraps`pythonCopiar código`from functools import` wraps `def mi_decorador(func`): ` @wraps(func)` ` def wrapper(*args, **kwargs`): ` print("Antes de la función"`) ` return` func(\*args, \*\*kwargs) ` return` wrapper `@mi_decorador` `def saludar`(): ` """Función para saludar."""` ` print("¡Hola!"`) `print`(saludar.\_\_doc\_\_) * Conserva el nombre y la documentación de una función al ser decorada. * Útil para crear decoradores personalizados. **Ejemplo**: 3. `@functools.partial` *(No es un decorador en sí, pero relacionado)*pythonCopiar código`from functools import` partial `def multiplicar(a, b`): ` return` a \* b `por_dos = partial(multiplicar, b=2`) `print(por_dos(4)) # Multiplica 4 * 2` * Crea una nueva función fijando algunos de los argumentos de otra función. **Ejemplo**: ### **Decoradores para Pruebas** 1. `@unittest.skip`pythonCopiar código`import` unittest `class MiPrueba`(unittest.TestCase): ` @unittest.skip("Motivo de omisión")` ` def test_saltar(self`): ` self.fail("No debería ejecutarse."`) * Omite una prueba en un caso de prueba de `unittest`. **Ejemplo**: 2. `@unittest.expectedFailure`pythonCopiar código`import` unittest `class MiPrueba`(unittest.TestCase): ` @unittest.expectedFailure` ` def test_fallo_esperado(self`): ` self.assertEqual(1, 0`) * Marca una prueba como esperada a fallar. **Ejemplo**: ### **Decoradores Misceláneos** 1. `@dataclasses.dataclass`pythonCopiar código`from dataclasses import` dataclass `@dataclass` `class Persona`: ` nombre: str` ` edad: int` `p = Persona("Carlos", 25`) `print`(p) * Convierte una clase en una **data class**, agregando automáticamente métodos como `__init__`, `__repr__`, y `__eq__`. **Ejemplo**: 2. `@typing.overload`pythonCopiar código`from typing import` overload `@overload` `def sumar(a: int, b: int) -> int`: ... `@overload` `def sumar(a: str, b: str) -> str`: ... `def sumar(a, b`): ` return` a + b * Especifica diferentes firmas de funciones para sobrecarga de funciones (solo para propósitos de tipado).
No entendí nada en esta clase jajaja :(
quede muy perdido en esta clase!!! creo que la metodologia de esta clase o la de toda la seccion de decoradores puede mejorarse porque hay muchos vacios y deja mucho hueco solo lo explica superficialmente y creo que es tema algo complejo
El decorador staticmethod sirve para indicar que el método de la clase va a ser estático. Esto, sin ser muy técnicos, significa que se puede acceder al método de la clase sin necesidad de instanciar un objeto de esta.
El decorador `@classmethod` se utiliza para definir un método que pertenece a la clase en lugar de a una instancia específica de la clase. Esto significa que puedes llamar a este método desde la clase misma, sin necesidad de crear una instancia. El primer argumento de un método de clase es `cls`, que representa la clase y no la instancia. Se usa comúnmente para crear métodos que deben operar en la clase en lugar de los objetos individuales, como un método para contar cuántas instancias de la clase han sido creadas.
El decorador `@property` en Python se utiliza para convertir un método en un atributo de clase. Esto permite acceder a funciones como si fueran atributos sin necesidad de llamarlas con paréntesis. Su propósito es mejorar la encapsulación, permitiendo acceso controlado a los atributos, lo que facilita la validación de datos. Por ejemplo, al usar `@property`, puedes definir un método que calcule y retorne un valor (como el área de un círculo) y acceder a él con `objeto.area` en lugar de `objeto.area()`. Esto hace que el código sea más legible y limpio, manteniendo la funcionalidad de métodos.
```js print("") print("* Ejercicio 1:") print("- Uso de un método estático (@staticmethod).") print("") class Calculator: """ Clase Calculator con un método estático para sumar dos números. """ @staticmethod def add(a: int, b: int) -> int: """ Método estático para sumar dos números enteros. Los métodos estáticos no tienen acceso a la instancia de la clase (self) ni a la clase misma (cls). Se comportan como funciones regulares que están asociadas a la clase. Args: a: El primer número entero. b: El segundo número entero. Returns: La suma de a y b. """ return a + b print(Calculator.add(2, 2)) # Salida: 4 print("") print("* Ejercicio 2:") print("- Uso de un método de clase (@classmethod).") print("") class Counter: """ Clase Counter con un método de clase para incrementar un contador. """ count = 0 @classmethod def increment(cls): """ Método de clase para incrementar el contador de la clase. Los métodos de clase tienen acceso a la clase misma (cls) y se utilizan para operaciones que afectan a la clase en su conjunto, no a una instancia específica. Args: cls: La clase Counter. """ cls.count += 1 Counter.increment() Counter.increment() print(Counter.count) # Salida: 2 print("") print("* Ejercicio 3:") print("- Uso de propiedades (@property) y setters para gestionar el radio de un círculo.") print("") class Circle: """ Clase Circle con propiedades para el área y el radio, y un setter para el radio. """ def __init__(self, radius: float): """ Constructor de la clase Circle. Args: radius: El radio del círculo. """ self.radius = radius @property def area(self) -> float: """ Propiedad para calcular el área del círculo. Las propiedades permiten acceder a atributos como si fueran atributos directos, pero en realidad se ejecutan métodos (getters, setters, deleters). """ return 3.1416 * (self.radius**2) @property def get_radius(self) -> float: """ Propiedad getter para el radio. """ return self.radius @get_radius.setter def get_radius(self, value: float): """ Setter para el radio, con validación. Los setters permiten controlar cómo se modifican los atributos. En este caso, se valida que el radio no sea negativo. Args: value: El nuevo valor para el radio. Raises: ValueError: Si el radio es menor que cero. """ if value < 0: raise ValueError("El radio no puede ser menor a cero") self.radius = value circle = Circle(5) print(circle.area) # Salida: 78.54 print(circle.get_radius) # Salida: 5.0 try: circle.get_radius = -1 # Intentar asignar un radio negativo except ValueError as e: print(e) # Salida: El radio no puede ser menor a cero print(circle.area) # Salida: 78.54 (el radio no cambió) print(circle.get_radius) # Salida: 5.0 (el radio no cambió) circle.get_radius = 10 print(circle.area) # Salida: 314.16 (el radio cambió) print(circle.get_radius) # Salida: 10.0 (el radio cambió) ```print("")print("\* Ejercicio 1:")print("- Uso de un método estático (@staticmethod).")print("") class Calculator:    """    Clase Calculator con un método estático para sumar dos números.    """     @staticmethod    def add(a: int, b: int) -> int:        """        Método estático para sumar dos números enteros.         Los métodos estáticos no tienen acceso a la instancia de la clase (self)        ni a la clase misma (cls).  Se comportan como funciones regulares que        están asociadas a la clase.         Args:            a: El primer número entero.            b: El segundo número entero.         Returns:            La suma de a y b.        """        return a + b print(Calculator.add(2, 2))  *# Salida: 4* print("")print("\* Ejercicio 2:")print("- Uso de un método de clase (@classmethod).")print("") class Counter:    """    Clase Counter con un método de clase para incrementar un contador.    """    count = 0     @classmethod    def increment(cls):        """        Método de clase para incrementar el contador de la clase.         Los métodos de clase tienen acceso a la clase misma (cls) y se utilizan        para operaciones que afectan a la clase en su conjunto, no a una instancia        específica.         Args:            cls: La clase Counter.        """        cls.count += 1 Counter.increment()Counter.increment()print(Counter.count)  *# Salida: 2* print("")print("\* Ejercicio 3:")print("- Uso de propiedades (@property) y setters para gestionar el radio de un círculo.")print("") class Circle:    """    Clase Circle con propiedades para el área y el radio, y un setter para el radio.    """    def \_\_init\_\_(self, radius: float):        """        Constructor de la clase Circle.         Args:            radius: El radio del círculo.        """        self.radius = radius     @property    def area(self) -> float:        """        Propiedad para calcular el área del círculo.         Las propiedades permiten acceder a atributos como si fueran atributos        directos, pero en realidad se ejecutan métodos (getters, setters, deleters).        """        return 3.1416 \* (self.radius\*\*2)     @property    def get\_radius(self) -> float:        """        Propiedad getter para el radio.        """        return self.radius     @get\_radius.setter    def get\_radius(self, value: float):        """        Setter para el radio, con validación.         Los setters permiten controlar cómo se modifican los atributos.  En este        caso, se valida que el radio no sea negativo.         Args:            value: El nuevo valor para el radio.         Raises:            ValueError: Si el radio es menor que cero.        """        if value < 0:            raise ValueError("El radio no puede ser menor a cero")        self.radius = value circle = Circle(5)print(circle.area)        *# Salida: 78.54*print(circle.get\_radius)  *# Salida: 5.0* try:    circle.get\_radius = -1  *# Intentar asignar un radio negativo*except ValueError as e:    print(e)  *# Salida: El radio no puede ser menor a cero* print(circle.area)        *# Salida: 78.54 (el radio no cambió)*print(circle.get\_radius)  *# Salida: 5.0 (el radio no cambió)* circle.get\_radius = 10print(circle.area)        *# Salida: 314.16 (el radio cambió)*print(circle.get\_radius)  *# Salida: 10.0 (el radio cambió)*
**Hi there!** Para practicar y entender un poquito mejor el uso de los decoradores me puse a programar algo que calculara los lados y ángulos de un triangulo 🤓 y aunque a mi código le faltan muchas cosas por pulir me justaría que me dijeran que tal les parece (reconozco que es medio largo pero si a alguien le interesa, le estaría muy agradecido si critica mi código) ```python """ ejemplo de uso de decoradores (staticmethod, classmethod, property, '.setter') ejercicio de creacion de una calculadora de triangulos """ from math import sqrt, degrees, radians, cos, sin, acos def validateValue(validator): """Decorator to validate the value of a property.""" def decorator(settterFunction): def wrapper(self, value): if validator(value): settterFunction(self, value) else: raise ValueError(f"Invalid value: {value}") return wrapper return decorator class Triangle(): def __init__(self, **kwarg: float): self._sides = {"a": None, "b": None, "c": None} self._angles = {"alpha": None, "beta": None, "theta": None} for key, value in kwarg.items(): # type verification if not isinstance(value, (int, float)): raise ValueError(f"Invalid value: {value}") else: # range verification if value <= 0: raise ValueError(f"Invalid value: {value} is less or equal to 0") else: # key verification if key in self._sides: self._sides[key] = value elif key in self._angles: self._angles[key] = value else: raise ValueError(f"Invalid key: {key}") @property def sides(self): return self._sides @property def angles(self): return self._angles @property def side_a(self): return self._sides["a"] @property def side_b(self): return self._sides["b"] @property def side_c(self): return self._sides["c"] @property def angle_alpha(self): return self._angles["alpha"] @property def angle_beta(self): return self._angles["beta"] @property def angle_theta(self): return self._angles["theta"] @side_a.setter @validateValue(lambda x: isinstance(x, (int, float)) and x > 0) def side_a(self, value): self._sides["a"] = value @side_b.setter @validateValue(lambda x: isinstance(x, (int, float)) and x > 0) def side_b(self, value): self._sides["b"] = value @side_c.setter @validateValue(lambda x: isinstance(x, (int, float)) and x > 0) def side_c(self, value): self._sides["c"] = value @angle_alpha.setter @validateValue(lambda x: isinstance(x, (int, float)) and 0 < x < 180) def angle_alpha(self, value): self._angles["alpha"] = value @angle_beta.setter @validateValue(lambda x: isinstance(x, (int, float)) and 0 < x < 180) def angle_beta(self, value): self._angles["beta"] = value @angle_theta.setter @validateValue(lambda x: isinstance(x, (int, float)) and 0 < x < 180) def angle_theta(self, value): self._angles["theta"] = value def triangleInfo(self): """return the sides and angles of the triangle""" return f"Triangle sides: {self.sides} \nTriangle angles: {self.angles}" @staticmethod def niceTriangle(): return "This is a nice triangle" @property def isAnglesComplete(self): """ check if the angles are complete return a boolean value """ return all(value is not None for value in self.angles.values()) @property def isSidesComplete(self): """ check if the sides are complete return a boolean value""" return all(value is not None for value in self.sides.values()) @property def kindOfTriangle(self): """Return the kind of triangle based on its sides.""" if self.isSidesComplete: a, b, c = self.sides.values() if a == b == c: return "Equilateral" elif a == b or a == c or b == c: return "Isosceles" else: return "Scalene" return "Incomplete sides" @property def isTriangle(self): """ Check if the given data conforms to a viable triangle. retur a bulean value if all information is complete and return a message if the information is incomplete """ # Get the sides a, b, c = self.sides.values() #get the angles alpha, beta, theta = self.angles.values() if self.isAnglesComplete and self.isSidesComplete: checkAngles = lambda x: sum(self.angles.values()) == 180 # check if the angles sum is equal 180 checkSides = lambda x: a + b > c and a + c > b and b + c > a # check if the sides are valid if checkAngles() and checkSides(): return True else: return False else: return "The triangle information is incomplete" @property def isCalculable(self): """Check if the triangle is calculable.""" # Count the sides and angles sides = self.dataCounter["sides"] angles = self.dataCounter["angles"] # Verify the possible conditions if sides == 3: return True # 3 sides are sufficient elif sides >= 2 and angles >= 1: return True # 2 sides and 1 angle elif sides >= 1 and angles >= 2: return True # 1 side and 2 angles else: return False # None of the conditions are met @property def dataCounter(self): """ Count the number of defined sides and angles. Returns a dictionary with the counts of sides and angles. """ return { "sides": sum(1 for value in self.sides.values() if value), "angles": sum(1 for value in self.angles.values() if value) } def confirmAngles(self): """Check if the angles sum is equal 180.""" if sum(self.angles.values()) == 180: return True return False def completeAngles(self): """ Calculate the missing angles if have all sides. returns a dictionary with the angles. """ # Get the sides a, b, c = self.sides.values() # Get the angles alpha, beta, theta = self.angles.values() # there is a missing angle? if not self.isAnglesComplete: if all(value is not None for value in self.sides.values()): # check if the sides are complete if alpha is None: alpha = degrees(acos((b**2 + c**2 - a**2) / (2 * b * c))) if beta is None: beta = degrees(acos((a**2 + c**2 - b**2) / (2 * a * c))) if theta is None: theta = degrees(acos((a**2 + b**2 - c**2) / (2 * a * b))) self._angles = {"alpha": alpha, "beta": beta, "theta": theta} if self.confirmAngles(): # check if the angles sum is equal 180 return self.angles else: for angle in self.angles: self.angles[angle] = None # reset the angles self.completeAngles() # recursive call else: if self.dataCounter["sides"] < 3 and self.dataCounter["angles"] <= 1: return "whit this sides data is impossible conform a triangle" if self.dataCounter["sides"] < 3 and self.dataCounter["angles"] == 2: if alpha is None: alpha = 180 - beta - theta if beta is None: beta = 180 - alpha - theta if theta is None: theta = 180 - alpha - beta self._angles = {"alpha": alpha, "beta": beta, "theta": theta} return self.angles # calculate the sides of the triangle def completeSides(self): """ Calculate the missing sides if have all angles. returns a dictionary with the sides. """ # Get the angles alpha, beta, theta = self.angles.values() # Get the sides a, b, c = self.sides.values() # Check if the triangle is calculable nSides, nAngles = self.dataCounter.values() if nSides >= 2: if a is None: a = sqrt(b**2 + c**2 - 2 * b * c * cos(radians(alpha))) if b is None: b = sqrt(a**2 + c**2 - 2 * a * c * cos(radians(beta))) if c is None: c = sqrt(a**2 + b**2 - 2 * a * b * cos(radians(theta))) self._sides = {"a": a, "b": b, "c": c} if self.isSidesComplete: return self.sides if nAngles >= 2: if a is None: try: a = b * sin(radians(alpha)) / sin(radians(beta)) except: a = c * sin(radians(alpha)) / sin(radians(theta)) if b is None: try: b = a * sin(radians(beta)) / sin(radians(alpha)) except: b = c * sin(radians(beta)) / sin(radians(theta)) if c is None: try: c = a * sin(radians(theta)) / sin(radians(alpha)) except: c = b * sin(radians(theta)) / sin(radians(beta)) self._sides = {"a": a, "b": b, "c": c} return self.sides @property def perimeter(self): """Calculate the perimeter of the triangle.""" return sum(self.sides.values()) @property def area(self): """ Calculate the area of the triangle if we have all sides. returns the area of the triangle if the sides are complete and none if the sides are incomplete """ if self.isSidesComplete: a, b, c = self.sides.values() s = self.perimeter / 2 return sqrt(s * (s - a) * (s - b) * (s - c)) # Heron's formula else: return None @classmethod def createTriangle(cls, a, b, theta): """Create a triangle with the given sides and angles.""" # Calculate the missing side c = sqrt(a**2 + b**2 - 2 * a * b * cos(radians(theta))) # Create the triangle return cls(a=a, b=b, c=c, theta=theta) # setters t1 = Triangle(a=3, b=4, c=5, alpha=90, beta=30) t2 = Triangle(c = 5, alpha = 90, beta = 30) print('triangulo 1') print(t1.triangleInfo()) print(t1.isAnglesComplete) print(t1.isSidesComplete) print(t1.kindOfTriangle) print(t1.isCalculable) print(t1.isTriangle) print(t1.completeAngles()) print(t1.completeSides()) print(t1.completeAngles()) print(t1.triangleInfo()) print('\ntriangulo 2') print(t2.triangleInfo()) print("it's calculable? " + str(t2.isCalculable)) print(t2.completeAngles()) print(t2.completeSides()) print(t2.kindOfTriangle) print(t2.area) t3 = Triangle(b=1) t3.angle_alpha = 60 t3.side_b = 3 t3.side_c = 4 t3.completeSides() print(t3.triangleInfo()) ```
No, no se puede utilizar el decorador `@nombre_del_metodo.setter` sin haber definido previamente el decorador `@property` para ese mismo método. El decorador `@property` convierte el método en un atributo, lo que permite el uso del decorador `@setter` para modificar su valor. Esto es fundamental en Python para garantizar que se mantenga la consistencia y el control sobre la asignación de valores a los atributos de la clase.
Los **decoradores** en programación son herramientas que permiten modificar o extender el comportamiento de funciones, métodos o clases sin cambiar directamente su código. Son muy utilizados en lenguajes como **Python**, donde se implementan como funciones o clases que actúan sobre otras funciones o clases. ### ¿Cómo funcionan los decoradores? Un decorador es una función que toma otra función o clase como entrada, realiza alguna acción (como añadir funcionalidades) y devuelve una nueva función o clase con el comportamiento modificado. ### Ejemplo sencillo en Python ```python def decorador(func): def nueva_funcion(): print("Antes de ejecutar la función original") func() print("Después de ejecutar la función original") return nueva_funcion @decorador def saludo(): print("Hola, mundo!") saludo() ```Salida: Antes de ejecutar la función original Hola, mundo! Después de ejecutar la función original. El decorador `decorador` modifica el comportamiento de la función `saludo`, añadiendo código antes y después de su ejecución.
```js # Decorator with class using static method # it means that the method is not dependent on the instance of the class class Calculator: @staticmethod def add( a, b): """ sum of two numbers """ return a + b # Decorator with class using class method # it means that the method is dependent on the class and not on the instance of the class Counter: """ Counter class """ count = 0 @classmethod def increment(cls): """ increment count """ cls.count += 1 # Counter.increment() # Counter.increment() # print(Counter.count) # Decorator with class using property # it means that the method is dependent on the instance of the class class Circle: """ Circle class """ def __init__(self, radius: float): self._radius = radius @property def area(self): """ calculate area of circle """ return 3.1416 * (self._radius ** 2) @property def radius(self): """ get radius """ return self._radius @radius.setter def radius(self, value): """ set radius """ if value < 0: raise ValueError("Radius cannot be negative") self._radius = value circle = Circle(5) print(circle.area) circle.radius = 10 print(circle.area) del circle.radius print(circle.area) ```""" This module is an example of using decorators with class """ *# Decorator with class using static method# it means that the method is not dependent on the instance of the class*class *Calculator*: @*staticmethod* def *add*( *a*, *b*): """ sum of two numbers """ return *a* + *b* *# Decorator with class using class method# it means that the method is dependent on the class and not on the instance of the*class *Counter*: """ Counter class """ count = 0 @*classmethod* def *increment*(*cls*): """ increment count """ *cls*.count += 1 *# Counter.increment()# Counter.increment()# print(Counter.count)* *# Decorator with class using property# it means that the method is dependent on the instance of the class* class *Circle*: """ Circle class """ def \_\_init\_\_(*self*, *radius*: *float*): *self*.\_radius = *radius* @*property* def *area*(*self*): """ calculate area of circle """ return 3.1416 \* (*self*.\_radius \*\* 2) @*property* def *radius*(*self*): """ get radius """ return *self*.\_radius @radius.setter def *radius*(*self*, *value*): """ set radius """ if *value* < 0: raise *ValueError(*"Radius cannot be negative"*)* *self*.\_radius = *value* circle = *Circle(5)print(circle.area)*circle.radius = 10*print(circle.area)*del circle.radius*print(circle.area)*