Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > How to validate the __init__ parameters

Reply
Thread Tools

How to validate the __init__ parameters

 
 
Denis Doria
Guest
Posts: n/a
 
      12-21-2009
Hi;

I'm checking the best way to validate attributes inside a class. Of
course I can use property to check it, but I really want to do it
inside the __init__:

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

All examples that I saw with property didn't show a way to do it in
the __init__. Just to clarify, I don't want to check if the parameter
is an int, or something like that, I want to know if the parameter do
not use more than X chars; and want to do it when I'm 'creating' the
instance; not after the creation:

a = A('foo', 'bar')

not

class A:
def __init__(self, foo = None, bar = None):
self._foo = foo
self._bar = bar
def set_foo(self, foo):
if len(foo) > 5:
raise <something>
_foo = foo
foo = property(setter = set_foo)

a = A()
a.foo = 'foo'


I thought in something like:

class A:
def __init__(self, foo = None, bar = None):
set_foo(foo)
self._bar = bar
def set_foo(self, foo):
if len(foo) > 5:
raise <something>
_foo = foo
foo = property(setter = set_foo)

But looks too much like java
 
Reply With Quote
 
 
 
 
Jean-Michel Pichavant
Guest
Posts: n/a
 
      12-21-2009
Denis Doria wrote:
> Hi;
>
> I'm checking the best way to validate attributes inside a class. Of
> course I can use property to check it, but I really want to do it
> inside the __init__:
>
> class A:
> def __init__(self, foo, bar):
> self.foo = foo #check if foo is correct
> self.bar = bar
>
> All examples that I saw with property didn't show a way to do it in
> the __init__. Just to clarify, I don't want to check if the parameter
> is an int, or something like that, I want to know if the parameter do
> not use more than X chars; and want to do it when I'm 'creating' the
> instance; not after the creation:
>
> a = A('foo', 'bar')
>
> not
>
> class A:
> def __init__(self, foo = None, bar = None):
> self._foo = foo
> self._bar = bar
> def set_foo(self, foo):
> if len(foo) > 5:
> raise <something>
> _foo = foo
> foo = property(setter = set_foo)
>
> a = A()
> a.foo = 'foo'
>
>
> I thought in something like:
>
> class A:
> def __init__(self, foo = None, bar = None):
> set_foo(foo)
> self._bar = bar
> def set_foo(self, foo):
> if len(foo) > 5:
> raise <something>
> _foo = foo
> foo = property(setter = set_foo)
>
> But looks too much like java
>

One possible way, straight and simple

class A:
def __init__(self, foo = None, bar = None):
if len(foo) > 5:
raise ValueError('foo cannot exceed 5 characters')
self._foo = foo
self._bar = bar


JM
 
Reply With Quote
 
 
 
 
Alf P. Steinbach
Guest
Posts: n/a
 
      12-21-2009
* Denis Doria:
>
> I thought in something like:
>
> class A:
> def __init__(self, foo = None, bar = None):
> set_foo(foo)
> self._bar = bar
> def set_foo(self, foo):
> if len(foo) > 5:
> raise <something>
> _foo = foo
> foo = property(setter = set_foo)
>
> But looks too much like java


Yeah.

If member _foo has this constraint regardless then it's logically part of that
member's type, so make it a type:

class _Foo:
def __init__( self, seq ):
if seq is None:
self.items = []
elif len( seq ) > 5:
raise <something>
else:
self.items = seq

class A: # Your example
def __init__( self, foo = None, Bar = None ):
self._foo = _Foo( foo )
self._bar = bar
def set_foo( self, foo ):
self._foo = _Foo( foo )
foo = property( setter = set_foo )


Cheers & hth.,

- Alf
 
Reply With Quote
 
r0g
Guest
Posts: n/a
 
      12-21-2009
