2009-11-10 17 views
17

me escribió un código como ésteProblema con la fabricación de objetos exigible en Python

>>> class a(object): 
     def __init__(self): 
      self.__call__ = lambda x:x 

>>> b = a() 

que esperaba que un objeto de la clase debe ser objeto invocable, pero finalmente no lo es.

>>> b() 

Traceback (most recent call last): 
    File "<pyshell#5>", line 1, in <module> 
     b() 
TypeError: 'a' object is not callable 
>>> callable(b) 
False 

>>> hasattr(b,'__call__') 
True 
>>> 

No puedo entender por qué. Por favor, ayúdenme.

+0

que estoy tratando de hacer un poco de azúcar sintáctico. Puedes imaginar hacer un generador de funciones dinámicas como este. clase A: b = create_custom_function ("AA") c = crear_función_personalizada ("BB") – kjshim

Respuesta

16

Sp Los métodos sociales se buscan en el tipo tipo (por ejemplo, clase) del objeto que se está operando, no en la instancia específica. Piénselo: de lo contrario, si una clase define __call__ por ejemplo, cuando se llama a la clase, se debe llamar al __call__ ... ¡qué desastre! Pero, afortunadamente, el método especial se busca en el tipo de clase, metaclase AKA, y todo está bien (las "clases heredadas" tenían un comportamiento muy irregular en esto, y es por eso que todos estamos mejor con los nuevos - que son los únicos que quedan en Python 3).

Por lo tanto, si necesita "anulación por instancia" de métodos especiales, debe asegurarse de que la instancia tenga su propia clase única. Eso es muy fácil:

class a(object): 
    def __init__(self): 
     self.__class__ = type(self.__class__.__name__, (self.__class__,), {}) 
     self.__class__.__call__ = lambda x:x 

y tú estás allí. Por supuesto que sería una tontería en este caso, ya que cada instancia termina con el mismo "llamado por instancia" (!) __call__, pero sería útil si realmente necesita reemplazar por instancia de cada instancia individual .

+0

¡Guau! Ese era el problema entre el método nuevo y el antiguo. Gracias por la ingeniosa manera de anular ese – kjshim

+0

@kjshim, de nada, pero no es especialmente inteligente, solo la forma estándar de Python para hacerlo ;-). –

12

__call__ necesita ser definido en la clase, no la instancia

class a(object): 
    def __init__(self): 
     pass 
    __call__ = lambda x:x 

pero la mayoría de la gente probablemente les resulta más fácil de leer para definir el método de la forma habitual

class a(object): 
    def __init__(self): 
     pass 
    def __call__(self): 
     return self 

Si es necesario tener comportamiento diferente para cada instancia que podría hacerlo así

class a(object): 
    def __init__(self): 
     self.myfunc = lambda x:x 

    def __call__(self): 
     return self.myfunc(self) 
+0

¿Qué ocurre si quiero crear una función invocable distinta por instancia en el tiempo de ejecución? ¿Puede darme una manera sutil para eso? Gracias por su respuesta. – kjshim

+0

y, he referido este artículo http://code.activestate.com/recipes/52304/ – kjshim

1

¿Qué tal esto? definir una clase base AllowDynamicCall:

class AllowDynamicCall(object): 
    def __call__(self, *args, **kwargs): 
     return self._callfunc(self, *args, **kwargs) 

Y luego subclase AllowDynamicCall:

class Example(AllowDynamicCall): 
    def __init__(self): 
     self._callfunc = lambda s: s 
Cuestiones relacionadas