mrCarnivore
mrCarnivore

Reputation: 5088

Not creating an object when conditions are not met in python?

Is it possible to not create an object if certain conditions are not met in the constructor of a class?

E.g.:

class ABC:
    def __init__(self, a):
        if a > 5:
            self.a = a
        else:
            return None

a = ABC(3)
print(a)

This should print None (since it should not create an Object but return None in this case) but currently prints the Object...

Upvotes: 4

Views: 1820

Answers (4)

progmatico
progmatico

Reputation: 4964

I think this shows the principle. Note that returning None is not returning a new object because None is a singleton in Python, but of course it is still an object. Note also that __init__ will not be called as None is not an A class object.

class A():
    def __new__(cls, condition):
        if condition:
            obj = super().__new__(cls)
            return obj

a = A(True)
print(a)
a1 = A(False)
print(a1)

This outputs:

<__main__.A object at 0x7f64e65c62e8>
None

Edit:

I tried to directly address your question, by showing the new behaviour. But I think all the answers and comments here are good contributions.

So the proper answer is more about how you should do this kind of thing.

I recommend, more or less in this order depending on your taste and context:

  1. "do the sane thing" by @Matt Messersmith. Test condition outside the class in client code and create the object only when appropriate.

  2. "If the check is complicated and I want to make it easier for the user, it is better placed inside the class." by MrCarnivore. Maybe, maybe not. You can group validation code in functions inside a module that you import and call from the outside, still like in 1) mostly because validation rules can be repetitive or even apply to different kinds of objects. This also hides validation complexity from the client code.

  3. raise an exception and use a try block in client code, by @Farhan.K. This is probably the more pythonic way if you test inside the class. You can still invoke an external data validation function inside the class for this.

  4. define a classmethod in the class that acts as an alternate constructor by @salparadise. This is a good option.

  5. go with a condition inside __new__ but do not pass it as an arg, or you have to use varargs to deal with that calls and __init__ calls. Then if you need varargs for other reasons, does not look a good option.

So I end up recommending several answers and options, except my own example. But I was only illustrating the main point of the question anyway.

Upvotes: 3

salparadise
salparadise

Reputation: 5825

you can use a classmethod as an alternate constructor and return what you want:

class ABC:
    def __init__(self, a):
        self.a = a

    @classmethod
    def with_validation(cls, a):
        if a > 5:
            return cls(a)
        return None


a = ABC.with_validation(10)

a
<__main__.ABC at 0x10ceec288>

a = ABC.with_validation(4)

a

type(a)
NoneType

Upvotes: 5

mrCarnivore
mrCarnivore

Reputation: 5088

With the help from @progmatico and a little try and error I managed to come to this solution:

class ABC:
    def __new__(cls, *args, **kwargs):
        if len(args) > 0:
            arg = args[0]
        else:
            arg = kwargs['a']

        if arg <= 5:
            return None
        return object.__new__(cls)

    def __init__(self, a):
        self.a = a

    def __str__(self):
        return str(self.a)

a = ABC(a=3)
print(a)
b = ABC(a=7)
print(b)

Upvotes: 2

quamrana
quamrana

Reputation: 39414

This code seems to show that an exception raised in an __init__() gives you the effect you want:

class Obj:
    def __init__(self):
        raise Exception("invalid condition")

class E:
    def __call__(self):
        raise Exception("raise")

def create(aType):
    return aType()

def catchEx():
    e = E()
    funcs=[Obj, int, e]

    for func in funcs:
        try:
            func()
            print('No exception:', func)
        except Exception as e:
            print(e)

catchEx()

Output:

invalid condition
No exception: <class 'int'>
raise

Upvotes: 3

Related Questions