CAP 09 · LEC 03·Programación orientada a objetos

self, métodos de instancia, @classmethod y @staticmethod

Python tiene tres tipos de métodos en una clase: de instancia (acceden a self), de clase (acceden a cls y actúan sobre la clase) y estáticos (funciones que viven en la clase por organización). Conocer la diferencia es señal de madurez.

● AVANZADO10 min lectura4 ejerciciospor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

Método de instancia y self

Un método de instancia recibe self como primer argumento. self es el objeto en sí — a través de él puedes leer y modificar cualquier atributo de la instancia o llamar a otros métodos.

class Counter: def __init__(self, start: int = 0) -> None: self.count = start self._history: list[int] = [start] # Método de instancia: accede y modifica self def increment(self, amount: int = 1) -> None: self.count += amount self._history.append(self.count) def reset(self) -> None: self.count = 0 self._history.append(0) def history(self) -> list[int]: return self._history.copy() # Los métodos pueden llamarse entre sí a través de self def double_increment(self) -> None: self.increment() # self.increment() llama al método del mismo objeto self.increment() c = Counter(10) c.increment(5) c.double_increment() print(c.count) # 22 print(c.history()) # [10, 15, 16, 17]
Salida22 [10, 15, 16, 17]

@classmethod y cls

Un @classmethod recibe cls (la clase) en lugar de self (la instancia). Se llama sobre la clase directamente — Clase.metodo() — aunque también funciona sobre instancias. Tiene acceso a los atributos de clase pero no a los de instancia.

class Temperature: absolute_zero_celsius: float = -273.15 def __init__(self, celsius: float) -> None: if celsius < Temperature.absolute_zero_celsius: raise ValueError(f"Temperatura por debajo del cero absoluto") self.celsius = celsius # @classmethod — recibe cls, no self @classmethod def from_fahrenheit(cls, fahrenheit: float) -> "Temperature": """Factory method: crea Temperature desde Fahrenheit.""" celsius = (fahrenheit - 32) * 5 / 9 return cls(celsius) # cls() crea una instancia de la clase (o subclase) @classmethod def from_kelvin(cls, kelvin: float) -> "Temperature": """Factory method: crea Temperature desde Kelvin.""" return cls(kelvin + cls.absolute_zero_celsius) def to_fahrenheit(self) -> float: return self.celsius * 9 / 5 + 32 def __repr__(self) -> str: return f"Temperature({self.celsius:.2f}°C)" # Crear instancias desde distintas unidades t1 = Temperature(100) t2 = Temperature.from_fahrenheit(212) t3 = Temperature.from_kelvin(373.15) print(t1) # Temperature(100.00°C) print(t2) # Temperature(100.00°C) print(t3) # Temperature(100.00°C) # También funciona sobre instancias (aunque es raro usarlo así) t4 = t1.from_fahrenheit(32) print(t4) # Temperature(0.00°C)
SalidaTemperature(100.00°C) Temperature(100.00°C) Temperature(100.00°C) Temperature(0.00°C)

@staticmethod

Un @staticmethod no recibe ni self ni cls. Es simplemente una función que vive dentro de la clase por razones organizativas — su lógica está relacionada con la clase, pero no necesita acceder a ningún estado de ella.

class MathUtils: PI: float = 3.141592653589793 @staticmethod def is_prime(n: int) -> bool: """Verifica si n es primo — no necesita ni self ni cls.""" if n < 2: return False for i in range(2, int(n ** 0.5) + 1): if n % i == 0: return False return True @staticmethod def gcd(a: int, b: int) -> int: """Máximo común divisor — algoritmo de Euclides.""" while b: a, b = b, a % b return a @staticmethod def lcm(a: int, b: int) -> int: """Mínimo común múltiplo.""" return abs(a * b) // MathUtils.gcd(a, b) # Se puede llamar sin instancia print(MathUtils.is_prime(17)) # True print(MathUtils.is_prime(18)) # False print(MathUtils.gcd(48, 18)) # 6 print(MathUtils.lcm(4, 6)) # 12 # O sobre una instancia (aunque es inusual para staticmethods) util = MathUtils() print(util.is_prime(97)) # True
SalidaTrue False 6 12 True

Factory methods con @classmethod

El patrón factory method con @classmethod es la forma idiomática de ofrecer múltiples constructores. Usar cls(...) en vez del nombre de la clase hace que funcione correctamente con la herencia.

from datetime import date class Person: def __init__(self, name: str, birth_year: int) -> None: self.name = name self.birth_year = birth_year @classmethod def from_birth_date(cls, name: str, birth_date: date) -> "Person": """Crea una persona a partir de una fecha de nacimiento completa.""" return cls(name, birth_date.year) @classmethod def from_string(cls, data: str) -> "Person": """Parsea 'nombre,año' para crear una Person.""" name, year_str = data.split(",") return cls(name.strip(), int(year_str.strip())) @property def age(self) -> int: return date.today().year - self.birth_year def __repr__(self) -> str: return f"Person('{self.name}', {self.birth_year})" # Tres formas de crear el mismo objeto p1 = Person("Ana", 1990) p2 = Person.from_birth_date("Ana", date(1990, 6, 15)) p3 = Person.from_string("Ana, 1990") print(p1) # Person('Ana', 1990) print(p2) # Person('Ana', 1990) print(p3) # Person('Ana', 1990) print(f"Edad: {p1.age} años")
SalidaPerson('Ana', 1990) Person('Ana', 1990) Person('Ana', 1990) Edad: 36 años
Tipo de métodoPrimer argCuándo usarlo
instancia (normal)selfOpera sobre datos del objeto
@classmethodclsFactory, constructores alternativos
@staticmethod(ninguno)Función utilitaria relacionada con la clase

Practica