按照设计,属性 getter 应该在 python 中抛出异常吗?

By design should a property getter ever throw an exception in python?

对于问题的某些上下文 - 我正在使用延迟加载来推迟 class 中某些属性的完全初始化,直到需要它们(如果有的话),因为它们在计算上可能很昂贵计算。

我的问题是 - 在 python 中,如果在计算 属性 的值时发生错误,或者无法计算该值,引发异常是否可以接受或者是否客观坏主意?

我知道这个问题:Best practices: throwing exceptions from properties - 在复习了其中的要点后实际上改写了这个问题。

但是我真的在寻找关于 python 的明确答案。 例如,即使那个值是 None,getter 也应该总是 return 一个值吗? 在其他语言中,例如c#,对于如何设计属性有明确的建议。

AVOID throwing exceptions from property getters.

Property getters should be simple operations and should not have any preconditions. If a getter can throw an exception, it should probably be redesigned to be a method. Notice that this rule does not apply to indexers, where we do expect exceptions as a result of validating the arguments

http://msdn.microsoft.com/en-us/library/ms229006.aspx

在python中也是如此吗? 属性 真的应该是一种方法吗? 我可以看到在强类型语言(如 c#)中这可能是一个问题,但我不确定这里是否成立。在调试器中测试它按预期工作。

明确地说,我正在测试如下内容。

class A(object):
    def __init__(self):
        self.__x = None

    @property
    def x(self):
        if not self.__x:
            self.__x = calculate_x()
            if not some_test(self.__x):
                # Is this a bad idea?
                raise ValueError('Get x error {}'.format(self.__x))
        return self.__x

    @x.setter
    def x(self, value):
        if not some_test(value):
            raise ValueError('Set x error {}'.format(value))
        self.__x = value

我一直在使用 RTFM 和很多关于属性的信息,但似乎看不到任何使用它或警告反对它的东西。这一切是完全可以接受的还是我从地狱创造了某种可怕的反模式?

我尝试这样做的原因是因为我想到了类似于 "lazy" 描述符的东西,它允许我快速标记属性。例如

from functools import wraps

    class Descriptor(object):
        def __init__(self, func):
            self.func = func
        def __get__(self, obj, type=None):
            value = self.func(obj)
            setattr(obj, self.func.__name__, value)
            return value

    def lazy(func):
        return wraps(func)(Descriptor(func))

然后

class A(object):
    def __init__(self):
        self.__x = None

    @lazy
    def x(self):
        # where calculate_x might raise an exception
        return calculate_x()

我觉得这个问题更适合Software Engineering Stack。 但无论如何:我会尽量避免在 getter 中抛出异常,当一个字段突然吹到你的脸上时,这有点令人惊讶。 但是 C# 也有可以做完全相同的延迟字段 - 所以根据现实世界的经验我会说:尽量避免这个问题,除非有充分的理由进行延迟加载,在这种情况下它可能没问题。

备选方案提供了一个 Initialize 方法,该方法将尝试或(我最喜欢的)添加一个 returns 值的方法,但在第一次使用后在内部使用支持字段来缓存该值。

像这样:

class bla(object):
   expensive = None
   def get_expensive():
      if not expensive:
          expensive =compute_expensive()
      return expensive

经过更多阅读并回到这里,我将回答我自己的问题并拒绝——根据 PEP 8 风格指南,不推荐这样做。

https://www.python.org/dev/peps/pep-0008/

Note 3: Avoid using properties for computationally expensive operations;
the attribute notation makes the caller believe that access is (relatively) 
cheap.

我意识到这在某种程度上解释了 Christian 的回答 - 但我一直在寻找关于这种模式的某种官方文档或指南。