2011-06-21 13 views
15

Moje pytanie jest są dwa następujące fragmenty kodu uruchomić samo przez interpreter:python: czy pola właściwości są automatycznie buforowane?

class A(object): 
    def __init__(self): 
    self.__x = None 

    @property 
    def x(self): 
    if not self.__x: 
     self.__x = ... #some complicated action 
    return self.__x 

i znacznie prostszy:

class A(object): 
    @property 
    def x(self): 
     return ... #some complicated action 

czyli jest interpreter wystarczająco inteligentny, aby buforować właściwość x ?

Moje założenie jest takie, że x nie ulega zmianie - ustalenie, że jest to trudne, ale gdy raz go znajdziesz, nie ma powodu, aby go znaleźć ponownie.

Odpowiedz

13

Nie, odbiorca będzie wywoływany za każdym razem, gdy uzyskasz dostęp do nieruchomości.

17

Nie trzeba dodawać memoize dekorator:

class memoized(object): 
    """Decorator that caches a function's return value each time it is called. 
    If called later with the same arguments, the cached value is returned, and 
    not re-evaluated. 
    """ 
    def __init__(self, func): 
     self.func = func 
     self.cache = {} 
    def __call__(self, *args): 
     try: 
     return self.cache[args] 
     except KeyError: 
     value = self.func(*args) 
     self.cache[args] = value 
     return value 
     except TypeError: 
     # uncachable -- for instance, passing a list as an argument. 
     # Better to not cache than to blow up entirely. 
     return self.func(*args) 
    def __repr__(self): 
     """Return the function's docstring.""" 
     return self.func.__doc__ 
    def __get__(self, obj, objtype): 
     """Support instance methods.""" 
     return functools.partial(self.__call__, obj) 

@memoized 
def fibonacci(n): 
    "Return the nth fibonacci number." 
    if n in (0, 1): 
     return n 
    return fibonacci(n-1) + fibonacci(n-2) 

print fibonacci(12) 
+6

memoize dekorator jest chyba przesadą za pomocą prostego nieruchomości Pythona. – SingleNegationElimination

+1

Należy również zauważyć, że w międzyczasie znajduje się dekorator ['functools.lru_cache (maxsize = 128, typed = False)'] (https://docs.python.org/3/library/functools.html#functools.lru_cache) (który zezwala również na parametr 'maxsize = None') –

8

właściwości nie automatycznie buforować ich wartości zwracanych. Polecenie getter (i setery) mają być wywoływane za każdym razem, gdy dostęp do nieruchomości jest możliwy.

Jednak Denis Otkidach napisał wspaniałą buforowane dekorator atrybutu (opublikowany w the Python Cookbook, 2nd edition a także oryginalnie na ActiveState pod PSF license) tylko dla tego celu:

class cache(object):  
    '''Computes attribute value and caches it in the instance. 
    Python Cookbook (Denis Otkidach) https://stackoverflow.com/users/168352/denis-otkidach 
    This decorator allows you to create a property which can be computed once and 
    accessed many times. Sort of like memoization. 

    ''' 
    def __init__(self, method, name=None): 
     # record the unbound-method and the name 
     self.method = method 
     self.name = name or method.__name__ 
     self.__doc__ = method.__doc__ 
    def __get__(self, inst, cls): 
     # self: <__main__.cache object at 0xb781340c> 
     # inst: <__main__.Foo object at 0xb781348c> 
     # cls: <class '__main__.Foo'>  
     if inst is None: 
      # instance attribute accessed on class, return self 
      # You get here if you write `Foo.bar` 
      return self 
     # compute, cache and return the instance's attribute value 
     result = self.method(inst) 
     # setattr redefines the instance's attribute so this doesn't get called again 
     setattr(inst, self.name, result) 
     return result 

Oto przykład demonstrując swoje zastosowanie:

def demo_cache(): 
    class Foo(object): 
     @cache 
     def bar(self): 
      print 'Calculating self.bar' 
      return 42 
    foo=Foo() 
    print(foo.bar) 
    # Calculating self.bar 
    # 42 
    print(foo.bar)  
    # 42 
    foo.bar=1 
    print(foo.bar) 
    # 1 
    print(Foo.bar) 
    # __get__ called with inst = None 
    # <__main__.cache object at 0xb7709b4c> 

    # Deleting `foo.bar` from `foo.__dict__` re-exposes the property defined in `Foo`. 
    # Thus, calling `foo.bar` again recalculates the value again. 
    del foo.bar 
    print(foo.bar) 
    # Calculating self.bar 
    # 42 

demo_cache() 
2

Dekorator z Denis Otkidach wymieniony w odpowiedzi @ unutbu został opublikowany w Python Cookbook O'Reilly's. Niestety O'Reilly nie określa żadnej licencji na przykłady kodu - tak jak nieformalne pozwolenie na ponowne użycie kodu.

Jeśli potrzebujesz dekoratora właściwości z pamięci podręcznej z licencją liberalną, możesz użyć Ken Seehof 's @cached_property z ActiveState code recipes. Jest wyraźnie opublikowany pod numerem MIT license.

def cached_property(f): 
    """returns a cached property that is calculated by function f""" 
    def get(self): 
     try: 
      return self._property_cache[f] 
     except AttributeError: 
      self._property_cache = {} 
      x = self._property_cache[f] = f(self) 
      return x 
     except KeyError: 
      x = self._property_cache[f] = f(self) 
      return x 

    return property(get) 
+1

Dodatkowo, jeśli pracujesz nad projektem Django, możesz zaimportować' @ cached_property' z 'django.utils.functional'. –

7

Python 3.2 roku oferuje wbudowany dekorator, który można wykorzystać do tworzenia pamięci podręcznej LRU:

@functools.lru_cache(maxsize=128, typed=False)

Ewentualnie, jeśli używasz Flask/Werkzeug, nie ma @cached_property dekorator .

Dla Django, spróbuj from django.utils.functional import cached_property

+0

Dostępne również w Django 'from django.utils.functional import cached_property' –

Powiązane problemy