【问题标题】:Handle exceptions with decorator on properties in Python在 Python 中使用装饰器处理异常
【发布时间】:2016-04-05 09:54:46
【问题描述】:

我有一个属性,它有一个断言来检查一个值是否是 str 类型。

为了捕捉这个assertionError,我根据我在网上找到的示例创建了一个装饰器。

装饰者:

def catch_assertionerror(function):
    def handle_problems(*args, **kwargs):
        try:
            return function(*args, **kwargs)
        except AssertionError:
            # log_error(err.args[0])
            print "error caught"
    return handle_problems

属性:

@catch_assertionerror
@name.setter
def name(self, value):
    assert isinstance(value, str), "This value should be a string"
    self._name = name

设置名称属性:

self.name = self.parse_name_from_xml()

当我运行这段代码时,没有显示错误,所以我猜它被捕获了,但另一方面,错误消息没有打印到屏幕上。

然后我尝试了一个在 Stachoverflow 上找到的更简单的示例:

def handleError(function):
    def handleProblems():
        try:
            function()
        except Exception:
            print "Oh noes"
    return handleProblems


@handleError
def example():
    raise Exception("Boom!")

这也处理了错误,但没有将错误消息打印到屏幕上。

有人可以向我解释一下我在这里缺少什么吗?

【问题讨论】:

  • 你没有显示调用函数的代码
  • 我刚刚在交互式 python 会话中尝试了您的“更简单的示例”,它按预期工作。你是如何运行这段代码的?

标签: python decorator


【解决方案1】:

你的后一个例子对我有用,但你的主要问题在于你没有用catch_assertionerror 包装函数

@catch_assertionerror
@name.setter
def name(self, value):
    assert isinstance(value, str), "This value should be a string"
    self._name = name

但是descriptor。更糟糕的是,您返回的是一个函数,而不是包装原始描述符的新描述符。现在,当您分配给 name 属性时,您只需将包装函数替换为分配的值。

一步一步,使用你原来的类定义:

class X(object):

    @property
    def name(self):
        return self._name

    @catch_assertionerror
    @name.setter
    def name(self, value):
        assert isinstance(value, str), "This value should be a string"
        self._name = value

>>> x = X()
>>> x.name
<unbound method X.handle_problems>
>>> x.__dict__
{}
>>> x.name = 2
>>> x.name
2
>>> x.__dict__
{'name': 2}

您必须做的是包装方法函数,然后将其传递给描述符处理装饰器:

class X(object):
    @property
    def name(self):
        return self._name
    @name.setter
    @catch_assertionerror
    def name(self, value):
        assert isinstance(value, str), "This value should be a string"
        self._name = value

等等:

>>> x = X()
>>> x.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in name
AttributeError: 'X' object has no attribute '_name'
>>> x.name = 2
error caught
>>> x.name = "asdf"
>>> x.name
'asdf'

将来考虑使用functools.wrapsfunctools.update_wrapper。没有它们,你的类和函数就更难检查了,因为你的包装器会隐藏原来的:

>>> @catch_assertionerror
... def this_name_should_show(): pass
... 
>>> this_name_should_show
<function handle_problems at 0x7fd3d69e22a8>

这样定义你的装饰器:

def catch_assertionerror(function):
    @wraps(function)
    def handle_problems(*args, **kwargs):
        ...
    return handle_problems

将保留原始函数的信息:

>>> @catch_assertionerror
... def this_name_should_show(): pass
... 
>>> this_name_should_show
<function this_name_should_show at 0x7fd3d69e21b8>

在您的情况下,它也会向您表明存在问题:

# When trying to define the class
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in X
  File "<stdin>", line 2, in catch_assertionerror
  File "/usr/lib/python2.7/functools.py", line 33, in update_wrapper
    setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'property' object has no attribute '__module__'

【讨论】:

    猜你喜欢
    • 2022-01-05
    • 2020-01-10
    • 2016-12-14
    • 2017-09-20
    • 1970-01-01
    • 2013-12-31
    • 1970-01-01
    • 2019-10-05
    • 2012-03-27
    相关资源
    最近更新 更多