2015-11-12 22 views
17

Làm thế nào tôi có thể sử dụng các hàm lru_cache của functools bên trong mà không làm rò rỉ bộ nhớ? Trong ví dụ tối thiểu sau, cá thể foo sẽ không được giải phóng mặc dù không nằm ngoài phạm vi và không có liên kết giới thiệu nào (ngoài lru_cache).Python functools lru_cache với các phương thức lớp: đối tượng giải phóng

from functools import lru_cache 
class BigClass: 
    pass 
class Foo: 
    def __init__(self): 
     self.big = BigClass() 
    @lru_cache(maxsize=16) 
    def cached_method(self, x): 
     return x + 5 

def fun(): 
    foo = Foo() 
    print(foo.cached_method(10)) 
    print(foo.cached_method(10)) # use cache 
    return 'something' 

fun() 

Nhưng foo và do đó foo.big (một BigClass) vẫn còn sống

import gc; gc.collect() # collect garbage 
len([obj for obj in gc.get_objects() if isinstance(obj, Foo)]) # is 1 

Điều đó có nghĩa rằng trường hợp Foo/BigClass vẫn đang cư trú trong bộ nhớ. Ngay cả khi xóa Foo (del Foo) sẽ không phát hành chúng.

Tại sao lru_cache giữ lại đối tượng đó? Không bộ nhớ cache sử dụng một số băm và không phải là đối tượng thực tế?

Cách được khuyến nghị sử dụng lru_caches trong các lớp học là gì?

tôi biết hai cách giải quyết: Use per instance caches hoặc make the cache ignore object (mà có thể dẫn đến kết quả sai, mặc dù)

Trả lời

16

Đây không phải là giải pháp sạch, nhưng nó hoàn toàn trong suốt đối với các lập trình viên:

import functools 
import weakref 

def memoized_method(*lru_args, **lru_kwargs): 
    def decorator(func): 
     @functools.wraps(func) 
     def wrapped_func(self, *args, **kwargs): 
      # We're storing the wrapped method inside the instance. If we had 
      # a strong reference to self the instance would never die. 
      self_weak = weakref.ref(self) 
      @functools.wraps(func) 
      @functools.lru_cache(*lru_args, **lru_kwargs) 
      def cached_method(*args, **kwargs): 
       return func(self_weak(), *args, **kwargs) 
      setattr(self, func.__name__, cached_method) 
      return cached_method(*args, **kwargs) 
     return wrapped_func 
    return decorator 

Nó có các thông số chính xác giống như lru_cache và hoạt động chính xác như nhau. Tuy nhiên, nó không bao giờ vượt qua self đến lru_cache và thay vào đó sử dụng một ví dụ lru_cache.

Các vấn đề liên quan