2012-05-29 13 views
7

Disclaimer Questo è solo un esercizio di meta-programmazione, non ha uno scopo pratico .La definizione di __getattr__ e __getitem__ su una funzione non ha effetto

Ho assegnato __getitem__ e __getattr__ metodi su un oggetto funzione, ma v'è alcun effetto ...

def foo(): 
    print "foo!" 

foo.__getitem__ = lambda name: name 
foo.__getattr__ = lambda name: name 
foo.baz = 'baz' 

test di integrità che siamo in grado di assegnare proprietà a una funzione:

>>> foo.baz 
'baz' 

Neat. Che ne dici dei "getter magici"?

>>> foo.bar 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'function' object has no attribute 'bar' 

>>> foo['foo'] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'function' object is not subscriptable 

>>> getattr(foo, 'bar') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'function' object has no attribute 'bar' 

È possibile avere un "getter magico" su un oggetto funzione?

risposta

6

No! Assegnazione __getitem__ a un'istanza non funziona su qualsiasi tipo di oggetto:

>>> class A(object): 
... pass 
... 
>>> a = A() 
>>> a.__getattr__ = lambda name: name 
>>> a.foo 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'A' object has no attribute 'foo' 

E non è possibile definire __getattr__ al tipo di funzione built-in:

>>> import types 
>>> types.FunctionType.__getitem__ = lambda name: name 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: can't set attributes of built-in/extension type 'function' 

E non è possibile sottoclasse types.FunctionType:

>>> import types 
>>> class F(types.FunctionType): 
... pass 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: Error when calling the metaclass bases 
type 'function' is not an acceptable base type 
+2

@kindall lo sottolinea qui di seguito, ma lo menzionerò qui in modo che rimanga con la risposta. le classi di nuovo stile non usano la risoluzione del metodo "normale" con '__getattr__' e i suoi amici. 'a.foo' è tradotto internamente in' type (a) .__ getattr __ ('foo') '. Quindi la proprietà '__getattr__' che è memorizzata in' a .__ dict__' non è stata trovata. Spero che abbia senso ...: -/ – colinta

1

AHA! Utilizzare __call__, e avvolgere la funzione in F()

class F(object): 
    def __init__(self, fn): 
     self.__dict__['fn'] = fn 

    def __call__(self, *args, **kwargs): 
     return self.fn(*args, **kwargs) 

    def __getitem__(self, name): 
     return name 

    def __getattr__(self, name): 
     return name 

>>> foo = F(foo) 
>>> f.bar 
'bar' 
>>> f['foo'] 
'foo' 
>>> foo() 
foo! 
+2

Fondamentalmente i tuoi decoratori descrittivi :) –

+1

Sì, e ho giocato con questo come decoratore, ma cose interessanti si rompono ... se decorare un metodo con una classe, quando si chiama il metodo non diventa un metodo vincolato. quindi una "class-as-decorator" non sembra essere la "stessa cosa" di una "funzione-come-decoratrice". Vedremo, posterò i miei risultati qui. – colinta

+0

Sono entrambi identici in ciò che forniscono, entrambi restituiscono un oggetto "callable". La sua classe come decoratori è più pulita e pulita –

2

Almeno su classi di nuovo stile (che sono l'unico tipo in Python 3 e il tipo che si dovrebbe usare in Python 2), Python guarda solo per i metodi di magia sulla classe (e sui suoi antenati), mai sull'istanza. Docs here.

E ovviamente non è possibile modificare il tipo di funzione o derivarne. Come hai notato, tuttavia, qualsiasi classe con un metodo __call__() crea istanze chiamabili, quindi questo è il modo per farlo.

+0

Tutto vero - Volevo condividere un po 'di più della storia completa in modo che chiunque fosse inciampato qui non dovesse andare a curiosare tra i documenti per trovare questa roba. ma sì, le classi new-style chiamano 'type (obj) .__ getattr__'. Che è documentato, certo, ma controintuitivo ... perché non usare solo la normale risoluzione del metodo? – colinta

+0

"La logica dietro questo comportamento si basa su una serie di metodi speciali come' __hash __() 'e' __repr __() 'che sono implementati da tutti gli oggetti, inclusi gli oggetti di tipo .Se la ricerca implicita di questi metodi utilizza il processo di ricerca convenzionale , fallirebbero se invocati sull'oggetto tipo stesso. " – kindall

Problemi correlati