是否可以将自定义异常用作哨兵值?

时间:2014-05-19 19:11:01

标签: python python-2.7 sentinel

考虑Python中的一个案例,其中一个使用getattr来动态获取方法或值。

def message(obj, msg, *args, **kwargs):
    result = getattr(obj, msg, None)
    if result is not None:
        try:
            return result(*args, **kwargs)
        except TypeError:
            return result

......只是,坚持 - 这不是一个非常好的行为。即使这是一个错误的getattr调用,无论如何都会隐式返回 - 这对于这种函数来说不一定是很好的行为。

为了确定一个坚实的相信我,你不想回归这个"面对没有好的和体面的哨兵的价值(我知道无论如何),我考虑将getattr的默认值设置为引发异常的函数。通过这种方式,一个糟糕的搜索应该总是显而易见并且被抓住,除非另一个人'决定是可爱的,并使这个无用的哨兵成为一个属性。

class _BadMessageException(Exception):
    pass

def _did_not_find(*args, **kwargs):
    raise BadMessageException

def _raise_right_exception(msg, obj):
    if not isinstance(msg, basestring):
        raise TypeError("Message '{}' was not a string".format(msg))
    else:
        raise AttributeError("Bad message '{}' sent to object '{}'".format(msg, obj))

这样,当消息返回None时,消息总是至少在up-up-up上,因为它在你要求它查找时找到了None。然后它也会引发你期望的异常:没有这种方法/ ivar的对象的AttributeError,传递了太多args的TypeError等等。编辑:当然,我发布了错误的代码摘录第一次。这是纠正后的功能。

def message(obj, msg, *args, **kwargs):
    result = getattr(obj, msg, _did_not_find)
    try:
        return result(*args, **kwargs)
    except TypeError:
        if not args or kwargs:
            return result
        else:
            _raise_right_exception(msg, obj)
    except _BadMessageException:
        _raise_right_exception(msg, obj)

感觉就像许多额外的代码只是为了确保以正确的方式失败。一个引发异常的函数,它只是一个引用首选异常的McGuffin,只是为了安抚eafp半神...... Hrrm。

是否有更简单的语法来声明有效的'失败'哨兵,在这种情况下还是在其他情况下,不知道或保证返回值?

1 个答案:

答案 0 :(得分:2)

根本不要给getattr()默认的 ; 提出AttributeError甚至TypeError以获取错误的msg值,而不是:

>>> getattr(object(), 'foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'foo'
>>> getattr(object(), 42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string

简化了您的方法:

def message(obj, msg, *args, **kwargs):
    result = getattr(obj, msg)
    try:
        return result(*args, **kwargs)
    except TypeError:
        return result

或者,对于您的更新版本,如果您传入参数,则重新加注大致相同:

def message(obj, msg, *args, **kwargs):
    result = getattr(obj, msg)
    try:
        return result(*args, **kwargs)
    except TypeError:
        if not args and not kwargs:
            # assume attribute access was desired
            return result
        raise