2010-07-07 17 views
6

Tal vez me sale completamente mal pero me encontré con algunos problemas extraños con lambda calling @classmethod.lambda calling @classmethod falla

que tienen la clase siguiente:

class MyClass: 
    LAMBDA = lambda: MyClass.ClassMethod() 

    @classmethod 
    def ClassMethod(cls): 
    pass 

Pero esto no siempre LAMBDA se llama con:

TypeError: unbound method <lambda>() must be called with MyClass instance as first argument (got nothing instead) 

No entiendo realmente por qué esto es así. Ya he pasado un tiempo tratando de hacerlo funcionar. Necesito que algunos atributos de clase estén ocupados por esa lambda y la auto-referencia de la clase obviamente no es posible en esa etapa.

+0

FYI, Esto funciona en Python 3, pero no en Python 2. – kennytm

+0

@KennyTM, Funciona en Python 3 por coincidencia. Python 3 eliminó el método enlazado y simplemente devuelve la función en sí. Si bien esta es una buena decisión de diseño, conceptualmente es dudoso utilizarla para crear métodos estáticos falsos. –

Respuesta

2

usted debe entender, que su código es exactamente equivalente a

class MyClass: 

    def LAMBDA(): 
    return MyClass.ClassMethod() 

    @classmethod 
    def ClassMethod(cls): 
    pass 

Es, pues, al parecer, como lo llama MyClass.LAMBDA(). Pero mira, LAMBDA es un método de instancia. Y lo está llamando sin una instancia real en su lugar.

¿Qué estás tratando de hacer exactamente?

Creo que si le está dando un nombre a una construcción lambda, también podría declararlo como el modo "normal": def. Desde mi experiencia, solo hay unos pocos casos aislados cuando el uso de lambas realmente contribuye a la calidad del código.

+0

LAMBDA es un * miembro de clase * si así se define. No dejes que el 'lambda' te distraiga. Considere 'LAMBDA = 4'. – kennytm

+0

bah, esa falta de 'retorno' fue una verdadera lástima – shylent

+2

@KennyTM," miembro de la clase "no es terminología de Python, por lo que parece una plataforma extraña para elegir. Cuando shylent dice que es un método, lo dice como lo hacemos constantemente: está hablando de lo semántico, no del nombre. –

2

LAMBDA aquí es solo un método normal. Cuando lo busca en la clase, obtiene un método sin enganchar, que es una especie de tontería que toma su función e impone que, aunque self aún no ha sido aprobado, el primer argumento debe ser una instancia de la clase en cuestión. Para confirmarlo, LAMBDA = lambda: MyClass.ClassMethod() no es diferente de def LAMBDA(): return MyClass.ClassMethod(). En el último caso, es más claro que tiene una especie de definición de método quebrado. Una función lambda y una función def forman exactamente los mismos tipos de objetos, y Python los convierte en métodos con las mismas reglas sobre la marcha en el momento de la búsqueda.

Sospecho que el código es posible que desee es

class MyClass(object): 
    @classmethod 
    def ClassMethod(cls): 
     pass 
MyClass.LAMBDA = MyClass.ClassMethod 

o

class MyClass(object): 
    @classmethod 
    def ClassMethod(cls): 
     pass 

    @classmethod 
    def LAMBDA(cls): 
     return cls.ClassMethod() 

(Nota este último se podría escribir con una lambda como lo hizo originalmente, pero no hay razón para hacerlo. Yo nunca usaría = lambda en ninguno de mis códigos, personalmente. También tenga en cuenta que ClassMethod y LAMBDA no siguen las reglas normales de estilo de capitalización de Python.)

0

Necesito algunos atributos de clase llenados por esa lambda y la auto-referencia de la clase obviamente no es posible en esa etapa.

Pero a partir de su código dado, todo LAMBDA hará cuando se le llama es en sí llaman MyClass.ClassMethod(), lo que significa que el método de hacer el poblamiento es ClassMethod (esto es, por supuesto, asumiendo que el cuerpo de ClassMethod no es en realidad esa única pass que ha mostrado, porque si está haciendo todo esto es un ejercicio inútil).A menos que haya algún aspecto de su código que le impida referirse a la clase antes de usarlo para nada, ¿por qué no puede llamar al MyClass.ClassMethod() después de la definición de la clase?

Y si está tratando de modificar los atributos de la clase dentro de la definición (es decir, llamando a LAMBDA dentro de la definición de MyClass, fuera de cualquier método/función/lo que sea), tal vez quiera echarle un vistazo al sección de la documentación sobre metaclasses.

En una nota lateral, si desea asignar atributos a una clase, ni siquiera necesita hacerlo dentro del cuerpo de la clase.

>>> class cls(object): # or just "class cls:" 
...  pass 
... 
>>> cls.value = 10 
>>> cls.name = 'class' 
>>> cls.value 
10 
>>> cls.name 
'class' 
>>> dir(cls) 
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'name', 'value'] 
1

Hay otra solución directa para esto. Tal vez esto no sea lo más limpio, pero creo que es lo que te estabas preguntando. Me encontré con una situación similar yo mismo. Puede desvincularlo deliberadamente con staticmethod().

Aquí está la versión errónea:

def bar(): return 0 

class foo(object): 
    x = lambda:bar() 
    @classmethod 
    def grok(klass): 
    return klass.x() 

foo().grok() 

Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "<console>", line 4, in grok 
TypeError: unbound method <lambda>() must be called with foo instance as first argument (got nothing instead) 

Y la versión ligeramente ajustado:

def bar(): return 0 

class foo(object): 
    x = staticmethod(lambda:bar()) 
    @classmethod 
    def grok(klass): 
    return klass.x() 

foo().grok() 

0 

Tenga en cuenta que las dos anteriores también ocurrir por:

x = bar 

lugar. Solo estaba incluyendo el lambda para ser una respuesta directa a la pregunta. Todas las sugerencias anteriores también funcionan, esta es solo una forma directa de alterar el código lo menos posible.

¿En cuanto a por qué uno querría hacer algo como el trabajo anterior con lambda? En mi caso, estaba haciendo una clase de fábrica que tenía controladores de eventos que de vez en cuando utilizaban datos de toda la clase para obtener información, y parte de esa información era de naturaleza de valor o invocable. (Esto era para un formulario de Django). Por lo tanto, el "invocable" era una lambda directa entregada por el usuario, que se tenía que llamar con self.value() o klass.value(), pero value era una función independiente (lambda o de otro modo).