[Python] Problema con __getattr__

Daniele Varrazzo piro a develer.com
Lun 27 Ott 2008 03:04:34 CET



On Sun, 26 Oct 2008 22:44:41 +0100, Listemessaggi CoPlast
<listemessaggi a coplast.eu> wrote:

> Ma se come detto, faccio l'overloading di questo operatore, cioè con:
> 
> class Commuter:
>     def __init__(self, val):
>         self.val = val
>     def __getattr__(self, nome):
>         print("BO")
> 
> se scrivo:
> 
> c = Commuter(23)
> print(c)
> 
> ottengo:
> 
> BO
> Traceback (most recent call last):
>   File "libreria.py", line 83, in ?
>     print(c)
> TypeError: 'NoneType' object is not callable
> 
> 
> Osservo che ha scritto BO segno che è passato per __getattr__. Ma
> perché?
> Forse viene interpretato come una qualificazione di un attributo di nome 
> "" (senza nome)?
> Se su def __getattr__(self, nome): ci inserisco un print(nome) ottengo:
> __str__
> Ciò mi mette ancora più in confusione.

__getattr__ viene invocato quando un attributo non viene trovato
nell'oggetto. Quando fai "print c", Python chiede a 'c' il metodo __str__,
che viene usato per convertire l'oggetto in stringa. Poiché non hai
definito __str__, questo viene chiesto a __getattr__ (come verifichi).
Nella tua definizione di __getattr__ c'è un "return None" implicito alla
fine: Python riceve qualcosa dal tuo oggetto e pensa che sia il metodo
__str__: quindi prova ad invocarlo. Quello che fa invece è provare a
invocare None(), che risulta nell'errore che riporti. 

Il problema di come hai definito tu __getattr__ è che "va sempre tutto
bene", salvo poi che il runtime pensa di avere un oggetto callable con la
semantica di __str__ mentre invece ha un None. Dovresti implementare il tuo
__getattr__ in maniera da rispondere solo a quello che si aspetta e dare
errore negli altri casi. Per esempio: 
    class Commuter:
        def __init__(self, val):
            self.val = val
        def __getattr__(self, nome):
             print(nome)
             raise AttributeError("%s has no attribute '%s'" 
                 % (self.__class__.__name__, nome))

    >>> print c
    __str__     
    __repr__  
    <__main__.Commuter instance at 0xb78397cc>

    >>> c.ciao
    ciao
    ciao
   
---------------------------------------------------------------------------
    <type 'exceptions.AttributeError'>        Traceback (most recent call
last)

    /home/piro/<ipython console> in <module>()

    <type 'exceptions.AttributeError'>: Commuter has no attribute 'ciao'

> Comunque il mio obiettivo sarebbe quello di usare __radd__ solo che il 
> mio oggetto ha sia la __radd__ che la __getattr__ e quest'ultima fa in 
> modo che la __radd__ non riceva correttamente l'istanza del mio oggetto 
> impedendomi di usarla.

La semantica è quella di invocare __getattr__ solo se l'attributo cercato
non viene trovato, quindi __radd__ non dovrebbe conflittare: dovrebbe
essere cercato *prima* di getattr. Probabilmente, per come poi usi la tua
classe, non è __radd__ ad essere invocato. La strada per il debug l'hai
azzeccata: un print nel __getattr__ va bene. Aggiungi il raise
AttributeError alla fine e scoprirai qual'è il metodo che ti serve.

-- 
Daniele Varrazzo - Develer S.r.l.
http://www.develer.com


Maggiori informazioni sulla lista Python