My Python Gems
November 05, 2008
Posted in Code.
1 comment.
I loved reading both of the Eric‘s blog posts and I suggest you check them out. I’m always on the lookout for little tricks or “hidden” features of Python, and I thought I would toss a few of my favorites into the hat along with what they’ve shared.
1. Merging 2 dictionaries
Here is how I merge 2 dictionaries quickly and easily: dict(defaults, **override). It takes 2 dictionaries, and where the second differs from the first, updates the values of matching keys. I find this useful for setting a bunch of defaults, and allowing the user to override them. This is the most memory efficient, quickest way of doing this and hey, it’s so much less code to write. Here’s an example:
class Example(object):
def __init__(self, **kwargs):
self.defaults = {
'up_is_down': True,
'vim_bindings': True,
'django_is_great': True,
}
self.options = dict(self.defaults, **kwargs)
In [1]: ex = Example(up_is_down=False)
In [2]: ex.defaults
Out[2]: {'django_is_great': True, 'up_is_down': True, 'vim_bindings': True}
In [3]: ex.options
Out[3]: {'django_is_great': True, 'up_is_down': False, 'vim_bindings': True}
2. Unpack dictionaries, lists, tuples as arguments
This is related to the above, you may be confused how that works. You can pass a dictionary to a function, and unpack it as arguments. Most people know that function(*args, **kwargs) (note: you can name *args, kwargs whatever you want, the important part being the asterisks) allows you to accept positional arguments and keyword (named) arguments without explicitly telling the function what to expect, which will give you a list of positional args, and a dict of keyword args. But when you pass to a function using the same syntax, you are unpacking the dictionary/list/tuple as positional and keyword (named) arguments.
In [1]: def coordinates(x,y):
....: return "%s, %s" % (x,y)
....:
In [2]: point = (2, 10)
In [3]: point2 = {'y':7, 'x':1}
In [4]: coordinates(*point)
Out[4]: '2,10'
In [5]: coordinates(**point2)
Out[5]: '1,7'
3. Dictionary get method
If you try to access a key in a dictionary like dict['foo'] and the key doesn’t exist, it raises an exception. If you instead use dict.get('foo'), it will return None if the key doesn’t exist. Now, if you pass a second argument, it will be returned instead. dict.get('foo', 'bar') will return 'bar' if there is no ‘foo’.
In [1]: d = {}
In [2]: d['foo']
------------------------------------
<type 'exceptions.KeyError'>: 'foo'
In [3]: d.get('foo')
In [4]: d.get('foo', 'bar')
Out[4]: 'bar'
4. Re-raising exceptions
This allows you to see if the exception is fatal or do whatever else you want to do, and if it is go ahead and raise it. The neat thing is, it will raise with the original traceback intact!
try:
foo()
except FooException, e:
if is_fatal(e):
raise
handle_nonfatal(e)
5. import operator
This gives you access to the intrinsic operators in Python, via functions. I’ve used this for combining a tuple or list of dynamically created Q objects in Django. Here is a really simple example:
In [1]: import operator
In [2]: reduce(operator.add, [1,2,3,4,5,6,7,])
Out[2]: 28
6. Slice step/increment used for reversing
I’d imagine this is the most well known of my gems, but I’m still surprised by how many people don’t know about it, or don’t know that it can be used to reverse. Using a negative number with the step argument will return the slice in reverse. This works with lists, strings, tuples, etc. Obviously, you can do more than just reverse with the step argument. Here is an example of it, in use.
In [1]: "hello"[::-1]
Out[1]: 'olleh'
In [2]: [1,2,3,4,5,6,][::-1]
Out[2]: [6, 5, 4, 3, 2, 1]
In [3]: (1,2,3,4,5,6)[::-1]
Out[3]: (6, 5, 4, 3, 2, 1)
In [4]: "hello"[::-2]
Out[4]: 'olh'
In [5]: "hello"[::2]
Out[5]: 'hlo'
In [6]: range(0,30)[::5]
Out[6]: [0, 5, 10, 15, 20, 25]
Comments
Justin Lilly
Nov 10, 2008
Permalink
To the top!
Post a comment!
I really like tip#3. I was recently asked that at a job interview. getattr was the specific question, but it is basically the same thing :)