2009-11-10 9 views
17

Ho scritto il codice come questoProblema con fare oggetto invocabile in python

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

>>> b = a() 

Mi aspettavo che un oggetto della classe dovrebbe essere oggetto invocabile, ma alla fine non lo è.

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

Non riesco a capire perché. Per favore aiutatemi.

+0

Sto cercando di fare un po 'di zucchero syntatic. Puoi immaginare di creare un generatore di funzioni dinamiche come questo. classe A: b = create_custom_function ("AA") c = create_custom_function ("BB") – kjshim

risposta

16

Sp i metodi ecial sono cercati su tipo (ad esempio, classe) dell'oggetto su cui si sta operando, non sull'istanza specifica. Pensaci: altrimenti, se una classe definisce __call__ ad esempio, quando viene chiamata la classe, dovrebbe chiamarsi __call__ ... che disastro! Ma fortunatamente il metodo speciale è invece cercato sul tipo della classe, il metaclasse AKA, e tutto va bene ("le classi legacy" avevano un comportamento molto irregolare in questo, ed è per questo che stiamo tutti meglio con quelli di nuova generazione - quali sono gli unici rimasti in Python 3).

Quindi, se è necessario "sovrascrivere per istanza" di metodi speciali, è necessario assicurarsi che l'istanza abbia una propria classe univoca. È molto semplice:

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

e tu sei lì. Ovviamente in questo caso sarebbe sciocco, dato che ogni istanza finisce con la stessa "cosiddetta peristanza" (!) __call__, ma sarebbe utile se fosse davvero necessario eseguire l'override su base di istanza individuale .

+0

Wow! Questo era il problema tra il metodo nuovo e quello vecchio stile. Grazie per il modo intelligente di ignorare quello – kjshim

+0

@kjshim, sei il benvenuto, ma non è particolarmente intelligente, solo il modo standard di Python per farlo ;-). –

12

__call__ deve essere definito sulla classe, non l'istanza

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

ma la maggior parte delle persone probabilmente trovano più leggibile per definire il metodo di solito modo

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

Se avete bisogno di avere diverso comportamento per ogni istanza si potrebbe fare come questo

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

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

Che cosa devo fare per creare distinti per-esempio la funzione callable in fase di esecuzione? Puoi darmi un modo suggestivo per questo? Grazie per la risposta. – kjshim

+0

e, ho riferito questo articolo http://code.activestate.com/recipes/52304/ – kjshim

1

Che dire di questo? Definire una classe di base AllowDynamicCall:

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

E poi sottoclasse AllowDynamicCall:

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