使用 cached_property 捕获异常

Posted

技术标签:

【中文标题】使用 cached_property 捕获异常【英文标题】:catching exception with cached_property 【发布时间】:2017-07-14 14:30:43 【问题描述】:

我正在尝试使用 decorator 捕获异常以获取 cached_property https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76

https://pypi.python.org/pypi/cached-property

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

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_cached_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)

【问题讨论】:

【参考方案1】:

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

例子:

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!')]

【讨论】:

【参考方案2】:

嗯,我发现了问题所在,它是 cached_property 的工作方式。为了缓存它,它将值以与其包装的函数相同的名称写入实例。问题是它包装的函数的名称具有来自您的装饰器的名称“包装”。因此,如果您在初始 fo.is_cache_working 之后访问 fo.wrapped,您将获得缓存结果。

没有简单的方法将这两种想法混合在一起。最简单的解决方案是编写自己的 cached_property 来存储自己的值:

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

【讨论】:

或者,在你的装饰器中,使用functools.wraps装饰器来装饰你的wrapped函数。因此,在顶部添加对functools 的导入,并在wrapped 定义之前的行中添加functools.wraps(fn)。这会将wrapped__name__ 更改为与fn 同名,以及其他一些内容。

以上是关于使用 cached_property 捕获异常的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot异常统一处理方法:@ControllerAdvice注解的使用全局异常捕获自定义异常捕获

lua中怎么捕获错误异常信息

c语言异常怎么捕获有几种方式

前端异常类型及捕获方式

全局捕获异常

lua中怎么捕获错误异常信息