tkbe

November 10, 2006

Python :: flatten

Filed under: python — tb @ 10:52 am

Although it can seem like a CS excercise, everyone will sooner or later have to write a "flatten" function -- a function that takes a nested "list" and makes it one-dimensional:

PYTHON:
  1. >>> flatten([1,2, [3,4], 5])
  2. [1, 2, 3, 4, 5]

it should also handle Python datatypes in a reasonable manner:

PYTHON:
  1. >>> flatten(['hello', 'world'])
  2. ['hello', 'world']
  3. >>> flatten(i**2 for i in range(10))
  4. [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

The trick to the implementation is to realize that everything that is iterable should be flattened... except strings and unicode. To test if something is iterable, call iter on it and be prepared to catch a TypeError: iterator over non-sequence. To test if something is string or unicode use isinstance(item, basestring). That results in something like this:

PYTHON:
  1. def flatten(lst):
  2.     def _flatten(lst, res):
  3.         for item in lst:
  4.             if isinstance(item, basestring):
  5.                 res.append(item)
  6.             else:
  7.                 try:
  8.                     _flatten(iter(item), res)
  9.                 except TypeError: # iterator over nonsequence
  10.                     res.append(item)
  11.         return res
  12.     return _flatten(lst, [])

That generates the entire list in the res variable of the inner function as it's recursing through the datastructure. That could be a very long list, so perhaps a generator works better?:

PYTHON:
  1. def flatten(lst):
  2.     for item in lst:
  3.         if isinstance(item, basestring):
  4.             yield item
  5.         else:
  6.             try:
  7.                 flatten(iter(item))
  8.             except TypeError: # iterator over nonsequence
  9.                 yield item
  10.     return

That looks pretty good :-)

So what was the reason I had to write flatten this time?...

PYTHON:
  1. >>> from html import table, tr, td
  2. >>> td_num = html.mktag('td', width="20%", style="background:aqua")
  3. >>> table(tr(td_num(x), td(x**2)) for x in range(6))

Which gives

0 0
1 1
2 4
3 9
4 16
5 25

the __str__ for all the classes is only defined for the base class, which needed to flatten its contents...

November 5, 2006

Python :: defprop - a very simple idiom for defining properties

Filed under: python — tb @ 3:21 pm

Most attempts at making it easier to define properties in Python seem to quickly descend into metaclass obscurity. Metaclasses can surely solve the problem for an appropriate definition of solve, but "Explicit is better than implicit", and "Simple is better than complex," so none of the metaclass solutions (mine included) have had great appeal. It's always fun to visit the Cookbook, and today I found a great recipe from Sean Ross at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183.

To recap, the traditional way of defining properties looks something like this

PYTHON:
  1. class MyClass(object):
  2.     def __init__(self):
  3.         self._foo = "foo"
  4.  
  5.     def getfoo(self):
  6.         return self._foo
  7.     def setfoo(self, value):
  8.         self._foo = value
  9.     def delfoo(self):
  10.         del self._foo
  11.     foo = property(getfoo, setfoo, delfoo, "property foo's doc string")

All the getters and setters pollute your class namespace (think about adding a bar property), visually there is no Pythonic grouping of the code that belongs to the property, and you don't really know that it's going to be a property until the end.

At the end of the recipe Sean says that if decorators are accepted (they were) his idiom would be even better, so here's my attempt:

PYTHON:
  1. # a simple decorator
  2. def defprop(doc):
  3.     def _defprop(fn):
  4.         return property(doc=doc, **fn())
  5.     return _defprop
  6.  
  7. class MyClass(object):
  8.     def __init__(self):
  9.         self._x = 42
  10.  
  11.     @defprop("The answer to life the universe and Everything")
  12.     def foo():
  13.         def fget(self):
  14.             return self._x
  15.         def fset(self, v):
  16.             self._x = v
  17.         return locals()

Overall, I think it doesn't suck too bad. The name defprop is based on the fact that def is an abbreviation also and property is taken.

Update: turns out I've found it easier to not use the decorator, just like the recipe mentioned above:

PYTHON:
  1. class MyClass(object):
  2.     def __init__(self):
  3.         self._x = 42
  4.  
  5.     def foo():
  6.         def fget(self):
  7.             return self._x
  8.         def fset(self, v):
  9.             self._x = v
  10.         return locals()
  11.     foo = property(**foo())

it's an easy idiom to remember, easier than writing the property from scratch, and apparantly easier than figuring out where to put a property so that it will be available everywhere I happen to be...

« Previous Page

Powered by WordPress