简体   繁体   English

通过设计,属性获取器是否应该在python中引发异常?

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

For some context to the question - I am using lazy loading to defer full initialization of certain properties in a class until the point at which they are needed (if at all) as they can be computationally expensive to calculate. 对于问题的某些上下文-我正在使用延迟加载将类中某些属性的完全初始化推迟到需要它们的点(如果有的话),因为它们的计算量很大。

My question is - in python if when calculating the value of the property an error occurs, or the value can't be calculated, is raising an exception acceptable or is it an objectively bad idea? 我的问题是-在python中,如果在计算属性的值时发生错误,或者无法计算该值,引发异常是可以接受的还是客观上是个坏主意?

I am aware of this question: Best practices: throwing exceptions from properties - and actually reworded this question after going over the points in it. 我知道这个问题: 最佳实践:从属性中抛出异常 -在遍历所有要点后实际上改写了该问题。

However I am really looking for a definitive answer with regards to python. 但是我真的在寻找关于python的明确答案。 For example should a getter always return a value even if that value is None? 例如,即使该值为无,getter还是应该始终返回一个值? In other languages, such as c#, there are clear recommendations on how to design properties. 在其他语言(例如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. 如果getter可以引发异常,则可能应该将其重新设计为一种方法。 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 http://msdn.microsoft.com/en-us/library/ms229006.aspx

Would the same be true in python? 在python中会一样吗? Should this property really be a method? 这个属性真的应该是一种方法吗? I can see how in a strongly typed language such as c# this could be an issue but am not really sure if the holds true here. 我可以看到在像c#这样的强类型语言中,这可能是一个问题,但是我不确定在这里是否成立。 Testing in the debugger it works as expected. 在调试器中测试它是否按预期工作。

To be clear I am testing something like the following. 为了清楚起见,我正在测试类似以下的内容。

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

I have been RTFM and a lot about properties but can't seem to see anything that either uses it or else warns against it. 我曾经做过RTFM,并且有很多关于属性的内容,但是似乎看不到任何使用它或警告它的东西。 Is this all perfectly acceptable or have I created some kind of monstrous antipattern from hell? 这是完全可以接受的,还是我从地狱创建了某种可怕的反模式?

The reason I am trying this is because I was think of something like a "lazy" descriptor that allows me to quickly markup properties. 我尝试此操作的原因是因为我想到了类似“惰性”描述符的内容,该描述符可让我快速标记属性。 eg 例如

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

then 然后

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

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

I think this question is more appropriate for the Software Engineering Stack. 我认为这个问题更适合软件工程堆栈。 But anyway: I would try to avoid throwing exceptions in getters, it's somewhat surprising when a field blows suddenly in your face. 但无论如何:我会尽量避免在getter中抛出异常,当字段突然在您的脸上吹来时,这有点令人惊讶。 But C# also has Lazy fields which can do exactly the same - so from real world experience I would say: try to avoid that problem, unless there are strong reasons to make something lazy loading, in which case it can be ok. 但是C#还具有可以完全相同的Lazy字段-因此,根据现实世界的经验,我会说:尽量避免出现此问题,除非有充分的理由进行某些延迟加载,在这种情况下可以。

Alternatives are providing an Initialize method which would try or (my favorite) add a method which returns the value, but uses a backing field internally to cache the value after the first usage. 备选方案是提供一个Initialize方法,该方法将尝试或(我最喜欢)添加一种返回值的方法,但是在首次使用后在内部使用后备字段来缓存该值。

like this: 像这样:

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

After more reading and coming back to this I am going to answer my own question and say no - this is not recommended based on the PEP 8 Style Guide. 经过更多的阅读并回到这个问题之后,我将回答我自己的问题,并说不-根据PEP 8样式指南,不建议这样做。

https://www.python.org/dev/peps/pep-0008/ 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.

I realise this is paraphrasing Christian's answer somewhat - but I was looking for some kind of official documentation or guidance on this kind of pattern. 我意识到这在某种程度上解释了克里斯蒂安(Christian)的答案-但我一直在寻找某种有关这种模式的官方文档或指南。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM