Tuesday, December 28, 2010

nested sequence unpacking

>>> a,(b,c) = 1,(2,3)
>>> a
1
>>> b
2
>>> c
3

the confusingly named setdefault

setdefault has the same behavior as the following function:

>>> def setdefault2(my_dict, key, def):
...    my_dict[key] = my_dict.get(key, def)
...    return my_dict[key]
...
>>> a = {}
>>> setdefault2(a, "1", "2")
'2'
>>> a.setdefault(1, 2)
2
>>> setdefault2(a, "1", "3")
'2'
>>> a.setdefault(1, 3)
2
>>> a
{'1': '2', 1: 2}


This function is useful in similar cases to a collections.defaultdict.  The difference being, with this function you can choose what you want the default to be each time you fetch a key from the dictionary.  With a defaultdict, the default value for a missing key must be set at construction time.

using finally to do something after return

>>> def foo():
...    try:
...       print "returning"
...       return
...    finally:
...       print "after return"
...
>>> foo()
returning
after return

sequence unpacking in argument list

>>> def foo(a, (b,c)): print a,b,c
...
>>> foo(1,(2,3))
1 2 3
>>> foo(1,())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in foo
ValueError: need more than 0 values to unpack

This feature has probably really confused you if you ever mistakenly put parenthesis around the parameters to a lambda.  In that case the lambda expression will take only one argument, and that argument will be a tuple.

>>> a = lambda (a,b): a+b
>>> a(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes exactly 1 argument (2 given)
>>> a((1,2))
3

Monday, December 27, 2010

change class (not instance) __repr__ with metaclass

>>> class PrintMeta(type):
...    def __repr__(cls):
...       return "Hello World!"
...
>>> class A(object):
...    __metaclass__ = PrintMeta
...
>>> A
Hello World!

metaclass abuse: conjoined classes

>>> class ConjoinedMeta(type):
...    def __new__(cls, name, bases, attrs):
...       for k,v in attrs.items():
...          setattr(ConjoinedMeta, k, v)
...       return ConjoinedMeta
...
>>> class A(object):
...    __metaclass__ = ConjoinedMeta
...    a = "a"
...
>>> A
<class '__main__.ConjoinedMeta'>
>>> class B(object):
...    __metaclass__ = ConjoinedMeta
...
>>> B.a
'a'
>>> B.b = 'b'
>>> A.b
'b'

Monday, December 13, 2010

extend list while iterating


>>> a = [1,2]
>>> for n in a:
...    print n, a, [n-1]*n
...    a += [n-1]*n
...
1 [1, 2] [0]
2 [1, 2, 0] [1, 1]
0 [1, 2, 0, 1, 1] []
1 [1, 2, 0, 1, 1] [0]
1 [1, 2, 0, 1, 1, 0] [0]
0 [1, 2, 0, 1, 1, 0, 0] []
0 [1, 2, 0, 1, 1, 0, 0] []
Note that this only works for lists since they have a defined ordering, and so the iterator doesn't "lose its place" if the list is modified.  For sets and dicts, this type of construct will result in "RuntimeError: changed size during iteration."