Python 使用缓存的\u属性捕获异常

Python 使用缓存的\u属性捕获异常,python,python-decorators,python-descriptors,Python,Python Decorators,Python Descriptors,我正在尝试使用装饰器捕获缓存的\u属性的异常 我想做一些简单的事情如下,但这不起作用 from pprint import pprint import time from cached_property import cached_property class MyException(Exception): pass def catch_my_exceptions(fn): def wrapped(*args, **kwargs): try:

我正在尝试使用
装饰器捕获
缓存的\u属性的异常

我想做一些简单的事情如下,但这不起作用

from pprint import pprint
import time
from cached_property import cached_property

class MyException(Exception):
    pass

def catch_my_exceptions(fn):
    def wrapped(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except MyException as e:
            cls = args[0]
            err = 'Found error at {}: {}'.format(time.asctime(), e)
            cls.error_msgs.append(err)
            print(err)
            return
    return wrapped

class Foo(object):
    def __init__(self):
        self.vars = {}

    @cached_property
    @catch_my_exceptions
    def is_cache_working(self):
        self.vars[time.asctime()] = True
        time.sleep(3)
        print('running cache runner')
        return time.asctime()

fo = Foo()
for i in range(3):
    print(fo.is_cache_working)
    pprint(fo.vars)


# This doesn't trigger caching

running cache runner
Thu Feb 23 21:45:15 2017
{'Thu Feb 23 21:45:11 2017': True}
running cache runner
Thu Feb 23 21:45:18 2017
{'Thu Feb 23 21:45:11 2017': True, 'Thu Feb 23 21:45:15 2017': True}
running cache runner
Thu Feb 23 21:45:21 2017
{'Thu Feb 23 21:45:11 2017': True,
 'Thu Feb 23 21:45:15 2017': True,
 'Thu Feb 23 21:45:18 2017': True}



# Current solution that works:
我的解决办法是做以下几点。有人能给我建议一个更好的方法吗。另外,我如何将异常列表传递给此
my\u cached\u decorator

import time
from pprint import pprint
from cached_property import cached_property

class MyException(Exception):
    pass

class my_cached_property(cached_property):
    def __init__(self, func):
        super(self.__class__, self).__init__(func)

    def __get__(self, obj, cls):
        try:
            super(self.__class__, self).__get__(obj, cls)
        except MyException as e:
            err = 'Found error at {}: {}'.format(time.asctime(), e)
            print(err)
            value = obj.__dict__[self.func.__name__] = None
            return value

class Foo(object):
    def __init__(self):
        self.vars = {}

    @my_cached_property
    def is_cache_working(self):
        self.vars[time.asctime()] = True
        time.sleep(3)
        print('running cache runner')
        raise MyException('fooobar')
        return time.asctime()

fo = Foo()
for i in range(3):
    print(fo.is_cache_working)
    pprint(fo.vars)

这可能不是最好的解决方案,但您可以访问从装饰器返回给调用方的内部函数,也可以访问装饰器闭包中的内部函数

例如:

def decorator(f):
    def wrapper(*args, **kwargs):
        try:
            f(*args, **kwargs)
        except Exception as e:
            wrapper.__dict__.setdefault('errors', []).append(e)
    return wrapper

@decorator
def raiser():
    raise Exception('Oh no!')

> raiser()
> raiser.errors
[Exception('Oh no!')]

好吧,我发现了问题所在,这就是缓存_属性的工作方式。为了缓存它,它将值写入实例,并使用与它包装的函数相同的名称。问题是,它所包装的函数的名称具有来自装饰程序的名称“wrapped”。因此,如果在初始fo.is_cache_工作之后访问fo.wrapped,您将得到缓存结果

要把这两种想法混合在一起是不容易的。最简单的解决方案是编写自己的缓存_属性,将值存储在自身上:

class cached_property(object):
    def __init__(self, func):
        self.func = func
        # you can store other function attributes here - such as __doc__ - if you want
        self.values = {}

    def __get__(self, instance, owner):
        if instance in self.values:
            return self.values[instance]
        else:
            value = self.values[instance] = self.func(instance)
            return value

或者,在decorator中,使用
functools.wrapps
decorator来修饰
wrapped
函数。因此,将导入添加到顶部的
functools
,并在
wrapped
定义之前的行中添加
functools.wrapps(fn)
。这会将
wrapped
\uuu名称\uuuu
更改为与
fn
同名,以及其他一些内容。