Denis Doria wrote:
> Hi;
>
> I'm checking the best way to validate attributes inside a class. Of
> course I can use property to check it, but I really want to do it
> inside the __init__:
>
> class A:
> def __init__(self, foo, bar):
> self.foo = foo #check if foo is correct
> self.bar = bar
>
> All examples that I saw with property didn't show a way to do it in
> the __init__. Just to clarify, I don't want to check if the parameter
> is an int, or something like that, I want to know if the parameter do
> not use more than X chars; and want to do it when I'm 'creating' the
> instance; not after the creation:
>
> a = A('foo', 'bar')
>
> not
>
> class A:
> def __init__(self, foo = None, bar = None):
> self._foo = foo
> self._bar = bar
> def set_foo(self, foo):
> if len(foo) > 5:
> raise <something>
> _foo = foo
> foo = property(setter = set_foo)
>
> a = A()
> a.foo = 'foo'
>
>
> I thought in something like:
>
> class A:
> def __init__(self, foo = None, bar = None):
> set_foo(foo)
> self._bar = bar
> def set_foo(self, foo):
> if len(foo) > 5:
> raise <something>
> _foo = foo
> foo = property(setter = set_foo)
>
> But looks too much like java




I use assertions myself e.g.

>>> foo = "123456"
>>> assert len(foo) <= 5

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError


Dunno if this would be considered good or bad programming practice by
those more experienced than I (comment always welcome!) but it works for
me


Roger.
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-21-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
 
Reply With Quote
 
Lie Ryan
Guest
Posts: n/a
 
      12-21-2009
On 12/22/2009 4:41 AM, Denis Doria wrote:
> Hi;
>
> I'm checking the best way to validate attributes inside a class. Of
> course I can use property to check it, but I really want to do it
> inside the __init__:
>
> class A:
> def __init__(self, foo, bar):
> self.foo = foo #check if foo is correct
> self.bar = bar
>


A nice sugar to do that:

import functools

class CheckError(Exception): pass

def func_check(*argcheckers):
def _checked(func):
def _function(*args):
res = [(check(arg), check, arg) for check, arg in
zip(argcheckers, args)]
if all(r[0] for r in res):
return func(*args)
else:
raise CheckError(filter(lambda x: x[0] == False, res))
return _function
return _checked

method_check = functools.partial(func_check, lambda a: True)

################################################## ########
def check_foo(arg):
return 5 <= arg <= 10

def check_bar(arg):
return 10 <= arg < 20

class A(object):
@method_check(check_foo, check_bar)
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-21-2009
On Mon, 21 Dec 2009 21:49:11 +0000, r0g wrote:

> I use assertions myself e.g.
>
>>>> foo = "123456"
>>>> assert len(foo) <= 5

> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> AssertionError
>
>
> Dunno if this would be considered good or bad programming practice by
> those more experienced than I (comment always welcome!) but it works for
> me



Bad practice.

Assertions are ignored when you run Python with the -O (optimization)
command line switch. Your code will behave differently if you use
assertions. So you should never use assertions for error-checking, except
perhaps for quick-and-dirty throw-away scripts.

Assertions are useful for testing invariants and "this will never happen"
conditions. A trivial example:

result = math.sin(2*math.pi*x)
assert -1 <= result <= 1

Assertions are also useful in testing, although be careful: since the
assert statement is ignored when running with -O, that means you can't
test your application when running with -O either! But do not use them
for data validation unless you're happy to run your application with no
validation at all.



--
Steven
 
Reply With Quote
 
r0g
Guest
Posts: n/a
 
      12-22-2009
Steven D'Aprano wrote:
> On Mon, 21 Dec 2009 21:49:11 +0000, r0g wrote:
>
>> I use assertions myself e.g.
>>
>>>>> foo = "123456"
>>>>> assert len(foo) <= 5

>> Traceback (most recent call last):
>> File "<stdin>", line 1, in <module>
>> AssertionError
>>
>>
>> Dunno if this would be considered good or bad programming practice by
>> those more experienced than I (comment always welcome!) but it works for
>> me

