2012-04-13 14 views
13

TL; DR: Konieczne zdefiniowanie unikalnego zestawu modułów pobierających i ustawiających dla każdej właściwości() 'd zmienna zasysa. Czy mogę zdefiniować ogólne programy pobierające i ustawiające i używać ich z dowolną zmienną, jakiej potrzebuję?Python: Ogólne programy pobierające i ustawiające

powiedzmy robię lekcje z jakimiś ładnymi pobierające i ustawiające:

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

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

    bar = property(getter, setter) 

Całkiem nieźle, prawda?

Załóżmy teraz, że dodałem inną zmienną o nazwie "baz" i nie chcę, aby ta gra sasquatch/jednorożca została pominięta. Cóż, myślę, że mogę zrobić inny zestaw pobierające i ustawiające:

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) 

Ale to nie jest bardzo suche i niepotrzebnie zaśmieca mój kod. Myślę, że mógłbym zrobić to trochę Suszarka:

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) 

Mimo, że może zrobić mój kod do włosów, to nie jest idealne. Jeśli chciałem unicornify i sasquatchify dwie kolejne zmienne, musiałbym dodać jeszcze cztery funkcje!

Musi być lepszy sposób na zrobienie tego. Czy mogę używać pojedynczego generycznego programu pobierającego i/lub ustawiającego dla wielu zmiennych?

Unicorn mniej i mniej Sasquatch realizacji w świecie rzeczywistym: Używam SQLAlchemy ORM i chcą przekształcić część danych podczas zapisywania i pobierania go z bazy danych. Niektóre transformacje mają zastosowanie do więcej niż jednej zmiennej i nie chcę zaśmiecać swoich klas za pomocą modułów pobierających i ustawiających.

Odpowiedz

12

Jak o właśnie:

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

Nieco bardziej ogólnie:

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) 

Albo, ze ledwo więcej pracy, można skorzystać z pełnego protokołu deskryptora, jak opisano w agf's answer.

+1

Ale lambdas ograniczające ustawiające i pobierające do pojedynczych wyrazów, które nie nadaje się do tego, co muszę zrobić. –

+0

@TheronLuhn Zrób to samo, ale z ogólnymi funkcjami (zdefiniowanymi jako wewnętrzne funkcje 'sasquatchicorn' lub przyjmujące nazwę jako argument). Lub wykonaj pełną deskryptor ofertę, jak w odpowiedzi [agf] (http://stackoverflow.com/a/10149431/344821). – Dougal

+0

A może coś w mojej edycji, która może trochę mniej pracy niż wykonanie pełnego deskryptora? Może? :) – Dougal

5

To właśnie descriptor protocolproperty oparta jest na to za:

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 

Podczas property w funkcji fabrycznego mogą być dobre dla Twojego przykład zabawek, to brzmi jak może trzeba większą kontrolę dla swojego prawdziwego przypadku użycia .

+0

Od pewnego czasu borykam się z tym problemem i być może jestem w błędzie, ale wygląda na to, że to faktycznie zdefiniuje "właściwość" na poziomie klasy współużytkowaną wśród wszystkich instancji klasy. Ta odpowiedź jest pomocna, ale to zachowanie z pewnością nie jest dla mnie. – krs013

+1

@ krs013 Masz rację, podczas gdy pokazał protokół deskryptora, nie był zbyt użyteczny. Zaktualizowałem go, aby przechowywać wartość na instancji, a nie na klasie. – agf

+0

Dzięki, to jest zachowanie, którego szukałem. Gdzie indziej widziałem to tam, gdzie deskryptor uzyskuje bezpośredni dostęp do obiektu '__dict__', ale myślę, że wygląda to czystsze. – krs013

1

Mój kolega zasugerował użycie zamknięć do zwrotu funkcji gettera i setera, a tego właśnie zdecydowałem się użyć.

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 

Ale dziękuję wszystkim za odpowiedzi :)

+1

Jest to zasadniczo takie samo, jak odpowiedź Dougala, ale używa funkcji zamiast lambdas. – Darthfett

+0

Czy to jest? Przepraszam, jestem nowy-toh do Pythona. Przyjmę Dougala. –

1

Korzystanie getattribute i setattr można zdefiniować to dla wszystkich atrybutów przeszłość i przyszłość.

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 

Drukuje:

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 

Daje

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 
Powiązane problemy