Python style: to check or not to check args and data members

Joel Hedlund joel.hedlund at gmail.com
Fri Sep 1 10:01:37 CEST 2006


 > Short answer: Use Traits. Don't invent your own mini-Traits.

Thanks for a quick and informative answer! I'll be sure to read up on the 
subject. (And also: thanks Bruno for your contributions!)

 > Types are very frequently exactly the wrong thing you want to check for.

I see what you mean. Allowing several data types may generate unwanted side 
effects (integer division when expecting real division, for example).

I understand that Traits can do value checking which is superior to what I 
presented, and that they can help me move validation away from functional 
code, which is always desirable. But there is still the problem of setting 
an approprate level of validation.

Should I validate data members only? This is quite easily done using Traits 
or some other technique and keeps validation bloat localized in the code. 
This is in line with the DRY principle and makes for smooth extensibility, 
but the tracebacks will be less useful.

Or should I go the whole way and validate at every turn (all data members, 
every arg in every method, ...)? This makes for very secure code and very 
useful tracebacks, but does not feel very DRY to me... Are the benefits 
worth the costs? Do I build myself a fortress of unmaintainability this way? 
Will people laugh at my modules?

Or taken to the other extreme: Should I simply duck-type everything, and 
only focus my validation efforts to external data (from users, external 
applications and other forces of evil). This solution makes for extremely 
clean code, but the thought of potential silent data corruption makes me 
more than a little queasy.

What level do you go for?

Thanks!
/Joel

Robert Kern wrote:
> Joel Hedlund wrote:
>> Hi!
>>
>> The question of type checking/enforcing has bothered me for a while, and 
>> since this newsgroup has a wealth of competence subscribed to it, I 
>> figured this would be a great way of learning from the experts. I feel 
>> there's a tradeoff between clear, easily readdable and extensible code 
>> on one side, and safe code providing early errors and useful tracebacks 
>> on the other. I want both! How do you guys do it? What's the pythonic 
>> way? Are there any docs that I should read? All pointers and opinions 
>> are appreciated!
> 
> Short answer: Use Traits. Don't invent your own mini-Traits.
> 
> (Disclosure: I work for Enthought.)
> 
>    http://code.enthought.com/traits/
> 
> Unfortunately, I think the standalone tarball on that page, uh, doesn't stand 
> alone right now. We're cleaning up the interdependencies over the next two 
> weeks. Right now, your best bet is to get the whole enthought package:
> 
>    http://code.enthought.com/ets/
> 
> Talk to us on enthought-dev if you need any help.
> 
>    https://mail.enthought.com/mailman/listinfo/enthought-dev
> 
> 
> Now back to Traits itself:
> 
> Traits does quite a bit more than "type-checking," and I think that is its 
> least-useful feature that it provides for Python users. Types are very 
> frequently exactly the wrong thing you want to check for. They allow inputs that 
> you would like to be invalid and disallow inputs that would have worked just 
> fine if you had relied on duck-typing. In general terms, Traits does 
> value-checking; it's just that some of the traits definitions check values by 
> validating their types.
> 
> You have to be careful with type-checking, because it can introduce fragility 
> without enhancing safety. But sometimes you are working with other code that 
> necessarily has type requirements (like extension code), and moving the 
> requirements forward a bit helps build usable interfaces.
> 
> Your examples would look like this with Traits:
> 
> 
> from enthought.traits.api import HasTraits, Int, method
> 
> class MyClass(HasTraits):
>      """My example class.
>      """
> 
>      int_member = Int(0, desc="I am an integer")
> 
>      method(None, Int)
>      def process_data(self, data):
>           """Do some data processing.
>           """
> 
>           self.int_member += 1
> 
> 
> a = MyClass(int_member=9)
> a = MyClass(int_member='moo')
> """
> Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>    File "/Users/kern/svn/enthought-lib/enthought/traits/trait_handlers.py", line 
> 172, in error
>      raise TraitError, ( object, name, self.info(), value )
> enthought.traits.trait_errors.TraitError: The 'int_member' trait of a MyClass 
> instance must be a value of type 'int', but a value of moo was specified.
> """
> 
> # and similar errors for
> # a.int_member = 'moo'
> # a.process_data('moo')
> 
> 
> The method() function predates 2.4 and has not yet been converted to a 
> decorator. We don't actually use it much.
> 



More information about the Python-list mailing list