>
>
> Bad practice.
>
> Assertions are ignored when you run Python with the -O (optimization)
> command line switch. Your code will behave differently if you use
> assertions. So you should never use assertions for error-checking, except
> perhaps for quick-and-dirty throw-away scripts.
>
> Assertions are useful for testing invariants and "this will never happen"
> conditions. A trivial example:
>
> result = math.sin(2*math.pi*x)
> assert -1 <= result <= 1
>
> Assertions are also useful in testing, although be careful: since the
> assert statement is ignored when running with -O, that means you can't
> test your application when running with -O either! But do not use them
> for data validation unless you're happy to run your application with no
> validation at all.
>
>
>



Yikes, glad to be set me straight on that one! Thanks It's a pity
though, I really like the way it reads. Is there anything similar with
ISN'T disabled when optimizations are turned on?

I guess I could do the following but it seems a bit ugly in comparison

if len(foo) <= 5: raise Exception



Roger.
 
Reply With Quote
 
Steve Holden
Guest
Posts: n/a
 
      12-22-2009
r0g wrote:
> Steven D'Aprano wrote:
>> On Mon, 21 Dec 2009 21:49:11 +0000, r0g wrote:
>>
>>> I use assertions myself e.g.
>>>
>>>>>> foo = "123456"
>>>>>> assert len(foo) <= 5
>>> Traceback (most recent call last):
>>> File "<stdin>", line 1, in <module>
>>> AssertionError
>>>
>>>
>>> Dunno if this would be considered good or bad programming practice by
>>> those more experienced than I (comment always welcome!) but it works for
>>> me

>>
>> Bad practice.
>>
>> Assertions are ignored when you run Python with the -O (optimization)
>> command line switch. Your code will behave differently if you use
>> assertions. So you should never use assertions for error-checking, except
>> perhaps for quick-and-dirty throw-away scripts.
>>
>> Assertions are useful for testing invariants and "this will never happen"
>> conditions. A trivial example:
>>
>> result = math.sin(2*math.pi*x)
>> assert -1 <= result <= 1
>>
>> Assertions are also useful in testing, although be careful: since the
>> assert statement is ignored when running with -O, that means you can't
>> test your application when running with -O either! But do not use them
>> for data validation unless you're happy to run your application with no
>> validation at all.
>>
>>
>>

>
>
> Yikes, glad to be set me straight on that one! Thanks It's a pity
> though, I really like the way it reads. Is there anything similar with
> ISN'T disabled when optimizations are turned on?
>
> I guess I could do the following but it seems a bit ugly in comparison
>
> if len(foo) <= 5: raise Exception
>

OK,so here's a revolutionary thought. Since you have now discovered that

a) you can't reliably rely on an AssertionError being raised by an
assert that asserts an invariant;

b) the alternative of explicitly raising an exception is at least
somewhat uglier; and

c) you have to do *something*. Er, actually, do you?

What's the exact reason for requiring that a creator argument be of a
specific type? So operations on the instances don't go wrong? Well, why
not just admit that we don't have control over everything, and just *let
things go wrong* when the wrong type is passed?

What will then happen? Why, an exception will be raised!

You might argue that the user, seeing the traceback from the exception,
won't know what to make of it. In response to which I would assert (pun
intended) that neither would the traceback produced by the assertion
failure.

Given that somebody who knows what they are talking about has to cast
their eye over a traceback to understand what's wrong with the program,
it's not much extra effort for that person realise that a method has
been called with an invalid argument type. I accept that there are
legitimate arguments in opposition to this point of view, but it has a
certain engineering efficiency (as long as we are prepared to work
inside frameworks like Django that will encapsulate any error tracebacks
so that you see all the information that you need, and the user gets
some other inexplicable error message like "A programmer probably
screwed up).

