Aprende leyendo en orden

Métodos de instancia, de clase y estáticos — Los tres tipos de métodos

Aprende los tres tipos de métodos de Python —de instancia, de clase y estáticos— con diagramas. Recorre el uso de self / cls / sin primer argumento y cuándo elegir cada uno.

La última vez cubrimos la diferencia entre las variables de clase y las de instancia. Cada método que hemos escrito hasta ahora en la forma def method(self, ...) es un método de instancia, pero las clases de Python tienen dos tipos más: métodos de clase y métodos estáticos. Esta vez aclararemos los tres tipos y cómo elegir entre ellos.

Hay tres tipos de métodos

Los métodos de una clase de Python se dividen en tres tipos, distinguidos por lo que toman como su primer argumento:

- Método de instancia: primer parámetro self. Recibe la propia instancia.

- Método de clase: primer parámetro cls. Recibe la propia clase. Decorado con @classmethod.

- Método estático: sin primer parámetro. No toma ni self ni cls. Decorado con @staticmethod.

El tipo determina a qué puede acceder cada método. Solo los métodos de instancia pueden tocar las variables de instancia self.x. Los métodos de clase son la forma de trabajar con variables de clase desde el lado de la clase, y los métodos estáticos son para lógica pura que no toca ninguna de las dos.

A qué pueden acceder los tres tipos de métodos
Método deinstancia (self)vars instancia+ vars clase+ argsMétodo declase (cls)vars clase+ argsMétodoestático (nada)solo argsaccedeaccedeaccede
Los métodos de instancia acceden a vars de instancia + vars de clase + args: a todo. Los métodos de clase acceden a vars de clase + args. Los métodos estáticos acceden solo a args.

Métodos de instancia — Tomando self

La forma def method(self, ...) que has estado escribiendo todo el tiempo es un método de instancia. El primer parámetro self se rellena automáticamente con la instancia desde la que se llamó al método, así que puedes acceder a las variables de instancia como self.name.

Cuando escribes apple.show(), Python lo traduce internamente a Product.show(apple). Puedes pensar en los métodos como "funciones que toman una instancia como su primer argumento".

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def total_with_tax(self, tax_rate):    # método de instancia
        return int(self.price * (1 + tax_rate))

apple = Product("apple", 150)
print(apple.total_with_tax(0.1))  # 165

# Internamente igual que esto
print(Product.total_with_tax(apple, 0.1))  # 165
Cómo self te permite acceder a las variables de instancia
__init__self.price = 150appleprice = 150apple.total_with_tax(0.1)self recibeappleself.price→ 150①set②call③read
__init__ establece apple.price = 150 → ② llamar a apple.method(0.1) pone apple en self → ③ self.price lee el valor establecido en la instancia.

Métodos de clase — @classmethod y cls

Un método de clase se define con el decorador @classmethod, con cls como primer parámetro (un nombre convencional). cls contiene automáticamente la propia clase: en este caso el plano Product.

Usa los métodos de clase cuando quieras leer o escribir una variable de clase o construir y devolver una única instancia (un factory). También puedes llamarlos a través de una instancia (apple.method()), pero llamarlos en la clase como Product.method() es lo estándar.

class Product:
    total_count = 0           # variable de clase

    def __init__(self, name, price):
        self.name = name
        self.price = price
        Product.total_count += 1

    @classmethod              # ① devuelve una variable de clase
    def get_total(cls):
        return f"{cls.total_count} productos registrados"

    @classmethod              # ② construye y devuelve una instancia (factory)
    def from_csv_row(cls, row):
        name, price = row.split(",")
        return cls(name, int(price))

Product("apple", 150)
Product("banana", 80)

print(Product.get_total())             # 2 productos registrados
orange = Product.from_csv_row("orange,120")
print(orange.name, orange.price)        # orange 120
Cómo funciona un método de clase
decorador@classmethoddef get_total( cls):Product.get_total()cls = Product(la clase)cls.total_count(var de clase)declararautovía lookup
Llamar a get_total(cls) (decorado con @classmethod) como Product.get_total() pone automáticamente la propia clase Product en cls. Desde ahí puedes acceder a las variables de clase como cls.total_count.
self vs cls
Método de instancia
  • def total_with_tax(self, tax_rate):
  • Llamada: apple.total_with_tax(0.1)
  • self se convierte en apple
  • Accede a la variable de instancia self.price
Método de clase
  • Decorado con @classmethod
  • def get_total(cls):
  • Llamada: Product.get_total() (desde la clase)
  • cls se convierte en Product
  • Accede a la variable de clase cls.total_count
self es "la instancia desde la que llamaste"; cls es "la propia clase". Cuando el trabajo es puramente sobre variables de clase, pasar por cls hace la intención más clara.

Escribe un método de clase get_total que devuelva "el número de productos creados hasta ahora".

① En class Product:, añade una variable de clase total_count = 0.

