1

考虑 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调用,无论如何都会隐式返回 None —— 对于这种函数来说,这不一定是很好的行为。

为了确定一个可靠的“相信我,你不想返回这个”值,面对没有好的和体面的哨兵(无论如何我都知道),我考虑将默认值设置为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-and-up 状态,因为它在您要求它查看的地方找到了一个 None。然后它还会引发您期望的异常:没有此类方法/ivar的对象的AttributeError,传递的参数过多的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。

是否有更简单的语法来声明有效的“失败”标记,无论是在这种情况下还是在返回值未知或无法保证的其他情况下?

4

1 回答 1

2

根本不要给getattr()默认值;让它提高甚至是一个坏的价值,而不是:AttributeErrorTypeErrormsg

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

或者,对于您的更新版本,如果您传入了参数,则与 re-raise 大致相同:

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
于 2014-05-19T19:17:00.223 回答