tkbe

June 14, 2007

emacs :: autocompletion and code-commenting

Filed under: python, emacs — tb @ 6:29 pm

I’m a believing member in the church of emacs, and in honor of the new release I went looking for solutions to a couple of outstanding issues I’ve had with my emacs installation.

Today’s first emacs find is M-; which, although undocumented, works excellently to comment and uncomment code blocks in Python (it had puzzled me for a while that the comment-block command on the Python menu was C-c # and that there was no mapping for the uncomment command). The M-; command works in a plethora of other modes as well to comment and uncomment blocks.

The second emacs treasure I found today was how to turn on autocompletion. Not the mind-numbingly annoying kind that some editors have that pops up and obscures half your program whenever you type a key, but rather cycling through options when pressing M-RET. To enable it, simply put this in your .emacs file:

(define-key global-map (read-kbd-macro "M-RET") 'hippie-expand)

Searching for some info on hippie-expand, I ran into dabbrev-expand which is bound to M-/. I couldn’t tell you what the difference is, but the ESC-Enter combination is easier to find blindfolded so that’s what I’m going with.

April 18, 2007

Python :: property set

Filed under: python — tb @ 11:11 am

It looks like Python is getting tuples with named members in 2.6(http://www.oluyede.org/blog/2007/03/11/updates-from-python-svn-part-2/ and http://docs.python.org/dev/lib/named-tuple-factory.html). I suspect many of us have implemented similar functionality ourselves, e.g. Shannon -jj Behrens describes how he sometimes uses dictionaries to return composite polymorphic values (http://jjinux.blogspot.com/2007/03/python-returning-multiple-things-of.html). The problem with dictionaries is of course that they require too much excercise of your little finger in typing ['xx']. That's even worse for me since I'm using a keyboard layout that switches national characters onto those keys when I tap the caps-lock key, so I can use my American-keyboard touch typing skillz and eat my national characters as well (I'm looking forward to the Metaphor-off!)

It looks like the new Python NamedTuple type is going to limit the fields to those that are defined at creation time. It's based on a tuple, so I suppose that follows naturally, however it doesn't seem natural for the abstract-data-type of a container of named fields with iteration and indexing. I've called my implementation of this ADT a property set since most of the motivating use cases for this was returning returning values that had properties attached to them. The use is as follows...

You can assign to random fields, the only limitation is that they cannot start with an underscore, but public fields wouldn't have that anyway so it's not really a limitation (the limitation comes from the fact that the implementation overrides __setattr__ and being able to interpret fields starting with an underscore as internal to the implementation simplifies things quite a bit):

PYTHON:
  1. >>> p = pset()
  2. >>> p.a = 42
  3. >>> p.b = 'hello'
  4. >>> p.c = [p.a, p.b]
  5. >>> p
  6. pset(a=42, b='hello', c=[42, 'hello'])

You can iterate over the values:

PYTHON:
  1. >>> for key, value in p:
  2. ...     print key, value
  3. ...
  4. a 42
  5. b hello
  6. c [42, 'hello']

Notice that it maintains the insertion order, and you can also access by index:

PYTHON:
  1. >>> p[1]
  2. 'hello'

For technical reasons it is not possible to maintain the order when creating a pset from keyword arguments (I was hesitating to put this functionality in, but practicality beats purity, and it's turned out to be very practical). Equality does not require isomorphism, which means that as long as the sets have the same fields they compare equal:

PYTHON:
  1. >>> q = pset(a=42, b='hello', c=[42,'hello'])
  2. >>> q
  3. pset(a=42, c=[42, 'hello'], b='hello')
  4. >>> p == q
  5. True

You can keep the order given to the constructor by initializing with a list of tuples:

PYTHON:
  1. >>> list(p.items())
  2. [('a', 42), ('b', 'hello'), ('c', [42, 'hello'])]
  3. >>> r = pset(p.items())
  4. >>> r
  5. pset(a=42, b='hello', c=[42, 'hello'])

The example above does of course not mean that you can't create a pset from a pset directly (this also maintains order):

PYTHON:
  1. >>> s = pset(p)
  2. >>> s
  3. pset(a=42, b='hello', c=[42, 'hello'])

It's also extremely useful to be able to use field indexing notation as well:

PYTHON:
  1. >>> p
  2. pset(a=42, b='hello', c=[42, 'hello'])
  3. >>> p.b
  4. 'hello'
  5. >>> p[1]
  6. 'hello'
  7. >>> p['b']
  8. 'hello'
  9. >>> p['b'] = 'world'
  10. >>> p
  11. pset(a=42, b='world', c=[42, 'hello'])
  12. >>> p[1] = 'foo'
  13. >>> p
  14. pset(a=42, b='foo', c=[42, 'hello'])

Here's the code:

PYTHON:
  1. class pset(dict):
  2.     """This code is placed in the Public Domain.
  3.    
  4.        Property Set class.
  5.        A property set is an object where values are attached to attributes,
  6.        but can still be iterated over as key/value pairs.
  7.        The order of assignment is maintained during iteration.
  8.        Only one value allowed per key.
  9.         >>> x = pset()
  10.         >>> x.a = 42
  11.         >>> x.b = 'foo'
  12.         >>> x.a = 314
  13.         >>> x
  14.          pset(a=314, b='foo')
  15.     """
  16.     def __init__(self, items=(), **attrs):
  17.         object.__setattr__(self, '_order', [])
  18.         super(pset, self).__init__()
  19.         for k, v in items:
  20.             self.add(k, v)
  21.         for k, v in attrs.items():
  22.             self.add(k, v)
  23.  
  24.     def add(self, key, value):
  25.         if type(key) in (int, long):
  26.             key = self._order[key]
  27.         elif key not in self._order:
  28.             self._order.append(key)
  29.         dict.__setitem__(self, key, value)
  30.  
  31.     def __eq__(self, other):
  32.         """Equal iff they have the same set of keys, and the values for
  33.            each key is equal. Key order is not considered for equality.
  34.         """
  35.         if set(self._order) == set(other._order):
  36.             for key in self._order:
  37.                 if self[key] != other[key]:
  38.                     return False
  39.             return True
  40.         return False
  41.  
  42.     def __iadd__(self, other):
  43.         for k, v in other:
  44.             self.add(k, v)
  45.  
  46.     # should probably have an __radd__ method too...
  47.     def __add__(self, other):
  48.         tmp = self.__class__()
  49.         tmp += self
  50.         tmp += other
  51.         return tmp
  52.  
  53.     def __repr__(self):
  54.         vals = ', '.join('%s=%s' % (k, repr(v)) for (k,v) in self)
  55.         return '%s(%s)' % (self.__class__.__name__, vals)
  56.  
  57.     def __getattr__(self, key):
  58.         if key not in self:
  59.             raise AttributeError(key)
  60.         return self.get(key)
  61.  
  62.     def __getitem__(self, key):
  63.         if type(key) in (int, long):
  64.             key = self._order[key]
  65.         return self.get(key)
  66.    
  67.     __str__ = __repr__
  68.  
  69.    
  70.     def __iter__(self):
  71.         return ((k, self.get(k)) for k in self._order)
  72.  
  73.     def items(self):
  74.         return iter(self)
  75.  
  76.     def __setattr__(self, key, val):
  77.         if key.startswith('_'):
  78.             object.__setattr__(self, key, val)
  79.         else:
  80.             self.add(key, val)
  81.  
  82.     def __setitem__(self, key, val):
  83.         self.add(key, val)

March 8, 2007

python :: how old are you?

Filed under: python — tb @ 5:06 pm

It's a simple question, any ~5 y/o can answer it "I'm five years, four months, and two days". So, how do we calculate that using Python? Subtracting date objects only produces objects giving the difference in days, which is very excact, and very unhelpful -- still it's the only sane option for a datetime library...

One problem is how old a person is who is born in the leap-year 2000, on date(2000, 2, 29) if today was date(2003, 2, 28). I decided they were 2 years, 11 months, and 30 days, and that on date(2003, 3, 1) they were 3 years and 1 day. That seems very logical to me, although I know there are lot's of people that disagree with me... Feel free to come up with code that works for you ;-)

The code turned out to be relatively short and simple and works on the simple principle of "I'm one year older if my birthday is today or was earlier this year".

PYTHON:
  1. from datetime import date as _date
  2. from calendar import monthrange as _monthrange
  3.  
  4. def age(dob, today=_date.today()):
  5.     y = today.year - dob.year
  6.     m = today.month - dob.month
  7.     d = today.day - dob.day
  8.  
  9.     while m <0 or d <0:
  10.         while m <0:
  11.             y -= 1
  12.             m = 12 + m  # m is negative
  13.         if d <0:
  14.             m -= 1
  15.             days = days_previous_month(today.year, today.month)
  16.             d = max(0, days - dob.day) + today.day
  17.  
  18.     return y, m, d
  19.  
  20. def days_previous_month(y, m):
  21.     m -= 1
  22.     if m == 0:
  23.         y -= 1
  24.         m = 12
  25.     _, days = _monthrange(y, m)
  26.     return days

The calendar module is way underutilized most of the time, so it was nice to get to use it...

Next Page »

Powered by WordPress