es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿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?

Tags:  ,

Answer

  1. Avatar for 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 llama self, y debe ser el primer argumento en la definición del método.

    Por lo tanto, cambiar la definición de my_fun1 a:

    def my_fun1(self, X):
        return X * 2
    

    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, usar MY_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:

    class TestClass:
        @staticmethod
        def my_fun1(X):
            return X * 2
    
        @staticmethod
        def my_fun2(X):
            return X / 2
    
        MY_FUN = my_fun1
        MY_FUNS = [my_fun1, my_fun2]
        N_X = [3, 4]
    

    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.

Comments are closed.