2012-04-13 18 views
13

TL; DR: Deve definire un insieme univoco di getter e setter per ciascuna proprietà() 'd variabile fa schifo. Posso definire getter e setter generici e usarli con qualsiasi variabile io voglia?Python: getter e setter generici

Diciamo che faccio una classe con alcuni getter e setter curato:

class Foo 
    def getter(self): 
     return _bar+' sasquatch' 

    def setter(self, value): 
     _bar = value+' unicorns' 

    bar = property(getter, setter) 

Abbastanza grande, giusto?

Ora diciamo che ho messo in un'altra variabile chiamata "baz" e io non voglio che sia lasciato fuori da questo Sasquatch divertente/unicorno. Beh, credo che avrei potuto fare un altro set di getter e setter:

class Foo 
    def bar_getter(self): 
     return _bar+' sasquatch' 

    def bar_setter(self, value): 
     _bar = value+' unicorns' 

    bar = property(bar_getter, bar_setter) 

    def baz_getter(self): 
     return _baz+' sasquatch' 

    def baz_setter(self, value): 
     _baz = value+' unicorns' 

    baz = property(baz_getter, baz_setter) 

ma non è molto secco e inutilmente ingombra il mio codice. Credo che avrei potuto fare un po 'asciugatrice:

class Foo 
    def unicornify(self, value): 
     return value+' unicorns' 

    def sasquatchify(self, value): 
     return value+' sasquatch' 

    def bar_getter(self): 
     return self.sasquatchify(_bar) 

    def bar_setter(self, value): 
     _bar = self.unicornify(_bar) 

    bar = property(bar_getter, bar_setter) 

    def baz_getter(self): 
     return self.sasquatchify(_baz) 

    def baz_setter(self, value): 
     _baz = self.unicornify(_baz) 

    baz = property(baz_getter, baz_setter) 

Anche se questo potrebbe rendere il mio codice asciugatrice, non è l'ideale. Se volessi unicornify e sasquatchify altre due variabili, avrei dovuto aggiungere altri quattro funzioni!

Ci deve essere un modo migliore per farlo. Posso usare un singolo getter generico e/o setter tra più variabili?

Implementazione del mondo reale senza unicard e sasquatch: Sto utilizzando SQLAlchemy ORM e desidero trasformare alcuni dati durante l'archiviazione e il recupero dal database. Alcune delle trasformazioni sono applicabili a più di una variabile, e non voglio ingombrare le mie lezioni con getter e setter.

risposta

12

ne dite solo:

def sasquatchicorn(name): 
    return property(lambda self: getattr(self, name) + ' sasquatch', 
        lambda self, val: setattr(self, name, val + ' unicorns')) 

class Foo(object): 
    bar = sasquatchicorn('_bar') 
    baz = sasquatchicorn('_baz') 

Un po 'più genericamente:

def sasquatchify(val): 
    return val + ' sasquatch' 

def unicornify(val): 
    return val + ' unicorns' 

def getset(name, getting, setting): 
    return property(lambda self: getting(getattr(self, name)), 
        lambda self, val: setattr(self, name, setting(val))) 

class Foo(object): 
    bar = getset('_bar', sasquatchify, unicornify) 
    baz = getset('_baz', sasquatchify, unicornify) 

Oppure, con poco più di lavoro, è possibile utilizzare il protocollo completo descrittore, come descritto in agf's answer.

+1

Ma le lambda limitare il setter e getter per un singolo espressioni, che non è adatto per quello che ho bisogno di fare. –

+0

@TheronLuhn Quindi fai la stessa cosa ma con funzioni generali (definite come funzioni interne di 'sasquatchicorn' o che prendono un nome come argomento). Oppure fai il deal completo del descrittore come nella risposta [agf] (http://stackoverflow.com/a/10149431/344821). – Dougal

+0

Oppure la cosa nella mia modifica, che è forse un po 'meno lavoro di un descrittore completo? Può essere? :) – Dougal

5

Questo è ciò che la descriptor protocolproperty si basa su è per:

class Sasicorn(object):        
    def __init__(self, attr):       
     self.attr = "_" + attr      
    def __get__(self, obj, objtype):     
     return getattr(obj, self.attr) + ' sasquatch' 
    def __set__(self, obj, value):     
     setattr(obj, self.attr, value + ' unicorns') 

class Foo(object):         
    def __init__(self, value = "bar"):    
     self.bar = value        
     self.baz = "baz"        
    bar = Sasicorn('bar')        
    baz = Sasicorn('baz')        

foo = Foo()           
foo2 = Foo('other')         
print foo.bar           
# prints bar unicorns sasquatch 
print foo.baz           
# prints baz unicorns sasquatch 
print foo2.bar          
# prints other unicorns sasquatch 

Mentre property in una funzione di fabbrica può andare bene per il tuo esempio giocattolo, suona come forse avete bisogno di un maggiore controllo per il vostro caso reale utilizzo .

+0

Ho sofferto per un po 'di tempo, e potrei sbagliarmi, ma sembra che questo definisca effettivamente una "proprietà" di livello di classe condivisa tra tutte le istanze della classe. Questa risposta è utile, ma per me questo non è certamente un comportamento. – krs013

+1

@ krs013 Hai ragione, mentre mostrava il protocollo descrittore, non era molto utile. L'ho aggiornato per memorizzare il valore sull'istanza anziché sulla classe. – agf

+0

Grazie, questo è il comportamento che stavo cercando. Altrove l'ho visto dove il descrittore accede direttamente al '__dict__' dell'oggetto, ma penso che questo sia più pulito. – krs013

1

Un mio collega ha suggerito di utilizzare chiusure per restituire le funzioni getter e setter, che è quello che ho deciso di utilizzare.

class Foo(object): 
    def setter(var): 
     def set(self, value): 
      setattr(self, var, value+' unicorn') 
     return set 

    def getter(var): 
     def get(self): 
      return getattr(self, var)+' sasquatch' 
     return get 

    bar = property(getter('_bar'), setter('_bar')) 

f = Foo() 
f.foo = 'hi' 
print f.foo 

Ma grazie a tutti per le vostre risposte :)

+1

Questo è essenzialmente lo stesso della risposta di Dougal, ma usa funzioni invece di lambda. – Darthfett

+0

E '? Scusa, sono new-ish per Python. Accetterò Dougal's. –

1

Uso getattribute e setattr si può definire questo per tutti gli attributi passato e futuro.

class Foo(object):                               

    x = 3 

    def __getattribute__(self, attr): 
    return str(object.__getattribute__(self, attr)) + ' sasquatch' 

    def __setattr__(self, attr, value): 
    object.__setattr__(self, attr, str(value) + ' unicorn') 

print Foo.x 
f = Foo() 
print f.x 
f.y = 4 
print f.y 

Questo stampa:

3 
3 sasquatch 
4 unicorn sasquatch 
-1
# coding=utf-8 
__author__ = 'Ahmed Şeref GÜNEYSU' 


class Student(object): 
    def __init__(self, **kwargs): 
     for k, v in kwargs.iteritems(): 
      self.__setattr__(k, v) 

if __name__ == '__main__': 
    o = Student(first_name='Ahmed Şeref', last_name='GÜNEYSU') 
    print "{0} {1}".format(o.first_name, o.last_name) 
    print o.email 

Ahmed Şeref GÜNEYSU 
    File "/Users/ahmed/PycharmProjects/sanbox/abstract_classes/object_initializer/__init__.py", line 13, in <module> 
    print o.email 
AttributeError: 'Student' object has no attribute 'email' 

Process finished with exit code 137