Archive
rstripping Simon Pegg: Don’t use rstrip for file extension removal
In Python, what does '/path/to/my/simon_pegg.jpeg'.rstrip('.jpeg')
yield?
If you guessed '/path/to/my/simon_pegg'
– good try, but not quite right. The real answer is '/path/to/my/simon_'
.
Despite what you might intuitively think, rstrip
does NOT strip off a substring from the end of a string. Instead, it eliminates all of the characters from the end of the string that are in the argument. (Note that this is not mutating the string; it returns a copy of the string.)
Since pegg
contains the characters that are in .jpeg
, it is eliminated as well.
While this behavior is documented, it may be surprising.
Why does it matter? There are many instances of people attempting to use this rstrip
approach to strip off a file extension. For instance, you might be converting an image from one filetype to another, and need to construct the final path. Or you might want to rename a bunch of files to have consistent extensions (jpeg
→ jpg
).
This buggy implementation of stripping a file extension is tricky because most of the time it works – but it works by coincidence (the file name happens not to end with the characters in the file extension).
Github is rife with examples of people making this same mistake. For instance,
main.py: name = str(name).rstrip(".tif")
fixname.py:os.rename(i.path,
i.path.rstrip('.gzip').rstrip('.gz') + '.gzip')
selector.py:l = [i.rstrip(".jpg") for i in k]
The Go language has the same semantics for its TrimRight function. This leads to the same sort of mistakes when people use it to trim file names. For instance,
filehelper.go: filename := strings.TrimRight(f.Name( ".pdf")
latest_images.go: idStr := strings.TrimRight(f.Name(), ".jpg")
The lessons to be learned from this are,
- Read the documentation of the library functions you use.
- Test your code, and not just of the happy paths. Good tests should try to break your implementation and exercise edge cases.
(Hat tip to my colleague Fredrik Lundh who alerted me to this problem and inspired this post)
Reblog: “Top 10 Mistakes that Python Programmers Make”
Martin Chikilian from Toptal rounds up some common mistakes that Python programmers make.
I have made mistake #1 on multiple occasions:
Common Mistake #1: Misusing expressions as defaults for function arguments
Python allows you to specify that a function argument is optional by providing a default value for it. While this is a great feature of the language, it can lead to some confusion when the default value is mutable. For example, consider this Python function definition:
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified
... bar.append("baz") # but this line could be problematic, as we'll see...
... return bar
A common mistake is to think that the optional argument will be set to the specified default expression each time the function is called without supplying a value for the optional argument. In the above code, for example, one might expect that calling foo() repeatedly (i.e., without specifying a bar argument) would always return ‘baz’, since the assumption would be that each time foo() is called (without a bar argument specified) bar is set to [] (i.e., a new empty list).
I don’t remember for sure, but I’ve probably done something like #5, modifying a list while iterating through it.
If you write Python code, the rest of the article is worth a read
Music hack roundup
Disclaimer: the opinions expressed are my own and do not represent that of my employer, Google
I love music. And programming. I really like pieces of technology that either create new or modify existing pieces of music. Here I detail some of my favorite projects I’ve found in the past few years.
Songsmith
Songsmith is a project from Microsoft Research. From the project description page:
Songsmith generates musical accompaniment to match a singer’s voice. Just choose a musical style, sing into your PC’s microphone, and Songsmith will create backing music for you. Then share your songs with your friends and family, post your songs online, or create your own music videos.
There was a commercial that detailed how the project worked, but what I found really great was what the community did with it. They fed the vocals of famous songs through the software to see what sort of music came out. The results are, ahem, mixed.
First, one that I think sounds pretty interesting – a swing version of Katy Perry’s I Kissed a Girl
Then going into the realm of hilarious:
We Will Rock You – Queen Vs Songsmith
Mortorhead’s Ace of Spades
Nirvana’s In Bloom
Enter Sandman
Roxanne
The Beatle’s A Day in the Life
The Swinger
According to musicmachinery.com’s writeup,
The Swinger is a bit of python code that takes any song and makes it swing.
If you’re not into music theory (or old music) you might not know what constitutes swing. The following video (only need to watch the first 30 seconds or so) is a great example of the difference of straight vs swing styles:
As you can hear, it sounds very different. The first half of each beat is stretched out, and the second half is shrunk down. It sounds even more different when you start listening to familiar songs converted from straight to swing, or vice versa. While most of the links have died, Daft Punk’s Around The World still plays, as does Jefferson Airplane’s White Rabbit.
The source code is available at https://github.com/echonest/remix/blob/master/examples/swinger/swinger.py.
Autocanonizer
From musicmachinery.com’s writeup of The Autocanonizer:
It takes any song and tries to make a canon out of it. A canon is a song that can be played against a copy of itself.
Wikipedia has a bit more information on what exactly a Canon is:
In music, a canon is a contrapuntal compositional technique that employs a melody with one or more imitations of the melody played after a given duration (e.g., quarter rest, one measure, etc.). The initial melody is called the leader (or dux), while the imitative melody, which is played in a different voice, is called the follower (or comes). The follower must imitate the leader, either as an exact replication of its rhythms and intervals or some transformation thereof (see “Types of canon”, below). Repeating canons in which all voices are musically identical are called rounds – “Row, Row, Row Your Boat” and “Frère Jacques” being widely known examples. An example of a classical strict canon is the Minuet of Haydn’s String Quartet in D Minor, Op. 76, No. 2 (White 1976, 66).
With that in mind, here are some example.
My favorite is Adele’s Someone Like You. This one sounds close to a round.
- Over The Rainbow – starts rough but 30 seconds in it sounds good
- The Fox – I like it. Lots of self harmonizing. The doubled up chorus actually works. It gets out of sync with itself at some points
- Take Five – demonstrates that the technique works with odd meter too. Not perfectly lined up at some points
See all the examples available at http://static.echonest.com/autocanonizer/loader.html
Source code available at: https://github.com/plamere/autocanonizer
Hat tip to Greg Linden whose Google+ post alerted me to this, and reminded me of these other projects I’d seen before.
MajorVsMinor
MajorVsMinor is a slight departure from the others I’ve listed because there is a human in the loop – it’s not all algorithmic. From Oleg Berg’s description from olegberg.com
Hello! I am Oleg Berg, a musician from Donetsk, Ukraine. I digitally re-edit famous compositions altering harmonic scale, and I call this experimental music project «Major versus Minor». It may sound surprising and unusual, but it is always interesting. Listen to the music videos below. And please donate to keep the project going
[…]
I by no means intend to enhance the famous music hits as I rework them; they are perfect already. I simply imagine what would it sound like, had the author written it in another mood. And it appears, I succeed in my imaginations.
Again, if you’re not a music nerd you might not know what the difference between major key and minor is. In general picture minor = sad, major = happy. You’ll instantly hear the difference in these versions.
First, a must if you’re an Arrested Development fan.
“Final Countdown in Major key”
My favorite comment from MYxxLxxCHIBI1:
I was literally coming down here to say that myself. GOB finally got accepted to the Alliance of Magicians
Maybe my favorite one –
“Be Worry, Don’t Happy”: Minor Key
I like this one too.
Jingle Bells
“Hey Jude” in minor key
See the whole channel at https://www.youtube.com/user/MajorVsMinor
Since this isn’t a software project per se, there is no link to the source code. According to Asshat8182’s comment on Smells Like Teen Spirit in Major key (with a name like that, he must be a reliable source of information), the way it’s accomplished is
The ‘somehow’ is called Celemony Melodyne. Specifically the DNA function
According to Wikipedia:
Celemony Software GmbH is a German musical software company that specializes in digital audio pitch correction software. It produces Melodyne, an industry standard audio pitch modification tool similar to Auto-Tune
Conclusion
I hope you’ve found this short roundup of music hacks interesting. There are some very creative people out there. If you find what they’re doing interesting, please let them know and/or donate so they’ll keep making great stuff.
Solving Scramble With Friends – a tale of three data structures
Solving Scramble with Friends – A tale of three data structures
This post aims to illustrate how to solve Scramble With Friends/Boggle and the techniques and data structures necessary. In particular, we will see how to prune an enormous search space, how to use recursion to simplify the task, and the relative performance of three different data structures: an unsorted list, a sorted list with binary search, and a trie.
Problem statement
Given an N x N board, (4×4 in case of Boggle and Scramble with Friends), find all of the paths through the board which create a valid word. These paths must use each tile at most once and be contiguous; tile N must be adjacent to tile N – 1 in any of 8 directions (North, Northeast, East, Southeast, South, Southwest, West, Northwest).
In this example, ANT is a valid solution in the bottom left corner, but NURSE is not because the Ns are not located next to the U.
Most naive approach
First, imagine that we have a method that, given a path through the board returns all valid words that starts with that path, called DoSolve
. For instance, DoSolve(['Qu'])
would return all the valid words (and their locations) that start with Qu. DoSolve(['N', 'E'])
would return all the valid paths that start with N followed by an E. With such a method, it is trivial to solve the problem. In pseudocode:
Solve(board):
solutions = empty list
for each location on board:
append DoSolve(location) to solutions
return solutions
The tricky part is, how do we make DoSolve work? Remember that it must
- return only valid words
- return only words that are formed from contiguous tiles
- must not repeat any tiles.
The easiest way to solve this problem is through recursion. As you probably remember, recursion is simply when a method calls itself. In order to avoid an infinite loop (and eventual stack overflow), there must be some sort of stopping criteria. As it was taught to me, you have to be sure that the problem becomes smaller at each step, until it becomes so small as to be trivially solved. Here’s that basic algorithm:
DoSolve(board, tiles_used)
solutions = empty list
current word = empty string
for letter in tiles_used:
append letter to current word
# 1 - ensure that the word is valid
if the current word is a valid word:
append current word to solutions
most recent tile = last tile in tiles_used
# 2 - ensure that the next tile we use is adjacent to the last one
for each tile adjacent to most recent tile:
# 3 - ensure that we do not repeat any tiles
if tile is on the board and tile hasn't been used previously:
new_path = append tile to copy of tiles_used list
solutions_starting_from_tile = DoSolve(board, new_path)
append solutions_starting_from_tile to solutions
return solutions
This will work, but it suffers from an incredible flaw. Do you see it?
The problem is that this method will waste an inordinate amount of time exhaustively searching the board for solutions even when the current path is completely useless. It will continue to search for words starting with QuR, QuRE, etc., etc., even though no words in the English language start with these letters. The algorithm is still correct, but it can be optimized very simply.
Pruning the search space
Those with some algorithms background might recognize the code above as a modified form of a depth first search. As described previously, a depth first search will exhaustively explore every possible path through the board. We can vastly improve the efficiency of this algorithm by quitting the search early when we know it won’t be fruitful. If we know all of the valid words, we can quit when we know that no word starts with the current string. Thus in the previous example, if the current word built up so far were “QuR”, the algorithm could determine that no words start with QuR and thus fail fast and early. This optimization will not affect the correctness of the algorithm because none of the potential paths we eliminate could possibly lead to a valid word; by constraint #1 this means that none of those paths would have ended up in the list of solutions in the first place.
How much does this save? On random 3×3 boards, The fastest implementation I have is sped up by a factor of 75. Solving 4×4 boards without pruning is infeasible.
Basic Python implementation
Assume as a black box we have two methods IsWord(string)
and HasPrefix(string)
. The pseudocode above can be expressed with Python (forgive the slightly modified parameter list; I found it easier to write it this way):
def DoSolve(self, board, previous_locations, location, word_prefix): """Returns iterable of FoundWord objects. Args: previous_locations: a list of already visited locations location: the current Location from where to start searching word_prefix: the current word built up so far, as a string """ solutions = [] new_word = word_prefix + board[location.row][location.col] previous_locations.append(location) if PREFIX_PRUNE and not self.HasPrefix(new_word): if DEBUG: print 'No words found starting with "%s"' %(new_word) return solutions # This is a valid, complete words. if self.IsWord(new_word): new_solution = FoundWord(new_word, previous_locations) if DEBUG: print 'Found new solution: %s' %(str(new_solution)) solutions.append(new_solution) # Recursively search all adjacent tiles for new_loc in location.Adjacent(): if board.IsValidLocation(new_loc) and new_loc not in previous_locations: # make a copy of the previous locations list so our current list # is not affected by this recursive call. defensive_copy = list(previous_locations) solutions.extend(self.DoSolve(board, defensive_copy, new_loc, new_word)) else: if DEBUG: print 'Ignoring %s as it is invalid or already used.' %(str(new_loc)) return solutions
Data structures
The data structure and algorithms used to implement the IsWord
and HasPrefix
methods are incredibly important.
I will examine three implementations and discuss the performance characteristics of each:
- Unsorted list
- Sorted list with binary search
- Trie
Unsorted list
An unsorted list is a terrible data structure to use for this task. Why? Because it leads to running time proportional to the number of elements in the word list.
class UnsortedListBoardSolver(BoardSolver): def __init__(self, valid_words): self.valid_words = valid_words def HasPrefix(self, prefix): for word in self.valid_words: if word.startswith(prefix): return True return False def IsWord(self, word): return word in self.valid_words
While this is easy to understand and reason about, it is extremely slow, especially for a large dictionary (I used approximately 200k words for testing).
Took 207.697 seconds to solve 1 boards; avg 207.697 with <__main__.UnsortedListBoardSolver object at 0x135f170>
(This time is with the pruning turned on).
Sorted list
Since we know the valid words ahead of time, we can take advantage of this fact and sort the list. With a sorted list, we can perform a binary search and cut our running time from O(n) to O(log n).
Writing a binary search from scratch is very error prone; in his classic work Programming Pearls, Jon Bently claims that fewer than 10% of programmers can implement it correctly. (See blog post for more).
Fortunately, there’s no reason whatsoever to write our own binary search algorithm. Python’s standard library already has an implementation in its bisect module. Following the example given in the module documentation, we get the following implementation:
class SortedListBoardSolver(BoardSolver): def __init__(self, valid_words): self.valid_words = sorted(valid_words) # http://docs.python.org/library/bisect.html#searching-sorted-lists def index(self, a, x): 'Locate the leftmost value exactly equal to x' i = bisect.bisect_left(a, x) if i != len(a) and a[i] == x: return i raise ValueError def find_ge(self, a, x): 'Find leftmost item greater than or equal to x' i = bisect.bisect_left(a, x) if i != len(a): return a[i] raise ValueError def HasPrefix(self, prefix): try: word = self.find_ge(self.valid_words, prefix) return word.startswith(prefix) except ValueError: return False def IsWord(self, word): try: self.index(self.valid_words, word) except ValueError: return False return True
Running the same gauntlet of tests, we see that this data structure performs far better than the naive, unsorted list approach.
Took 0.094 seconds to solve 1 boards; avg 0.094 with <__main__.SortedListBoardSolver object at 0x135f1d0>
Took 0.361 seconds to solve 10 boards; avg 0.036 with <__main__.SortedListBoardSolver object at 0x135f1d0>
Took 2.622 seconds to solve 100 boards; avg 0.026 with <__main__.SortedListBoardSolver object at 0x135f1d0>
Took 25.065 seconds to solve 1000 boards; avg 0.025 with <__main__.SortedListBoardSolver object at 0x135f1d0>
Trie
The final data structure I want to illustrate is that of the trie. The Wikipedia article has a lot of great information about it.
From Wikipedia:
In computer science, a trie, or prefix tree, is an ordered tree data structure that is used to store an associative array where the keys are usually strings. Unlike a binary search tree, no node in the tree stores the key associated with that node; instead, its position in the tree defines the key with which it is associated. All the descendants of a node have a common prefix of the string associated with that node, and the root is associated with the empty string. Values are normally not associated with every node, only with leaves and some inner nodes that correspond to keys of interest.
Snip:
Looking up a key of length m takes worst case O(m) time. A BST performs O(log(n)) comparisons of keys, where n is the number of elements in the tree, because lookups depend on the depth of the tree, which is logarithmic in the number of keys if the tree is balanced. Hence in the worst case, a BST takes O(m log n) time. Moreover, in the worst case log(n) will approach m. Also, the simple operations tries use during lookup, such as array indexing using a character, are fast on real machines
Again, I don’t want to reinvent what’s already been done before, so I will be using Jeethu Rao’s implementation of a trie in Python rather than rolling my own.
Here is a demonstration of its API via the interactive prompt:
>>> import trie >>> t = trie.Trie() >>> t.add('hell', 1) >>> t.add('hello', 2) >>> t.find_full_match('h') >>> t.find_full_match('hell') 1 >>> t.find_full_match('hello') 2 >>> t.find_prefix_matches('hello') [2] >>> t.find_prefix_matches('hell') [2, 1]
Unfortunately, there’s a bug in his code:
>>> t = trie.Trie() >>> t.add('hello', 0) >>> 'fail' in t False >>> 'hello' in t True # Should be false; 'h' starts a string but # it is not contained in data structure >>> 'h' in t True
His __contains__
method is as follows:
def __contains__( self, s ) : if self.find_full_match(s,_SENTINEL) is _SENTINEL : return False return True
The find_full_match
is where the problem lies.
def find_full_match( self, key, fallback=None ) : "' Returns the value associated with the key if found else, returns fallback "' r = self._find_prefix_match( key ) if not r[1] and r[0]: return r[0][0] return fallback
_find_prefix_match
returns a tuple of node that the search terminated on, remainder of the string left to be matched. For instance,
>>> t._find_prefix_match('f') [[None, {'h': [None, {'e': [None, {'l': [None, {'l': [None, {'o': [0, {}]}]}]}]}]}], 'f'] >>> t.root [None, {'h': [None, {'e': [None, {'l': [None, {'l': [None, {'o': [0, {}]}]}]}]}]}]
This makes sense, ‘f’ doesn’t start any words in the trie containing just the word ‘hello’, so the root is returned with the ‘f’ string that doesn’t match. find_full_match
correctly handles this case, since r[1] = ‘f’, not r[1] = False, and the fallback is returned. That fallback is used by contains to signify that the given string is not in the trie.
The problem is when the string in question starts a valid string but is itself not contained in the trie. As we saw previously, ‘h’ is considered to be in the trie.
>>> r = t._find_prefix_match('h') >>> r [[None, {'e': [None, {'l': [None, {'l': [None, {'o': [0, {}]}]}]}]}], "] >>> r[0] [None, {'e': [None, {'l': [None, {'l': [None, {'o': [0, {}]}]}]}]}] >>> r[1] " >>> bool(not r[1] and r[0]) True >>> r[0][0] # None
The issue is that his code does not check that there is a value stored in the given node. Since no such value has been stored, the code returns None, which is not equal to the SENTINEL_ value that his __contains__
method expects. We can either change findfullmatch to handle this case correctly, or change the __contains__
method to handle the None result as a failure. Let’s modify the find_full_match
method to obey it’s implied contract (and be easier to understand):
def find_full_match( self, key, fallback=None ) : "' Returns the value associated with the key if found else, returns fallback "' curr_node, remainder = self._find_prefix_match(key) stored_value = curr_node[0] has_stored_value = stored_value is not None if not remainder and has_stored_value: return stored_value return fallback
Let’s make sure this works:
>>> reload(trie) <module 'trie' from 'trie.py'> >>> t = trie.Trie() >>> t.add('hello', 2) >>> 'f' in t False >>> 'hell' in t False >>> 'hello' in t True
OK, with that minor patch, here’s a first pass implementation of the solution using the trie:
class TrieBoardSolver(BoardSolver): def __init__(self, valid_words): self.trie = trie.Trie() for index, word in enumerate(valid_words): # 0 evaluates to False which screws up trie lookups; ensure value is 'truthy'. self.trie.add(word, index+1) def HasPrefix(self, prefix): return len(self.trie.find_prefix_matches(prefix)) > 0 def IsWord(self, word): return word in self.trie
Unfortunately, this is slow. How slow?
Took 2.626 seconds to solve 1 boards; avg 2.626 with <__main__.TrieBoardSolver object at 0x135f070>
Took 22.681 seconds to solve 10 boards; avg 2.268 with <__main__.TrieBoardSolver object at 0x135f070>
While this isn’t as bad as the unsorted list, it’s still orders of magnitudes slower than the binary search implementation.
Why is this slow? Well, it’s doing a whole lot of unnecessary work. For instance, if we want to determine if ‘h’ is a valid prefix, this implementation will first construct the list of all words that start with h, only to have all that work thrown away when we see that the list is not empty.
A much more efficient approach is to cheat a little and use the previously discussed method _find_prefix_match
which returns the node in the tree that the search stopped at and how much of the search string was unmatched.
By using this method directly, we can avoid creating the lists of words which we then throw away. We modify the HasPrefix method to the following:
def HasPrefix(self, prefix): curr_node, remainder = self.trie._find_prefix_match(prefix) return not remainder
With this optmization, the trie performance becomes competitive with the binary search:
Took 0.027 seconds to solve 1 boards; avg 0.027 with <__main__.TrieBoardSolver object at 0x135f230>
Took 0.019 seconds to solve 1 boards; avg 0.019 with <__main__.SortedListBoardSolver object at 0x135f330>
Took 0.199 seconds to solve 10 boards; avg 0.020 with <__main__.TrieBoardSolver object at 0x135f230>
Took 0.198 seconds to solve 10 boards; avg 0.020 with <__main__.SortedListBoardSolver object at 0x135f330>
Took 2.531 seconds to solve 100 boards; avg 0.025 with <__main__.TrieBoardSolver object at 0x135f230>
Took 2.453 seconds to solve 100 boards; avg 0.025 with <__main__.SortedListBoardSolver object at 0x135f330>
It is still slower, but not nearly as bad as before.
Solving the board in the screenshot
With all this machinery in place, we can run the original board in the screenshot through the algorithms:
words = ReadDictionary(sys.argv[1]) print 'Read %d words' %(len(words)) trie_solver = TrieBoardSolver(words) sorted_list_solver = SortedListBoardSolver(words) b = Board(4, 4) b.board = [ ['l', 'qu', 'r', 'e'], ['s', 'l', 'u', 's'], ['a', 't', 'i', 'c'], ['n', 'r', 'e', 'n'] ] print 'Solving with binary search' sorted_solutions = sorted_list_solver.Solve(b) print 'Solving with trie' trie_solutions = trie_solver.Solve(b) # Results should be exactly the same assert sorted_solutions == trie_solutions for solution in sorted(sorted_solutions): print solution words = sorted(set([s.word for s in sorted_solutions])) print words
The results appear after the conclusion.
Conclusion
I have presented both pseudocode and Python implementations of an algorithm for solving the classic Boggle/Scramble With Friends game. In the process, we saw such concepts as recursion, depth first search, optimizing code through pruning unfruitful search branches, and the importance of using the right data structure. This post does not aim to be exhaustive; I hope I have piqued your interest for learning more about tries and other lesser known data structures.
For results, the patched trie implementation, and the driver program, see below. To run the driver program, pass it a path to a list of valid words, one per line. e.g.
python2.6 solver.py /usr/share/dict/words
https://gist.github.com/2833942#file_trie.py
For mobile users, the link can be found at https://gist.github.com/2833942
Hello {planet_name}: Creating strings with dynamic content in Python
2012-05-16
The ability to create strings with dynamic content is very important in creating applications. Python makes this easy, but it’s not always clear what the correct approach is. Let us go through four ways of creating strings:
- Implicit concatenation
- Explicit concatenation
- String formatting via % operator
- String formatting via
format
method
Implicit concatenation
If whitespace alone separates adjacent string literals, they will be concatenated together.
>>> 'hello' 'world' 'helloworld'
This can be useful if you have a long string that you want to break up
>>> a_long_string = ('hello this is a very very very very' ... 'long string') >>> print a_long_string hello this is a very very very verylong string
Implicit concatenation does not work for inserting variables into strings:
>>> name = 'Nick' >>> 'hello' name File "<stdin>", line 1 'hello' name
In that case, we need to be more explicit.
Explicit concatenation
You can also use the +
operator to concatenate strings.
>>> 'hello' + 'world' 'helloworld'
Unlike in Java, types are not automatically coerced; it is an error to attempt to concatenate non-strings to a string:
>>> num_examples = 4 >>> 'I have ' + num_examples + ' examples to discuss.' Traceback (most recent call last): File "<input>", line 1, in <module> TypeError: cannot concatenate 'str' and 'int' objects
You must explicitly convert non-string types into strings before using this operator.
>>> 'I have ' + str(num_examples) + ' examples to discuss.' 'I have 4 examples to discuss.'
String formatting via % operator
Creating longer strings by concatenating together different substrings works, but it’s not the most elegant solution. It is a common mistake to forget to put a space before or after whatever is inserted in the middle of two other strings, wasting time on the programmer’s part. It is also somewhat hard to scan while reading the source code; the + signs can distract from what the string is trying to say.
If you have used C before, you are probably familiar with printf
and sprintf
, which allow you to embed special characters within your strings which are subsituted with later arguments. For instance,
printf("hello %s\n", "world");
would produce “hello world”. Python has this feature built in to strings with the % operator. You can read more in depth about the [String Formatting Operations][] and its syntax, but at the very least you should memorize the flags %d (for integer types), %f (for floating point), and %s (for strings). For instance,
>>> 'I have %d things to talk about' %num_topics 'I have 4 things to talk about'
If you have more than one substitution to make, you must surround the substituted values in parentheses:
>>> 'I have %d things to talk about. Number 1: %s' %(num_topics, 'Implicit concatenation') 'I have 4 things to talk about. Number 1: Implicit concatenation'
This is a very powerful technique, especially when you learn the formatting flags that this operator supports. For instance,
>>> 'It is %.2f degrees out' %(98.63483) # display 2 decimal places 'It is 98.63 degrees out'
This technique does not work as well when there are many substitutions to make:
query = """SELECT %s FROM %s WHERE %s GORUP BY %s ORDER BY %s""" %(columns, table, where_clause, order_by_clause, group_by_clause)
Did you catch the mistake? (Hint – check the order of args in parentheses) It’s easy to make a mistake like this in production. If you have more than 2 or 3 substitutions, I recommend the following technique – string format.
String formatting via format
function
Strings have a format function which can also play the part of variable substiution. The benefit of this approach is that each substitution is named, and it is impossible to provide the arguments in the wrong order.
query = """SELECT {columns} FROM {table} WHERE {where_clause} GORUP BY {group_by_clause} ORDER BY {order_by_clause}""".format(columns = 'sum(num_users) AS actives', table = '1_day_actives', where_clause = 'country = "US"', order_by_clause = 'actives', # Note the wrong order - it doesn't matter, just as for # providing keyword arguments group_by_clause = 'actives')
If you prefer, you can use a dictionary to power the variable replacement.
data_dict = { 'columns': 'sum(num_users) AS actives', 'table': '1_day_actives', 'where_clause': 'country = "US"', 'order_by_clause': 'actives', 'group_by_clause': 'actives' } query = """SELECT {columns} FROM {table} WHERE {where_clause} GORUP BY {group_by_clause} ORDER BY {order_by_clause}""".format(**data_dict)
Note the format
method is only available in Python 2.6 and newer. As the documentation states,
This method of string formatting is the new standard in Python 3, and should be preferred to the % formatting described in String Formatting Operations in new code.
Hopefully I have shown that this is easier to read and less error prone than the alternatives.
Conclusion
It’s easy to get stuck in a rut and do things exactly the same way, even when alternatives exist. I only just recently learned about the string.format function, and find it preferable to all of the alternatives I have laid out. I hope you also find it useful.
Three ways of creating dictionaries in Python
Dictionaries are the fundamental data structure in Python, and a key tool in any Python programmer’s arsenal. They allow O(1) lookup speed, and have been heavily optimized for memory overhead and lookup speed efficiency.
Today I”m going to show you three ways of constructing a Python dictionary, as well as some additional tips and tricks.
Dictionary literals
Perhaps the most commonly used way of constructing a python dictionary is with curly bracket syntax:
d = {"age":25}
As dictionaries are mutable, you need not know all the entries in advance:
# Empty dict d = {} # Fill in the entries one by one d["age"] = 25
From a list of tuples
You can also construct a dictionary from a list (or any iterable) of key, value pairs. For instance:
d = dict([("age", 25)])
This is perhaps most useful in the context of a list comprehension:
class Person(object): def __init__(self, name, profession): self.name = name self.profession = profession people = [Person("Nick", "Programmer"), Person("Alice","Engineer")] professions = dict([ (p.name, p.profession) for p in people ]) >>> print professions {"Nick": "Programmer", "Alice": "Engineer"}
This is equivalent, though a bit shorter, to the following:
people = [Person("Nick", "Programmer"), Person("Alice","Engineer")] professions = {} for p in people: professions[p.name] = p.profession
This form of creating a dictionary is good for when you have a dynamic rather than static list of elements.
From two parallel lists
This method of constructing a dictionary is intimately related to the prior example. Say you have two lists of elements, perhaps pulled from a database table:
# Static lists for purpose of illustration names = ["Nick", "Alice", "Kitty"] professions = ["Programmer", "Engineer", "Art Therapist"]
If you wished to create a dictionary from name to profession, you could do the following:
professions_dict = {} for i in range(len(names)): professions_dict[names[i]] = professions[i]
This is not ideal, however, as it involves an explicit iterator, and is starting to look like Java. The more Pythonic way to handle this case would be to use the zip
method, which combines two iterables:
print zip(range(5), ["a","b","c","d","e"]) [(0, "a"), (1, "b"), (2, "c"), (3, "d"), (4, "e")] names_and_professions = zip(names, professions) print names_and_professions [("Nick", "Programmer"), ("Alice", "Engineer"), ("Kitty", "Art Therapist")] for name, profession in names_and_professions: professions_dict[name] = profession
As you can see, this is extremely similar to the previous section. You can dispense the iteration, and instead use the dict
method:
professions_dict = dict(names_and_professions) # You can dispence the extra variable and create an anonymous # zipped list: professions_dict = dict(zip(names, professions))
Further reading
Python Gotcha: Word boundaries in regular expressions
TL;DR
Be careful trying to match word boundaries in Python using regular expressions. You have to be sure to either escape the match sequence or use raw strings.
Word boundaries
Word boundaries are a great way of performing regular expression searches for whole words while avoiding partial matches. For instance, a search for the regular expression “the” would match both the word “the” and the start of the word “thesaurus”.
>>> import re >>> re.match("the", "the") # matches >>> re.match("the", "thesaurus") # matches
The way to match a word boundary is with ‘\b’, as described in the Python documentation. I wasted a few minutes wrestling with trying to get this to work.
>>> re.match("\bthe\b", "the") # no match
It turns out that \b is also used as the backspace control sequence. Thus in order for the regular expression engine to interpret the word boundary correctly, you need to escape the sequence:
>>> re.match("\\bthe\\b", "the") # match
You can also use raw string literals and avoid the double backslashes:
>>> re.match(r"\bthe\b", "the") # match
In case you haven’t seen the raw string prefix before, here is the relevant documentation:
String literals may optionally be prefixed with a letter ‘r’ or ‘R’; such strings are called raw strings and use different rules for interpreting backslash escape sequences.
Conclusion
Make sure you are familiar with the escape sequences for strings in Python, especially if you are dealing with regular expressions whose special characters might conflict. The Java documentation for regular expressions makes this warning a bit more explicit than Python’s:
The string literal “\b”, for example, matches a single backspace character when interpreted as a regular expression, while “\\b” matches a word boundary.
Hopefully this blog post will help others running into this issue.
Car Talk Puzzler #5: The Perfect Square Dance
PUZZLER: The Perfect Square Dance!
Sally invited 17 guests to a dance party. She assigned each guest a number from 2 to 18, keeping 1 for herself. The sum of each couple’s numbers was a perfect square. What was the number of Sally’s partner?
The fifth in my ongoing series of solving Car Talk Puzzlers with my programming language of choice. I’m using Python again, just like last time.
There are a few pieces to this. The first is, how do we generate a list of all possible pairs that match the perfect square constraint? With list comprehensions and a helper function this is easy.
def perfect_square(n): return math.sqrt(n) == int(math.sqrt(n)) # range creates a list that's exclusive of last number. Thus to go from 1 to n, # use range(1, n+1) guests = range(1, num_guests + 1) # By enforcing the x# a whole bunch of equivalent pairs (e.g. (3,6) and (6,3)). pairs = [(x,y) for x in guests for y in guests if x<y and perfect_square(x+y)] >>> guests [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] >>> pairs [(1, 3), (1, 8), (1, 15), (2, 7), (2, 14), (3, 6), (3, 13), (4, 5), (4, 12), (5, 11), (6, 10), (7, 9), (7, 18), (8, 17), (9, 16), (10, 15), (11, 14), (12, 13)]
>>> itertools.combinations([1,2,3,4,5], 2) # This is a sort of generator object which lazily returns the values as needed. # To force them to all be evaluated at once, to see how this works, # wrap the call in a list function. >>>list(itertools.combinations([1,2,3,4,5],2)) [(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
This performs exactly as how we would expect. Note that I want combinations rather than permutations because the order does not matter.
Yeah, I think we can handle checking 48620 combinations.
def flatten(nested): """ Flatten one level of nesting. Returns a generator object For instance: list(flatten([(1,3),(5,6)])) --> [1,3,5,6] """ return itertools.chain.from_iterable(nested) def all_guests_present_once(combination): """ Returns whether each guest is present once Combination is a list of tuples, e.g. [(1,5),(7,8)] """ flattened = list(flatten(combination)) return len(set(flattened)) == len(flattened) >>> all_guests_present_once([(1,3),(4,5)]) True >>> all_guests_present_once([(1,3),(3,6)]) False
OK we’re ready to throw it all together.
def dance_arrangement(num_guests): """ Returns a valid pairing for all guests if possible, else an empty set """ # Clearly you need an even number of guests to have everyone paired if num_guests % 2 == 1: return [] else: # range creates a list that's exclusive of last number. Thus to go from 1 to n, # use range(1, n+1) guests = range(1, num_guests + 1) # By enforcing the x # a whole bunch of equivalent pairs (e.g. (3,6) and (6,3)). pairs = [(x,y) for x in guests for y in guests if x # brute force search all_arrangements = itertools.combinations(pairs, num_guests / 2) return filter(all_guests_present_once, all_arrangements)
Running the program with num_guests = 18, we get
[((1, 15), (2, 14), (3, 13), (4, 12), (5, 11), (6, 10), (7, 18), (8, 17), (9, 16))]
8 [((1, 8), (2, 7), (3, 6), (4, 5))] 14 [((1, 8), (2, 14), (3, 13), (4, 12), (5, 11), (6, 10), (7, 9))] 16 [((1, 8), (2, 7), (3, 6), (4, 5), (9, 16), (10, 15), (11, 14), (12, 13))] 18 [((1, 15), (2, 14), (3, 13), (4, 12), (5, 11), (6, 10), (7, 18), (8, 17), (9, 16))]
As you can see, 8, 14, and 16 guests can also be paired up in this way. Something to keep in mind the next time you are going to have a party.
Full sourcecode can be found on Github.