【问题标题】:catching exception with cached_property使用 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)

【问题讨论】:

    标签: python python-decorators python-descriptors


    【解决方案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 同名,以及其他一些内容。
      猜你喜欢
      • 2013-03-24
      • 2012-10-20
      • 1970-01-01
      • 1970-01-01
      • 2014-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多