2011-08-11 4 views
33

Tak więc, staram się wymyślić najlepszy (najbardziej elegancki z najmniejszą ilością kodu) sposób, aby umożliwić nadpisanie określonych funkcji właściwości (np. Po prostu pobierający, po prostu ustawiający itp.) w python. Jestem fanem następującego sposobu tworzenia właściwości, ponieważ wszystkie ich metody są hermetyzowane w tym samym wciętym bloku kodu (łatwiej jest zauważyć, gdzie kończą się funkcje zajmujące się jedną właściwością i funkcje dotyczące następny rozpocząć):Zastępowanie właściwości w pythonie

@apply 
def foo(): 
    """A foobar""" 
    def fget(self): 
     return self._foo 
    def fset(self, val): 
     self._foo = val 
    return property(**locals()) 

Jednakże, jeśli chcę dziedziczą z klasy, która określa właściwości w ten sposób, a potem, powiedzmy, przesłonić funkcję seter foo, wydaje się trudne. Zrobiłem kilka wyszukiwania i większość odpowiedzi, które znalazłem, aby zdefiniować oddzielne funkcje w klasie bazowej (np. getFoo i setFoo), jawnie utworzyć z nich definicję właściwości (np. foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())), a następnie przesłonić getFoo, setFoo i delFoo w razie potrzeby.

Nie podoba mi się to rozwiązanie, ponieważ oznacza to, że muszę zdefiniować lambas dla każdej właściwości, a następnie wypisać każde wywołanie funkcji (kiedy przedtem mogłem właśnie wykonać property(**locals())). Ja także nie otrzymuję enkapsulacji, którą miałem pierwotnie.

Idealnie, co chciałbym, aby móc zrobić byłoby coś takiego:

class A(object): 
    def __init__(self): 
     self.foo = 8 
    @apply 
    def foo(): 
     """A foobar""" 
     def fget(self): 
      return self._foo 
     def fset(self, val): 
      self._foo = val 
     return property(**locals()) 

class ATimesTwo(A): 
    @some_decorator 
    def foo(): 
     def fset(self, val): 
      self._foo = val * 2 
     return something 

I wtedy wyjście będzie wyglądać następująco:

>>> a = A() 
>>> a.foo 
8 
>>> b = ATimesTwo() 
>>> b.foo 
16 

Zasadniczo ATimesTwo dziedziczy getter funkcja od A, ale zastępuje funkcję ustawiania. Czy ktoś wie, jak to zrobić (w sposób, który wygląda podobnie do powyższego przykładu)? Jaką funkcję powinien wyglądać some_decorator, a co powinie zwrócić funkcja foo?

+0

Nie widzę sposobu, aby zrobić to, co chcesz z samym dekoratorem, ponieważ nie ma ono dostępu do obiektu, tylko do funkcji. domyślam się, że masz dekorator, który zwraca funkcję, która działa dalej, gdy metoda jest najpierw wywoływana, ale potem nadpisałeś początkowe foo. także - jak pewnie wiesz - nie używasz języka w zamierzonym stylu ... ale możesz znaleźć sposób, aby to zrobić za pomocą metaklasu. –

+0

To podobne pytanie może być pomocne: http://stackoverflow.com/questions/237432/python-properties-and-inheritance – stderr

Odpowiedz

39

Jestem pewna, że ​​już to słyszałeś, ale apply został przestarzałym przez osiem lat, ponieważ Python 2.3. Nie używaj go. Używanie locals() jest również sprzeczne z zen Python - jawne jest lepsze niż niejawne. Jeśli naprawdę tak zwiększonej wcięcia, nie ma potrzeby tworzenia wyrzucenie przedmiotu, po prostu zrób

if True: 
    @property 
    def foo(self): 
     return self._foo 
    @foo.setter 
    def foo(self, val): 
     self._foo = val 

Które nie nadużywa locals użyć apply wymagają utworzenia dodatkowego przedmiotu lub potrzebujesz linię potem z foo = foo() utrudnia znalezienie końca bloku. Działa równie dobrze dla twojego staroświeckiego sposobu używania property - po prostu rób to jak zwykle.

Jeśli chcesz zastąpić właściwość w dowolnej podklasie, możesz użyć wartości recipe like this.

Jeśli podklasa wie, w którym nieruchomość została zdefiniowana, po prostu zrobić:

class ATimesTwo(A): 
    @A.foo.setter 
    def foo(self, val): 
     self._foo = val * 2 
+1

"' if True: '" ?? –

37

Python docs na property dekoratora sugerują następujące idiom:

class C(object): 
    def __init__(self): 
     self._x = None 
    @property 
    def x(self): 
     return self._x 
    @x.setter 
    def x(self, value): 
     self._x = value 
    @x.deleter 
    def x(self): 
     del self._x 

a następnie podklasy można zastąpić pojedynczym seter/getter w ten sposób:

class C2(C): 
    @C.x.getter 
    def x(self): 
     return self._x * -1 

To jest trochę warty ponieważ nadrzędnym wiele metod wydaje się wymagać, aby zrobić coś takiego:

class C3(C): 
    @C.x.getter 
    def x(self): 
     return self._x * -1 
    # C3 now has an x property with a modified getter 
    # so modify its setter rather than C.x's setter. 
    @x.setter 
    def x(self, value): 
     self._x = value * 2 

Oczywiście w punkcie, że jesteś nadrzędne getter, setter i Deleter można chyba tylko przedefiniować nieruchomości na C3.