Ostatnio przejrzałem istniejącą bazę kodu zawierającą wiele klas, w których atrybuty instancji odzwierciedlają wartości przechowywane w bazie danych. Przefaktoryzowałem wiele z tych atrybutów, aby ich wyszukiwanie w bazie danych zostało odroczone, tj. nie być inicjalizowane w konstruktorze, ale tylko przy pierwszym czytaniu. Te atrybuty nie zmieniają się przez cały okres istnienia instancji, ale są prawdziwym wąskim gardłem w obliczaniu tego pierwszego razu i są dostępne tylko w specjalnych przypadkach. W związku z tym mogą być również buforowane po ich pobraniu z bazy danych (dlatego pasuje to do definicji memoizacji, w której dane wejściowe to po prostu „brak danych wejściowych”).
W kółko wpisuję następujący fragment kodu dla różnych atrybutów w różnych klasach:
class testA(object):
def __init__(self):
self._a = None
self._b = None
@property
def a(self):
if self._a is None:
# Calculate the attribute now
self._a = 7
return self._a
@property
def b(self):
#etc
Czy istnieje już dekorator, który może to zrobić w Pythonie, o którym po prostu nie wiem? A może istnieje dość prosty sposób na zdefiniowanie dekoratora, który to robi?
Pracuję pod Pythonem 2.5, ale odpowiedzi 2.6 mogą nadal być interesujące, jeśli są znacząco różne.
Uwaga
To pytanie zostało zadane, zanim Python zawierał wiele gotowych dekoratorów do tego. Zaktualizowałem go tylko pod kątem poprawnej terminologii.
functools.lru_cache()
.Odpowiedzi:
Do wszelkiego rodzaju świetnych narzędzi używam boltons .
W ramach tej biblioteki masz cachedwłaściwość :
źródło
Oto przykład implementacji leniwego dekoratora nieruchomości:
Sesja interaktywna:
źródło
__get__
@wraps(fn)
poniżej,@property
aby niewraps
functools
Napisałem to dla siebie ... Aby użyć go do prawdziwych, jednorazowo obliczonych leniwych właściwości. Podoba mi się, ponieważ unika przyklejania dodatkowych atrybutów do obiektów, a po aktywacji nie marnuje czasu na sprawdzanie obecności atrybutów itp .:
Uwaga:
lazy_property
Klasa nie jest deskryptorem danych , co oznacza, że jest przeznaczona tylko do odczytu. Dodanie__set__
metody uniemożliwiłoby jej prawidłowe działanie.źródło
fget
drogi@property
. Aby zapewnić niezmienność / idempotencję, musisz dodać__set__()
metodę, która wywołujeAttributeError('can\'t set attribute')
(lub jakikolwiek wyjątek / wiadomość Ci odpowiada, ale to właśnieproperty
wywołuje). To niestety pochodzi z wpływu wydajności ułamka mikrosekundy, ponieważ__get__()
zostanie wywołana przy każdym dostępie niż ciągnięcie wartości fget od dict w drugim i kolejnym dostępem. Moim zdaniem warto zachować niezmienność / idempotencję, która jest kluczowa dla moich przypadków użycia, ale YMMV.Oto wywoływalnym że pobiera opcjonalny argument limitu czasu, w
__call__
można również skopiować nad__name__
,__doc__
,__module__
od nazw func za:dawny:
źródło
property
jest klasą. Deskryptor być dokładne. Po prostu wyprowadzaj z niego i wdrażaj pożądane zachowanie.źródło
To, czego naprawdę chcesz, to dekorator
reify
(powiązany ze źródłem!) Z Pyramid:źródło
:)
pyramid
zależności.Jak dotąd dochodzi do pomieszania terminów i / lub pomieszania pojęć, zarówno w pytaniach, jak iw odpowiedziach.
Leniwa ocena oznacza po prostu, że coś jest oceniane w czasie wykonywania w ostatnim możliwym momencie, gdy potrzebna jest wartość.
Właśnie to robistandardowy(*) Dekorowana funkcja jest obliczana tylko i za każdym razem, gdy potrzebujesz wartości tej właściwości. (zobacz artykuł na Wikipedii o leniwej ocenie)@property
dekorator.(*) Właściwie prawdziwie leniwa ocena (porównaj np. Haskell) jest bardzo trudna do osiągnięcia w Pythonie (i skutkuje kodem, który jest daleki od idiomatyczności).
Zapamiętywanie jest poprawnym określeniem tego, czego szuka pytający. Czyste funkcje, które nie są uzależnione od efektów ubocznych dla oceny wartości powrotu może być bezpiecznie memoized i tam jest rzeczywiście dekorator w functools
@functools.lru_cache
więc bez konieczności pisania własnych dekoratorów, chyba że trzeba specjalizuje zachowanie.źródło
@property
„leniwy” nie ma w tym momencie większego sensu. (Pomyślałem również o memoisacji jako mapie danych wejściowych do danych wyjściowych w pamięci podręcznej, a ponieważ te właściwości mają tylko jedno wejście, nic, mapa wydawała się bardziej złożona niż to konieczne.)Możesz to zrobić przyjemnie i łatwo, budując klasę z natywnej właściwości Pythona:
Możemy użyć tej klasy właściwości jak zwykłej właściwości klasy (obsługuje również przypisywanie elementów, jak widać)
Wartość obliczona tylko za pierwszym razem, a następnie wykorzystaliśmy zapisaną wartość
Wynik:
źródło