2013-04-26 9 views
11

I amore Python's @property decora il sistema. Mi piace il fatto che tu possa eseguire un codice personalizzato quando chiami aClassObect.attribute. Soprattutto per la convalida dei dati quando imposti un attributo. Tuttavia, una cosa che voglio, ma non riesco a trovarla, è un modo per eseguire codice personalizzato quando si tenta di impostare un attributo che non esiste. Ad esempio, dire che ho ottenuto la seguente classe:Un metodo Python "catch all" per attributi non definiti/non implementati nelle classi

class C(object): 
    def __init__(self): 
     self._x = None 

    @property 
    def x(self): 
     """I'm the 'x' property.""" 
     return self._x 

    @x.setter 
    def x(self, value): 
     self._x = value 

    @x.deleter 
    def x(self): 
     del self._x 

Ora, se ho myObj, che è un'istanza della classe C, e io chiamo myObject.x = 42, verrà eseguito il setter appropriata, che è grande per dati di convalida. Ma questo non impedisce a qualcuno di chiamare myOjbect.b = 47 e creerà felicemente un nuovo attributo chiamato b. C'è un modo per eseguire codice speciale quando si imposta un nuovo attributo? Avrei la possibilità di generare errori per dire qualcosa come "errore, questo attributo non esiste".

+2

Ho una classe che funge da API per un database. Ogni istanza rappresenta una voce. Dopo aver modificato gli attributi di un oggetto, che rappresentano i campi nel database, si chiama 'object.save()' per aggiornare il DB. Preferirei che venisse generato un errore piuttosto che i campi essere omessi in modo silenzioso perché in realtà non esistono nel DB. –

risposta

9

Per elaborare un bit sulla risposta Elazar, è necessario ignorare il metodo magico __setattr__ ed eseguire un controllo per verificare se l'attributo esiste già. In caso contrario, sollevare un'eccezione. Ecco che cosa che potrebbe apparire come per la classe:

def __setattr__(self, name, value): 
    if not hasattr(self, name): # would this create a new attribute? 
     raise AttributeError("Creating new attributes is not allowed!") 
    super(C, self).__setattr__(name, value) 

E 'anche possibile sovrascrivere __getattr__ o __getattribute__ se si vuole affrontare le richieste di attributi inesistenti, ma per il problema specifico che hai descritto questo non è probabilmente necessario.

Si noti che il metodo __setattr__ di cui sopra non funzionerà interamente con la proprietà corrente, a causa del deleter. Se si elimina myObj.x, successivamente getter genererà un'eccezione se si tenta di accedere a x in seguito. Ciò significa che hasattr restituirà False quando si verifica se x è un attributo esistente e quindi il mio __getattr__ non consente di ricrearlo. Se hai davvero bisogno di essere in grado di eliminare (e ricreare) attributi specifici, avrai bisogno di un'implementazione più sofisticata __setattr__ (potrebbe verificare nella classe una proprietà con il nome desiderato, piuttosto che fare affidamento su hasattr).

+0

oh aspetta, ero così sicuro che avrebbe funzionato, ma c'è un problema: questa restrizione si applica ovunque, anche nel costruttore quando sto inizializzando i valori. L'ho appena provato e mi impedisce di usare l'attributo 'AttributeError' nel costruttore. C'è un modo per aggirare questo? –

+0

@ J-bob: Ah, sì. Devi chiamare esplicitamente qualcosa come 'super (C, self) .__ setattr __ (" _ x ", None)' ovunque tu imposti un attributo se vuoi bypassare il controllo. – Blckknght

+0

Ho trovato una buona soluzione. Nella parte superiore del mio file, ho "import inspect". Quindi sostituisco la tua linea, 'if not hasattr (self, name):' con 'if not hasattr (self, name) e inspect.stack() [1] [3]! = '__init __':'. 'inspect.stack' esaminiamo lo stack chiamante, quindi questo codice controlla il nome del metodo che lo chiama. Se il metodo di chiamata è '__init__', consente di creare l'attributo. Altrimenti solleva l'errore. Raccomando di modificare la tua risposta per mostrarlo. –

9

override __getattr__ e __setattr__.

Problemi correlati