The company I work for, Enthought, has a nifty system they use in pretty much all their software called Traits ([url=https://svn.enthought.com/svn/enthought/Traits/tags/enthought.traits_2.0.1b1/docs/Traits2_UM.pdf]user manual — PDF!). It’s pretty cool, and it does some nifty stuff; they describe it as having five features:
[list=1][]Initialization
[]Validation
[]Delegation
[]Notification
[*]Visualization[/list]
Unfortunately, I don’t really like their implementation (it’s open-source, and there’s even a Trac installation for it), because it does a lot of what I consider magic. For example, check out Pg. 2 (the page numbered two, starting from the beginning of the first chapter) of the user manual linked above: [font=monospace]_age_changed[/font] is dynamically found and called, without the programmer ever specifying that it should be used. That doesn’t really seem Pythonic.
So, I wrote it from scratch. They won’t use my system because it doesn’t have seven years of bug-fixes and tests (although many of their actual unittests fail), but I did it more to see if I could, anyways.
I didn’t rewrite the visualization part, though, because I couldn’t care less about that, and I wasn’t really interested in trying to replicate that beautifully.
I’ve posted my version at my site: http://code.nokrev.com/?p=traits-rewritten.git, but I’ve reproduced some examples (based of the tests) below, so as to provide a basic feel for what it does.
from traits.base import Trait, String
class Traited(object):
x = Trait() # Value defaults to None
s = String() # Defaults to ''
if __name__ == '__main__':
t = Traited()
print t.x, t.s # => 'None '
t.x = 'Some value'
t.s = 'A string'
print t.x, t.s # => 'Some value (here, a str) A string'
Here’s an example of delegation (the child’s last name is the father’s last name). Unfortunately, I have not found a way to implement this without resorting to magic, so I still am unhappy with how this actually works (but I am open to suggestions):
from traits.base import Trait, String
from traits.delegation import Delegate
class Parent(object):
first_name = String()
last_name = String()
class Child(object):
father = Delegate(Parent)
first_name = String()
last_name = father.last_name
if __name__ == '__main__':
dad = Parent()
dad.first_name = 'Daddy'
dad.last_name = 'Wheeler'
son = Child()
son.father = dad
son.first_name = 'Jeff'
print son.last_name # => 'Wheeler'
Some 4th of July validation:
from traits.base import Trait, String, validate
class Flag(object):
color = String(default='white')
@validate(color)
def name_validator(self, trait, color):
if color.lower() not in ('red', 'white', 'blue'):
raise ValueError('Color must be "red", "white", or "blue"')
if __name__ == '__main__':
c = Flag()
try:
c.color = 'Green'
except ValueError:
print 'Could not set color to "Green"'
c.color = 'Red'
c.color = 'white'
I think you get the idea … it does some other cool stuff too, and even supports multiple inheritance.