How to validate the __init__ parameters

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Mon Dec 21 16:50:56 EST 2009


On Mon, 21 Dec 2009 09:41:22 -0800, Denis Doria wrote:

> Hi;
> 
> I'm checking the best way to validate attributes inside a class. 

There is no "best way", since it depends on personal taste.


> Of
> course I can use property to check it, but I really want to do it inside
> the __init__:

If you "really want to do it inside the __init__", then copy the code 
that you would put in the property's setter into the __init__ method. But 
why do you care that the check is inside the __init__ method? All that is 
really important is that the __init__ method *calls* the check, not where 
the check lives.



> class A:
>     def __init__(self, foo, bar):
>         self.foo = foo #check if foo is correct
>         self.bar = bar

Here are some ways of doing this, more or less in order of complexity and 
difficulty.


(1) Don't validate at all. Just document that foo must be no more than 
five characters long, and if the caller pays no attention and passes a 
too-long string, any explosions that happen are their fault, not yours.

class A:
    """Class A does blah blah.
    If foo is longer than five characters, behaviour is undefined.
    """
    def __init__(self, foo = None, bar = None):
        self.foo = foo
        self.bar = bar

(This may seem silly, but for more difficult constraints which are hard 
to test, it may be your only choice.)


(2) Validate once only, at initialisation time:

class A:
    def __init__(self, foo = None, bar = None):
        if len(foo) > 5:
            raise ValueError("foo is too big")
        self.foo = foo
        self.bar = bar


Note that further assignments to instance.foo will *not* be validated.


(3) Move the validation into a method. This is particularly useful if the 
validation is complex, or if you expect to want to over-ride it in a sub-
class.

class A:
    def __init__(self, foo = None, bar = None):
        self.validate(foo)
        self.foo = foo
        self.bar = bar
    def validate(self, foo):
        if len(foo) > 5:
            raise ValueError("foo is too big")


Further assignments to instance.foo are still not validated.


(4) Validate on every assignment to foo by using a property. Note that 
for this to work, you MUST use a new-style class. In Python 3, you don't 
need to do anything special, but in Python 2.x you must inherit from 
object:

class A(object):
    def __init__(self, foo = None, bar = None):
        self.foo = foo  # calls the property setter
        self.bar = bar
    def _setter(self, foo):
        if len(foo) > 5:
            raise ValueError("foo is too big")
        self._foo = foo
    def _getter(self):
        return self._foo
    foo = property(_getter, _setter, None, "optional doc string for foo")


If you think this looks "too much like Java", well, sorry, but that's 
what getters and setters look like. But note that you never need to call 
the getter or setter explicitly, you just access instance.foo as normal.


(5) Use explicit Java-style getter and setter methods. This avoids using 
property, so it doesn't need to be a new-style class:

class A:
    def __init__(self, foo = None, bar = None):
        self.setfoo(foo)
        self.bar = bar
    def setfoo(self, foo):
        if len(foo) > 5:
            raise ValueError("foo is too big")
        self._foo = foo
    def getfoo(self):
        return self._foo

If you want to write Java using Python syntax, this may be the solution 
for you. But be aware that Python programmers will laugh at you.


(5) If the constraint on foo is significant enough, perhaps it should be 
made part of the type of foo.

class FooType(str):  # or inherit from list, or whatever...
    def __init__(self, x):
        if len(x) > 5:
            raise ValueError("initial value is too big, invalid FooType")


class A:
    def __init__(self, foo = None, bar = None):
        self.foo = FooType(foo)
        self.bar = bar
 

Other, more complex solutions include using decorators or even metaclass 
programming. Don't worry about these at this point, I'm just showing off 
*wink*


Hope this helps,




-- 
Steven



More information about the Python-list mailing list