使用 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 捕获异常的主要内容,如果未能解决你的问题,请参考以下文章