¿Por qué a veces necesito incluir “self” como argumento al definir funciones en módulos externos de Python?
Digamos que tengo dos funciones de Python definidas en un módulo llamado funcs.py
:
def my_fun1(X):
return X * 2
def my_fun2(X):
return X / 2
<code>
y una clase de datos definida en un archivo llamado `testclass.py`:
</code>python
from dataclasses import dataclass
import funcs
@dataclass
class TestClass:
MY_FUN = funcs.my_fun1
MY_FUNS = [funcs.my_fun1, funcs.my_fun2]
N_X = [3, 4]
<code>
Si luego ejecuto un código de prueba:
</code>python
import numpy as np
from testclass import TestClass
tc = TestClass()
data = [np.random.randn(x, 2) for x in tc.N_X]
print(tc.MY_FUN(data[0]))
<code>
me da el error:
</code>none
TypeError: my_fun1() recibe 1 argumento posicional pero se dieron 2
<code>
Si luego cambio la definición de `my_fun1` a:
</code>python
def my_fun1(X):
<code>
Funciona bien, pero cuando intento:
</code>python
print(tc.MY_FUNS[0](data[0]))
<code>
Obtengo el error:
</code>none
TypeError: my_fun1() falta 1 argumento posicional requerido: 'X'
Puedo solucionar esto eliminando self
de las definiciones y declarando una única función en la clase de prueba como una lista singleton, por ejemplo, MY_FUN = [funcs.my_fun1]
. Sin embargo, esto parece una solución un poco “hacky”, y me gustaría poder declarar una única función o una lista de funciones sin tener que cambiar las definiciones de las funciones más adelante.
¿Hay alguna manera de hacer esto? ¿Por qué Python necesita el argumento self
a veces y otras veces no en este caso?
davy.ai
La razón detrás del
TypeError
se debe al hecho de que el método de la clase no está definido correctamente. En Python, un método de clase siempre toma al menos un argumento, que representa la instancia. Este argumento generalmente se llamaself
, y debe ser el primer argumento en la definición del método.Por lo tanto, cambiar la definición de
my_fun1
a:funciona, porque permite que la instancia
tc
se pase como el primer argumento.Sin embargo, esto crea un problema al intentar llamar a
my_fun1
como un elemento de una lista, ya que el primer argumento esperado seráself
, y no los datos a los que queremos aplicar la función. Entonces, en este caso, usarMY_FUN = [funcs.my_fun1]
es una posible solución.Otra posible solución es definir las funciones como métodos estáticos de la clase, utilizando el decorador
@staticmethod
:De esta manera, los métodos se pueden llamar sin una instancia de la clase, y los argumentos coincidirán con el número esperado.
En última instancia, la elección entre estas alternativas dependerá de los detalles del problema en cuestión y de las preferencias personales en términos de diseño y organización.