2012-04-13 16 views
13

TL; DR: Definieren einer eindeutigen Menge von Getter und Setter für jede Eigenschaft() 'd Variable saugt. Kann ich generische Getter und Setter definieren und sie mit der von mir gewünschten Variablen verwenden?Python: Generische Getter und Setter

Lassen Sie uns sagen, dass ich eine Klasse mit einigen netten Getter und Setter machen:

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

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

    bar = property(getter, setter) 

ziemlich groß, nicht wahr?

Jetzt sagen wir, ich habe eine andere Variable namens "baz" und ich will nicht, dass es aus diesem Sasquatch/Einhorn Spaß weggelassen wird. Nun, ich glaube, ich könnte einen anderen Satz von Getter und Setter machen:

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) 

Aber das ist nicht sehr trocken und unnötig meinen Code unübersichtlich. Ich schätze, ich könnte es ein wenig machen DRYer:

Obwohl das meinen Code DRYer machen könnte, ist es nicht ideal. Wenn ich zwei weitere Variablen unicornify und sasquatchifizieren möchte, müsste ich vier weitere Funktionen hinzufügen!

Es muss einen besseren Weg geben, dies zu tun. Kann ich einen einzelnen generischen Getter und/oder Setter für mehrere Variablen verwenden?

Unicorn-less und sasquatch-less reale Implementierung: Ich benutze SQLAlchemy ORM, und möchte einige der Daten beim Speichern und Abrufen von der Datenbank transformieren. Einige der Transformationen sind auf mehr als eine Variable anwendbar, und ich möchte meine Klassen nicht mit Gettern und Sätzen überladen.

Antwort

12

Wie wäre es einfach:

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') 

Etwas allgemeiner:

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) 

Oder mit kaum mehr Arbeit, können Sie den vollständigen Descriptor-Protokoll verwenden kann, wie in agf's answer beschrieben.

+1

Aber die lambdas die Getter und Setter auf eine einzelne Ausdrücke beschränken, die nicht geeignet ist, was ich brauche, um machen. –

+0

@TheronLuhn So machen Sie das Gleiche, aber mit allgemeinen Funktionen (entweder definiert als innere Funktionen von 'Sasquatchicorn' oder die einen Namen als Argument annehmen). Oder machen Sie den vollständigen Descriptor-Deal wie in [agf] (http://stackoverflow.com/a/10149431/344821) Antwort. – Dougal

+0

Oder mach das Ding in meinem Schnitt, was vielleicht etwas weniger Arbeit ist als eine vollständige Beschreibung? Könnte sein? :) – Dougal

5

Dies ist, was die descriptor protocolproperty basiert auf für ist:

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 

Während property in einer Fabrik Funktion kann für Ihr Spielzeug Beispiel in Ordnung, es ist wie vielleicht klingt Sie für Ihre realen Anwendungsfall mehr Kontrolle benötigen .

+0

Ich habe mit dieser für eine Weile gekämpft, und ich könnte falsch liegen, aber es sieht so aus, als würde dies tatsächlich definieren eine Klasse-Ebene "Eigenschaft" unter allen Instanzen der Klasse geteilt. Diese Antwort ist hilfreich, aber dieses Verhalten ist es für mich nicht. – krs013

+1

@ krs013 Sie haben Recht, während es das Deskriptor-Protokoll zeigte, war es nicht sehr nützlich. Ich habe es aktualisiert, um den Wert für die Instanz und nicht für die Klasse zu speichern. – agf

+0

Danke, das ist das Verhalten, nach dem ich gesucht habe. Anderswo hatte ich es gesehen, wo der Deskriptor direkt auf das '__dict__' des Objekts zugreift, aber ich denke, das sieht sauberer aus. – krs013

1

Ein Kollege von mir schlug vor, Verschlüsse zu verwenden, um Getter- und Setter-Funktionen zurückzugeben, was ich entschieden habe zu verwenden.

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 

Aber ich danke Ihnen allen für Ihre Antworten :)

+1

Das ist im Wesentlichen das gleiche wie Dougals Antwort, verwendet aber Funktionen anstelle von Lambdas. – Darthfett

+0

Ist es? Tut mir leid, ich bin neu bei Python. Ich werde Dougals akzeptieren. –

1

Mit getattribute und setattr Sie können dies definieren für alle vergangenen und zukünftigen Attribute.

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 

Diese Drucke:

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 

Gibt

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