根据设计,属性getter是否会在python中抛出异常?

时间:2018-02-14 02:54:58

标签: python exception design-patterns properties getter-setter

对于问题的某些上下文 - 我使用延迟加载来推迟类中某些属性的完全初始化,直到需要它们的时间点(如果有的话),因为它们的计算成本很高。

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

我知道这个问题:Best practices: throwing exceptions from properties - 并且在重复了这个问题之后,实际上重写了这个问题。

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

  

避免从属性getter中抛出异常。

     

属性getter应该是简单的操作,不应该有任何操作   前提条件。如果一个getter可以抛出异常,它应该可能   被重新设计成一种方法。请注意,此规则不适用于   索引器,我们确实期望通过验证的例外情况   参数

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和很多关于属性的东西,但似乎看不到任何使用它或者发出警告的东西。这一切都完全可以接受,还是我从地狱创造了某种怪异的反模式?

我尝试这个的原因是因为我想到了一个像“懒惰”的描述符,它允许我快速标记属性。 e.g。

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

2 个答案:

答案 0 :(得分:1)

我认为这个问题更适合软件工程堆栈。 但无论如何:我会尽量避免在吸气器中抛出异常,当你的脸突然爆炸时,这有点令人惊讶。 但是C#也有Lazy字段可以完全相同 - 所以从现实世界的经验我会说:尽量避免这个问题,除非有充分的理由做一些延迟加载,在这种情况下它可以没问题。

替代方法是提供一个Initialize方法,它会尝试或(我最喜欢)添加一个返回值的方法,但在第一次使用后在内部使用一个支持字段来缓存该值。

像这样:

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

答案 1 :(得分:0)

经过更多阅读并回到此处后,我将回答我自己的问题,并说不 - 根据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.

我意识到这在某种程度上解释了克里斯蒂安的答案 - 但我正在寻找关于这种模式的某种官方文件或指导。