In other words, "be prepared to fix your program when it goes wrong" is
a valid alternative to trying to anticipate every single last thing that
might go wrong. This philosophy might not be appropriate for
extra-terrestrial exploration, but most Python programmers aren't doing
that.

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
PyCon is coming! Atlanta, Feb 2010 http://us.pycon.org/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/
 
Reply With Quote
 
Steve Holden
Guest
Posts: n/a
 
      12-22-2009
r0g wrote:
> Steven D'Aprano wrote:
>> On Mon, 21 Dec 2009 21:49:11 +0000, r0g wrote:
>>
>>> I use assertions myself e.g.
>>>
>>>>>> foo = "123456"
>>>>>> assert len(foo) <= 5
>>> Traceback (most recent call last):
>>> File "<stdin>", line 1, in <module>
>>> AssertionError
>>>
>>>
>>> Dunno if this would be considered good or bad programming practice by
>>> those more experienced than I (comment always welcome!) but it works for
>>> me

>>
>> Bad practice.
>>
>> Assertions are ignored when you run Python with the -O (optimization)
>> command line switch. Your code will behave differently if you use
>> assertions. So you should never use assertions for error-checking, except
>> perhaps for quick-and-dirty throw-away scripts.
>>
>> Assertions are useful for testing invariants and "this will never happen"
>> conditions. A trivial example:
>>
>> result = math.sin(2*math.pi*x)
>> assert -1 <= result <= 1
>>
>> Assertions are also useful in testing, although be careful: since the
>> assert statement is ignored when running with -O, that means you can't
>> test your application when running with -O either! But do not use them
>> for data validation unless you're happy to run your application with no
>> validation at all.
>>
>>
>>

>
>
> Yikes, glad to be set me straight on that one! Thanks It's a pity
> though, I really like the way it reads. Is there anything similar with
> ISN'T disabled when optimizations are turned on?
>
> I guess I could do the following but it seems a bit ugly in comparison
>
> if len(foo) <= 5: raise Exception
>

OK,so here's a revolutionary thought. Since you have now discovered that

a) you can't reliably rely on an AssertionError being raised by an
assert that asserts an invariant;

b) the alternative of explicitly raising an exception is at least
somewhat uglier; and

c) you have to do *something*. Er, actually, do you?

What's the exact reason for requiring that a creator argument be of a
specific type? So operations on the instances don't go wrong? Well, why
not just admit that we don't have control over everything, and just *let
things go wrong* when the wrong type is passed?

What will then happen? Why, an exception will be raised!

You might argue that the user, seeing the traceback from the exception,
won't know what to make of it. In response to which I would assert (pun
intended) that neither would the traceback produced by the assertion
failure.

Given that somebody who knows what they are talking about has to cast
their eye over a traceback to understand what's wrong with the program,
it's not much extra effort for that person realise that a method has
been called with an invalid argument type. I accept that there are
legitimate arguments in opposition to this point of view, but it has a
certain engineering efficiency (as long as we are prepared to work
inside frameworks like Django that will encapsulate any error tracebacks
so that you see all the information that you need, and the user gets
some other inexplicable error message like "A programmer probably
screwed up).

In other words, "be prepared to fix your program when it goes wrong" is
a valid alternative to trying to anticipate every single last thing that
might go wrong. This philosophy might not be appropriate for
extra-terrestrial exploration, but most Python programmers aren't doing
that.

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
PyCon is coming! Atlanta, Feb 2010 http://us.pycon.org/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
Should one always add super().__init__() to the __init__? Ramchandra Apte Python 17 09-30-2012 12:04 PM
TypeError: object.__init__() takes no parameters Oliver Python 5 09-09-2011 05:31 PM
super(...).__init__() vs Base.__init__(self) Kent Johnson Python 7 02-12-2006 08:59 PM
__new__ does not call __init__ as described in descrintro.html (WAS:Can __new__ prevent __init__ from being called?) Steven Bethard Python 2 02-16-2005 06:50 AM



Advertisments