Search code examples
pythonexceptiondesign-patternspropertiesgetter-setter

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?

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. For example should a getter always return a value even if that value is None? In other languages, such as c#, there are clear recommendations on how to design properties.

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

Would the same be true in 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. 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. 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. 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))

then

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

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

Solution

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

    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.