2014-09-12 10 views
6

Quali sono alcune regole generali per scegliere quali di questi implementare in una determinata classe, in una determinata situazione?Quali sono alcune regole pratiche per decidere tra __get__, __getattr__ e __getattribute__?

Ho letto i documenti, e quindi capisco la differenza tra loro. Piuttosto, sto cercando una guida su come integrare al meglio il loro utilizzo nel mio flusso di lavoro, essendo meglio in grado di notare le opportunità più sottili di usarle e quali usare quando. Quel genere di cose. I metodi in questione sono (a mia conoscenza):

## fallback 
__getattr__ 
__setattr__ 
__delattr__ 

## full control 
__getattribute__ 
##(no __setattribute__ ? What's the deal there?) 

## (the descriptor protocol) 
__get__ 
__set__ 
__delete__ 
+1

Al close-voter: IMX, domande dello studente come "come faccio a decidere tra X e Y?" o "qual è la differenza tra X e Y?" sono davvero due domande - "qual è lo scopo di X?" e "qual è lo scopo di Y?" Forse sarebbe meglio chiedere loro separatamente, ma nessuno dei due è, nella fattispecie, un IMO "troppo ampio", e vi è almeno * qualche * collegamento tra i diversi metodi magici qui. –

risposta

5

__setattribute__ non esiste perché __setattr__ è sempre chiamato. __getattr__ viene chiamato solo per f.x se la ricerca dell'attributo non riesce tramite il canale normale (che è fornito da __getattribute__, in modo che la funzione venga chiamata sempre allo stesso modo).

Il protocollo descrittore è leggermente ortogonale rispetto agli altri.Dato

class Foo(object): 
    def __init__(self): 
     self.x = 5 

f = Foo() 

le seguenti condizioni:

  • f.x sarebbe invocare f.__getattribute__('x') se fosse definito.
  • f.x non invoca f.__getattr__('x') se è stato definito.
  • f.y sarebbe invocare f.__getattr__('y') se sono stati definiti, oppure f.__getattribute__('y') se si sono stati definiti.

Il descrittore viene richiamato da un attributo, piuttosto che per un attributo. Cioè:

class MyDescriptor(object): 
    def __get__(...): 
     pass 
    def __set__(...): 
     pass 

class Foo(object): 
    x = MyDescriptor() 

f = Foo() 

Ora, f.x causerebbe type(f).__dict__['x'].__get__ di essere chiamato, e f.x = 3 chiamerebbe type(f).__dict__['x'].__set__(3).

Cioè, Foo.__getattr__ e Foo.__getattribute__ sarebbero stati utilizzati per trovare ciò f.xriferimenti; una volta ottenuto ciò, f.x produce il risultato di type(f.x).__get__() se definito, e f.x = y invoca f.x.__set__(y) se definito.

(Le chiamate di cui sopra a __get__ e __set__ sono solo approssimativamente corretta, dal momento che ho lasciato fuori i dettagli di quali argomenti __get__ e __set__ effettivamente ricevere, ma questo dovrebbe essere sufficiente per spiegare la differenza tra __get__ e __getattr[ibute]__.)

Metti un altro modo, se MyDescriptor non ha definito __get__, quindi f.x restituirebbe semplicemente l'istanza di MyDescriptor.

+0

Oh .. Vedo ora. Grazie per aver reso quelle connessioni più chiare. C'è un modo per rendere 'x = 3' (con qualifica l'oggetto al quale è collegato il metodo?) – Inversus

+0

* (senza qualificazione ... – Inversus

+0

Ma ora che ci ho giocato un po '. Penso che __getattribute__ sia esattamente quello che mi serve Il mio caso d'uso è quando si trova in una collezione, quindi penso che funzionerà Grazie – Inversus

6

Per __getattr__ vs __getattribute__, si veda per esempio Difference between __getattr__ vs __getattribute__.

__get__ non è realmente correlato. Ho intenzione di citare dal official documentation for the descriptor protocol qui:

Il comportamento predefinito per l'accesso attributo è quello di ottenere, impostare o eliminare l'attributo dal dizionario di un oggetto. Ad esempio, a.x ha una catena di ricerca che inizia con a.__dict__['x'], quindi type(a).__dict__['x'] e continua attraverso le classi base di type(a) esclusi i metaclassi. Se il valore cercato è un oggetto che definisce uno dei metodi descrittori, Python può sovrascrivere il comportamento predefinito e invocare invece il metodo descrittore. Dove questo si verifica nella catena di precedenza dipende da quali metodi descrittori sono stati definiti.

Lo scopo è quello di controllare __get__ cosa accade quando a.x è trovato attraverso quella "catena di ricerca" (ad esempio, per creare un metodo oggetto invece di restituire una funzione normale trovato tramite type(a).__dict__['x']); lo scopo di __getattr__ e __getattribute__ consiste nel modificare la catena di ricerca stessa.

Non c'è __setattribute__ perché c'è solo un modo per impostare in realtà un attributo di un oggetto, sotto il cofano. Potresti voler far accadere altre cose "magicamente" quando viene impostato un attributo; ma lo __setattr__ lo copre. __setattribute__ non è in grado di fornire alcuna funzionalità che non sia già __setattr__.

Tuttavia, la risposta migliore, nella stragrande maggioranza dei casi, è di non pensare nemmeno a utilizzare nessuno di questi. Primo sguardo alle astrazioni di livello superiore, come ad esempio property s, classmethod s e staticmethod s. Se pensi di aver bisogno di strumenti specializzati come questo, e non riesci a capirlo da solo, ci sono buone probabilità di sbagliare nel tuo modo di pensare; ma a prescindere, è meglio pubblicare una domanda più specifica in quel caso.

Problemi correlati