代码之家  ›  专栏  ›  技术社区  ›  fbparis

如何从函数中动态删除装饰器?

  •  0
  • fbparis  · 技术社区  · 5 年前

    我找到了一种激活它的方法:

    (...)
    setattr(self, "_greedy_function", my_cache_decorator(self._cache)(getattr(self, "_greedy_function")))
    (...)
    

    哪里 self._cache self._greedy_function

    它工作得很好,但是现在如果我想停用缓存并“取消装饰”呢 _greedy_function ?

    _贪心函数 在装饰它之前,也许有一种方法可以从修饰函数中检索它,这样会更好。

    import logging
    from collections import OrderedDict, namedtuple
    from functools import wraps
    
    logging.basicConfig(
        level=logging.WARNING,
        format='%(asctime)s %(name)s %(levelname)s %(message)s'
    )
    
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)
    
    CacheInfo = namedtuple("CacheInfo", "hits misses maxsize currsize")
    
    def lru_cache(cache):
        """
        A replacement for functools.lru_cache() build on a custom LRU Class.
        It can cache class methods.
        """
        def decorator(func):
            logger.debug("assigning cache %r to function %s" % (cache, func.__name__))
            @wraps(func)
            def wrapped_func(*args, **kwargs):
                try:
                    ret = cache[args]
                    logger.debug("cached value returned for function %s" % func.__name__)
                    return ret
                except KeyError:
                    try:
                        ret = func(*args, **kwargs)
                    except:
                        raise
                    else:
                        logger.debug("cache updated for function %s" % func.__name__)
                        cache[args] = ret
                        return ret
            return wrapped_func
        return decorator
    
    class LRU(OrderedDict):
        """
        Custom implementation of a LRU cache, build on top of an Ordered dict.
        """
        __slots__ = "_hits", "_misses", "_maxsize"
    
        def __new__(cls, maxsize=128):
            if maxsize is None:
                return None
            return super().__new__(cls, maxsize=maxsize)
    
        def __init__(self, maxsize=128, *args, **kwargs):
            self.maxsize = maxsize
            self._hits = 0
            self._misses = 0
            super().__init__(*args, **kwargs)
    
        def __getitem__(self, key):
            try:
                value = super().__getitem__(key)
            except KeyError:
                self._misses += 1
                raise
            else:
                self.move_to_end(key)
                self._hits += 1
                return value
    
        def __setitem__(self, key, value):
            super().__setitem__(key, value)
            if len(self) > self._maxsize:
                oldest, = next(iter(self))
                del self[oldest]
    
        def __delitem__(self, key):
            try:
                super().__delitem__((key,))
            except KeyError:
                pass
    
        def __repr__(self):
            return "<%s object at %s: %s>" % (self.__class__.__name__, hex(id(self)), self.cache_info())
    
        def cache_info(self):
            return CacheInfo(self._hits, self._misses, self._maxsize, len(self))
    
        def clear(self):
            super().clear()
            self._hits, self._misses = 0, 0
    
        @property
        def maxsize(self):
            return self._maxsize
    
        @maxsize.setter
        def maxsize(self, maxsize):
            if not isinstance(maxsize, int):
                raise TypeError
            elif maxsize < 2:
                raise ValueError
            elif maxsize & (maxsize - 1) != 0:
                logger.warning("LRU feature performs best when maxsize is a power-of-two, maybe.")
            while maxsize < len(self):
                oldest, = next(iter(self))
                print(oldest)
                del self[oldest]
            self._maxsize = maxsize
    

    编辑: https://gist.github.com/fbparis/b3ddd5673b603b42c880974b23db7cda (kik.set_缓存()方法…)

    0 回复  |  直到 5 年前
        1
  •  2
  •   Davis Herring    5 年前

    现代版 functools.wraps 将原始功能安装为 __wrapped__ 在他们创造的包装上。(一个人可以搜索 __closure__ 对于通常用于此目的的嵌套函数,但也可以使用其他类型)。

    另一种选择是 永久的 旗帜 保持其状态 例如 ,也可以是包装器本身的属性。

        2
  •  4
  •   gdlmx    5 年前

    你把事情搞得太复杂了。装饰器可以通过 del self._greedy_function . 没有必要 __wrapped__ 属性。

    下面是 set_cache unset_cache 方法:

    class LRU(OrderedDict):
        def __init__(self, maxsize=128, *args, **kwargs):
            # ...
            self._cache = dict()
            super().__init__(*args, **kwargs)
    
        def _greedy_function(self):
            time.sleep(1)
            return time.time()
    
        def set_cache(self):
            self._greedy_function = lru_cache(self._cache)(getattr(self, "_greedy_function"))
    
        def unset_cache(self):
            del self._greedy_function
    

    使用你的装饰师 lru_cache ,以下是结果

    o = LRU()
    o.set_cache()
    print('First call', o._greedy_function())
    print('Second call',o._greedy_function()) # Here it prints out the cached value
    o.unset_cache()
    print('Third call', o._greedy_function()) # The cache is not used
    

    输出

    First call 1552966668.735025
    Second call 1552966668.735025
    Third call 1552966669.7354007