27 · Clases, instancias, atributos, métodos
Define tu primera clase con __init__ y métodos, entiende atributos de instancia vs. de clase y detecta la trampa del atributo mutable que le da a casi todo el mundo.
Una **clase** es un plano; una **instancia** es un objeto concreto construido a partir de ese plano. `__init__(self, ...)` se ejecuta cuando llamas a `ClassName(...)` — su trabajo es adjuntar **atributos de instancia** con `self.x = x`. Cada método recibe `self` como primer argumento para poder acceder y modificar la instancia a la que pertenece.
Sin esto:
Sin clases estás obligado a pasar diccionarios o tuplas sueltos — sin comportamiento adjunto, sin garantías sobre qué claves existen. Todo estimador de sklearn, todo módulo de PyTorch y todo objeto de configuración en código ML de producción es una clase.
Una clase agrupa datos (atributos) y comportamiento (métodos) en un único objeto reutilizable. Defines una clase con class Nombre: y le das un inicializador (__init__) que Python llama automáticamente cuando escribes MiClase(arg1, arg2). Dentro de __init__, self apunta a la nueva instancia que se está creando — self.x = x almacena el valor como atributo de instancia visible para todos los métodos de esa instancia.
Hay dos tipos de atributos:
- Atributos de instancia — definidos dentro de
__init__sobreself. Cada instancia tiene su propia copia. - Atributos de clase — definidos en el cuerpo de la clase (fuera de cualquier método). Todas las instancias comparten el mismo objeto — lo cual es una trampa cuando ese objeto es mutable (lista, dict).
Convenciones de nombres de Python: _un_subrayado_inicial señala "protegido, no tocar desde fuera"; __doble_subrayado_inicial activa el name mangling (Foo.__bar se convierte en _Foo__bar), reduciendo la posibilidad de sobreescritura accidental en subclases. Los métodos mágicos (dunder) como __repr__ y __str__ te permiten controlar cómo se ve un objeto al imprimirlo.
Python (in browser)
`__repr__` es el string orientado al desarrollador — debe ser inequívoco e idealmente reproducir el objeto cuando se pega en un shell de Python. `__str__` es el string orientado al usuario; si está ausente Python recurre a `__repr__`. Escribir solo `__repr__` te da ambos gratis en la mayoría de contextos.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
Cada llamada a `LinearModel(...)` crea un objeto completamente nuevo. `m1.bias = 5.0` modifica solo `m1` — `m2` queda completamente intacta. Este es el punto central de los atributos de instancia.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
Tanto `a.history` como `b.history` apuntan a la **misma lista**. Añadir un elemento a través de cualquier instancia muta el objeto compartido. Esto casi nunca es lo que se desea.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
¿Qué devuelve `__init__`?
Construye una clase desde cero en el Arena. Enfócate en `__init__`, atributos de instancia y al menos un método significativo.
- `class Nombre:` define un plano. `__init__(self, ...)` adjunta atributos de instancia mediante `self.attr = valor`. Cada instancia obtiene su propia copia de los atributos de instancia.
- Los atributos a nivel de clase se comparten entre todas las instancias — peligroso con defaults mutables (listas, dicts). Mueve los defaults a `__init__` para mantener las instancias independientes.
- `__repr__` devuelve el string orientado al desarrollador. Nomenclatura: `_protegido` es convención; `__mangled` previene colisiones de nombres en subclases.
Cada estimador de sklearn es una clase: `LinearRegression()` crea una instancia, `.fit(X, y)` y `.predict(X)` son métodos, `coef_` e `intercept_` son atributos de instancia escritos por `fit`. Las subclases de `nn.Module` de PyTorch almacenan pesos de capas como atributos de instancia. Los configs de experimentos (tasa de aprendizaje, tamaño de batch, semilla) se representan mejor como instancias de dataclass — clases ligeras con `__init__` y `__repr__` autogenerados.
Si lo quitas: Sin clases pasarías dicts mutables por cada función del pipeline — sin seguridad de tipos, sin encapsulación y sin forma de adjuntar comportamiento (como `.predict()`) junto a los datos sobre los que opera.