② En __init__(self, name, price), después de self.name = name / self.price = price, escribe Product.total_count += 1.

③ Define get_total decorado con @classmethod y primer parámetro cls, que devuelve cls.total_count.

④ Construye Product("apple", 150) y Product("banana", 80), luego print(Product.get_total()) y confirma que muestra 2.

(Si lo ejecutas correctamente, aparecerá una explicación.)

Editor Python

Ejecutar el código para ver el resultado

Métodos estáticos — @staticmethod

Un método estático se define con @staticmethod y no toma ni self ni cls. Es "una función simple que vive en la clase": completa su trabajo usando solo sus argumentos, sin acceso a variables de instancia o de clase.

Encaja en casos donde el trabajo está relacionado con la clase pero no depende de una instancia o estado de clase concretos: validación de entrada, formateo, cálculos puros usando constantes. Úsalo para mantener funciones de utilidad junto a la clase relacionada en lugar de escribirlas como funciones independientes.

class Product:
    def __init__(self, name, price):
        if not Product.is_valid_price(price):
            raise ValueError(f"precio inválido: {price}")
        self.name = name
        self.price = price

    @staticmethod
    def is_valid_price(price):           # sin self, sin cls
        return 0 <= price <= 1_000_000

print(Product.is_valid_price(150))      # True
print(Product.is_valid_price(-1))       # False

# Product("apple", -1)  # ValueError: precio inválido: -1

Los métodos estáticos funcionan desde la clase o desde la instancia

Tanto Product.is_valid_price(150) como apple.is_valid_price(150) funcionan igual. Pero como el método en realidad no usa la instancia, llamarlo en la clase hace la intención más clara. Escribir apple.is_valid_price(...) hace que un lector se detenga y se pregunte "¿depende esto del estado de apple?": mejor evitarlo.

Escribe un método estático para la validación de precios.

① En class Product:, define is_valid_price(price) como un @staticmethod que devuelve 0 <= price <= 1_000_000.

② Al inicio de __init__(self, name, price), escribe if not Product.is_valid_price(price): raise ValueError("precio inválido") antes de asignar self.name / self.price.

③ Confirma que Product("apple", 150) tiene éxito y Product("sea_urchin", -1) falla con ValueError envolviéndolo en try / except.

Editor Python

Ejecutar el código para ver el resultado

Eligiendo entre los tres

Cuál usar se puede decidir mecánicamente basándose en "a qué necesita acceder el método".

- Toca variables de instancia (self.x)método de instancia

- Solo toca variables de clase (cls.x)método de clase

- No toca ninguna (solo impulsado por argumentos)método estático

En caso de duda: primero comprueba si tocas variables de instancia. Si no, comprueba las variables de clase. Si ninguna, estático. Ese orden no te llevará por mal camino.

Flujo de decisión para los tipos de métodos
¿tocaself.x?Método deinstancia¿tocacls.x?Método declaseno tocaningunaMétodoestáticológica puraen la claseNoNousar
Primera ramificación: ¿tocas la variable de instancia self.x? Siguiente: ¿manejas variables de clase? Si ninguna, estático es la elección correcta.
TipoDecorador1er parámetroUso típico
Método de instancianingunoselfMétodos regulares que leen/escriben self.x
Método de clase@classmethodclsLeer/escribir variables de clase, factories
Método estático@staticmethodningunoValidación, formateo, lógica pura

Pon los tres tipos de métodos en una sola clase y siente las diferencias.

① Define class Product: con la variable de clase total_count = 0 y __init__(self, name, price). Al inicio de __init__, lanza ValueError si falla Product.is_valid_price(price); en caso contrario asigna self.name = name / self.price = price y ejecuta Product.total_count += 1.

② Añade un método de instancia total_with_tax(self, tax_rate) que devuelva int(self.price * (1 + tax_rate)).

③ Añade un método de clase get_total(cls) decorado con @classmethod que devuelva cls.total_count.

④ Añade un método estático is_valid_price(price) decorado con @staticmethod que devuelva 0 <= price <= 1_000_000.

⑤ Construye apple = Product("apple", 150) y print tanto apple.total_with_tax(0.1) como Product.get_total().

Editor Python

Ejecutar el código para ver el resultado

Con este artículo has cubierto los tres tipos de métodos —instancia / clase / estático— y cómo elegir entre ellos. Eso completa el conjunto básico de clases, instancias, atributos y métodos en Python. Desde aquí, puedes pasar a los tres pilares de la programación orientada a objetos: encapsulación (controlar la visibilidad de los atributos), herencia (heredar capacidades de otra clase) y polimorfismo (el mismo nombre de método comportándose de forma diferente según el tipo).

QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1¿Qué decorador usas para definir un método estático?

Pregunta 2¿Qué método es el más natural para @classmethod?

Pregunta 3¿Qué métodos se pueden llamar sin crear una instancia? (Elige la única respuesta más apropiada si se aplican varias.)