Clean way to not get object back from instantiation attempt gone bad

Steven D'Aprano steve at REMOVEME.cybersource.com.au
Tue Aug 15 21:19:08 EDT 2006


On Tue, 15 Aug 2006 16:04:12 -0700, tobiah wrote:

> Suppose I do:
> 
> 
> myfoo = Foo('grapes', 'oranges')
> 
> And in the __init__() of Foo, there is
> a real problem with the consumption of fruit.
> Is there a clean way to ensure that myfoo
> will be None after the call?

I don't believe so.

Generally, in the event of an error, you should raise an exception:

class Foo():
    def __init__(self, *fruits):
        if "grapes" in fruits:
            raise AllergyError("I'm allergic to grapes")
        # process fruits

Then handle the exception:

try:
    myfoo = Foo('grapes', 'oranges')
except AllergyError:
    # recover gracefully
    handle_failed_instance()


If you wish, you can wrap it in a function:

def safe_foo(*fruits):
    try:
        return Foo(*fruits)
    except AllergyError:
        return None

myfoo = safe_foo('grapes', 'oranges')

The disadvantage of this is now your code has to be sprinkled with a
million tests "if myfoo is not None: process(myfoo)".



> Would the
> __init__() just do del(self), or is there
> a better way to think about this?


An alternative is to use a propagating "not a Foo" Foo object, like NaNs
and INFs in floating point.

class Foo():
    def __init__(self, *fruits):
        if "grapes" in fruits:
            self._state = "failed" # NaNs use special bit patterns
        else:
            # process fruits
            self._state = "okay"
    def method(self, *args):
        if self._state != "failed":
            # process instance
        else:
            pass  # do nothing
    def __add__(self, other):
        if self._state == "failed":
            return self
        elif other._state == "failed":
            return other
        else:
            # process Foo addition
            return something

Now you can call Foo methods regardless of whether the instance is valid
or not, errors will propagate cleanly, and you only need to check whether
the instance is valid at the very end of your code.

However, unless there is a clear advantage of doing this, you're creating
a fair bit of extra work for yourself.



-- 
Steven D'Aprano 




More information about the Python-list mailing list