syntax highlighting for everyone!

This commit is contained in:
Mark Pilgrim
2009-06-08 12:44:13 -04:00
parent 672132a1d3
commit ae146df0d9
27 changed files with 2621 additions and 1151 deletions
+66 -65
View File
@@ -12,11 +12,11 @@ body{counter-reset:h1 7}
<meta name=viewport content='initial-scale=1.0'>
</head>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input name=q size=25>&nbsp;<input type=submit name=sa value=Search></div></form>
<p>You are here: <a href=index.html>Home</a> <span>&#8227;</span> <a href=table-of-contents.html#advanced-iterators>Dive Into Python 3</a> <span>&#8227;</span>
<p>You are here: <a href=index.html>Home</a> <span class=u>&#8227;</span> <a href=table-of-contents.html#advanced-iterators>Dive Into Python 3</a> <span class=u>&#8227;</span>
<p id=level>Difficulty level: <span title=advanced>&#x2666;&#x2666;&#x2666;&#x2666;&#x2662;</span>
<h1>Advanced Iterators</h1>
<blockquote class=q>
<p><span>&#x275D;</span> Great fleas have little fleas upon their backs to bite &#8217;em,<br>And little fleas have lesser fleas, and so ad infinitum. <span>&#x275E;</span><br>&mdash; Augustus De Morgan
<p><span class=u>&#x275D;</span> Great fleas have little fleas upon their backs to bite &#8217;em,<br>And little fleas have lesser fleas, and so ad infinitum. <span class=u>&#x275E;</span><br>&mdash; Augustus De Morgan
</blockquote>
<p id=toc>&nbsp;
<h2 id=divingin>Diving In</h2>
@@ -44,7 +44,7 @@ E = 4</code></pre>
<p>In this chapter, we&#8217;ll dive into an incredible Python program originally written by Raymond Hettinger. This program solves alphametic puzzles <em>in just 14 lines of code</em>.
<p class=d>[<a href=examples/alphametics.py>download <code>alphametics.py</code></a>]
<pre><code>import re
<pre><code class=pp>import re
import itertools
def solve(puzzle):
@@ -91,9 +91,9 @@ if __name__ == '__main__':
<pre class=screen>
<samp class=p>>>> </samp><kbd>import re</kbd>
<a><samp class=p>>>> </samp><kbd>re.findall('[0-9]+', '16 2-by-4s in rows of 8')</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>re.findall('[0-9]+', '16 2-by-4s in rows of 8')</kbd> <span class=u>&#x2460;</span></a>
<samp>['16', '2', '4', '8']</samp>
<a><samp class=p>>>> </samp><kbd>re.findall('[A-Z]+', 'SEND + MORE == MONEY')</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>re.findall('[A-Z]+', 'SEND + MORE == MONEY')</kbd> <span class=u>&#x2461;</span></a>
<samp>['SEND', 'MORE', 'MONEY']</samp></pre>
<ol>
<li>The <code>re</code> module is Python&#8217;s implementation of <a href=regular-expressions.html>regular expressions</a>. It has a nifty function called <code>findall()</code> which takes a regular expression pattern and a string, and finds all occurrences of the pattern within the string. In this case, the pattern matches sequences of numbers. The <code>findall()</code> function returns a list of all the substrings that matched the pattern.
@@ -108,15 +108,15 @@ if __name__ == '__main__':
<pre class=screen>
<samp class=p>>>> </samp><kbd>a_list = ['a', 'c', 'b', 'a', 'd', 'b']</kbd>
<a><samp class=p>>>> </samp><kbd>{c for c in a_list}</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>{c for c in a_list}</kbd> <span class=u>&#x2460;</span></a>
<samp>{'a', 'c', 'b', 'd'}</samp>
<samp class=p>>>> </samp><kbd>a_string = 'EAST IS EAST'</kbd>
<a><samp class=p>>>> </samp><kbd>{c for c in a_string}</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>{c for c in a_string}</kbd> <span class=u>&#x2461;</span></a>
<samp>{'A', ' ', 'E', 'I', 'S', 'T'}</samp>
<samp class=p>>>> </samp><kbd>words = ['SEND', 'MORE', 'MONEY']</kbd>
<a><samp class=p>>>> </samp><kbd>''.join(words)</kbd> <span>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd>''.join(words)</kbd> <span class=u>&#x2462;</span></a>
<samp>'SENDMOREMONEY'</samp>
<a><samp class=p>>>> </samp><kbd>{c for c in ''.join(words)}</kbd> <span>&#x2463;</span></a>
<a><samp class=p>>>> </samp><kbd>{c for c in ''.join(words)}</kbd> <span class=u>&#x2463;</span></a>
<samp>{'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'}</samp></pre>
<ol>
<li>Given a list of several strings, a set comprehension with the identity function will return a set of unique strings from the list. This makes sense if you think of it like a <code>for</code> loop. Take the first item from the list, put it in the set. Second. Third. Fourth &mdash; wait, that&#8217;s in the set already, so it only gets listed once. Fifth. Sixth &mdash; again, a duplicate, so it only gets listed once. The end result? All the unique items in the original list, without any duplicates. The original list doesn&#8217;t even need to be sorted first.
@@ -127,7 +127,7 @@ if __name__ == '__main__':
<p>The alphametics solver uses this technique to get a list of all the unique characters in the puzzle.
<pre><code>unique_characters = {c for c in ''.join(words)}</code></pre>
<pre><code class=pp>unique_characters = {c for c in ''.join(words)}</code></pre>
<p>This list is later used to assign digits to characters as the solver iterates through the possible solutions.
@@ -138,8 +138,8 @@ if __name__ == '__main__':
<p>Like many programming languages, Python has an <code>assert</code> statement. Here&#8217;s how it works.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd>assert 1 + 1 == 2</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>assert 1 + 1 == 3</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>assert 1 + 1 == 2</kbd> <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>assert 1 + 1 == 3</kbd> <span class=u>&#x2461;</span></a>
<samp class=traceback>Traceback (most recent call last):
File "&lt;stdin>", line 1, in <module>
AssertionError</samp></pre>
@@ -150,11 +150,11 @@ AssertionError</samp></pre>
<p>Therefore, this line of code:
<pre><code>assert len(unique_characters) <= 10</code></pre>
<pre><code class=pp>assert len(unique_characters) <= 10</code></pre>
<p>&hellip;is equivalent to&hellip;
<pre><code>if len(unique_characters) > 10:
<pre><code class=pp>if len(unique_characters) > 10:
raise AssertionError</code></pre>
<p>But a bit easier to read and write.
@@ -169,14 +169,14 @@ AssertionError</samp></pre>
<pre class=screen>
<samp>>>> </samp><kbd>unique_characters = {'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'}</kbd>
<a><samp>>>> </samp><kbd>gen = (ord(c) for c in unique_characters)</kbd> <span>&#x2460;</span></a>
<a><samp>>>> </samp><kbd>gen</kbd> <span>&#x2461;</span></a>
<a><samp>>>> </samp><kbd>gen = (ord(c) for c in unique_characters)</kbd> <span class=u>&#x2460;</span></a>
<a><samp>>>> </samp><kbd>gen</kbd> <span class=u>&#x2461;</span></a>
<samp>&lt;generator object &lt;genexpr> at 0x00BADC10></samp>
<a><samp>>>> </samp><kbd>next(gen)</kbd> <span>&#x2462;</span></a>
<a><samp>>>> </samp><kbd>next(gen)</kbd> <span class=u>&#x2462;</span></a>
<samp>69</samp>
<samp>>>> </samp><kbd>next(gen)</kbd>
<samp>68</samp>
<a><samp>>>> </samp><kbd>tuple(ord(c) for c in unique_characters)</kbd> <span>&#x2463;</span></a>
<a><samp>>>> </samp><kbd>tuple(ord(c) for c in unique_characters)</kbd> <span class=u>&#x2463;</span></a>
<samp>(69, 68, 77, 79, 78, 83, 82, 89)</samp></pre>
<ol>
<li>A generator expression is like an anonymous function that yields values. The expression itself looks like a list comprehension [FIXME have we introduced this yet?], but it&#8217;s wrapped in parentheses instead of square brackets.
@@ -187,7 +187,7 @@ AssertionError</samp></pre>
<p>Here&#8217;s another way to accomplish the same thing, using a <a href=generators.html>generator function</a>:
<pre><code>def ord_map(a_string):
<pre><code class=pp>def ord_map(a_string):
for c in a_string:
yield ord(c)
@@ -202,21 +202,21 @@ gen = ord_map(unique_characters)</code></pre>
<p>The idea is that you take a list of things (could be numbers, could be letters, could be dancing bears) and find all the possible ways to split them up into smaller lists. All the smaller lists have the same size, which can be as small as 1 and as large as the total number of items. Oh, and nothing can be repeated. Mathematicians say things like &#8220;let&#8217;s find the permutations of 3 different items taken 2 at a time,&#8221; which means you have a sequence of 3 items and you want to find all the possible ordered pairs.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd>import itertools</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>perms = itertools.permutations([1, 2, 3], 2)</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>next(perms)</kbd> <span>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd>import itertools</kbd> <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>perms = itertools.permutations([1, 2, 3], 2)</kbd> <span class=u>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>next(perms)</kbd> <span class=u>&#x2462;</span></a>
<samp>(1, 2)</samp>
<samp class=p>>>> </samp><kbd>next(perms)</kbd>
<samp>(1, 3)</samp>
<samp class=p>>>> </samp><kbd>next(perms)</kbd>
<a><samp>(2, 1)</samp> <span>&#x2463;</span></a>
<a><samp>(2, 1)</samp> <span class=u>&#x2463;</span></a>
<samp class=p>>>> </samp><kbd>next(perms)</kbd>
<samp>(2, 3)</samp>
<samp class=p>>>> </samp><kbd>next(perms)</kbd>
<samp>(3, 1)</samp>
<samp class=p>>>> </samp><kbd>next(perms)</kbd>
<samp>(3, 2)</samp>
<a><samp class=p>>>> </samp><kbd>next(perms)</kbd> <span>&#x2464;</span></a>
<a><samp class=p>>>> </samp><kbd>next(perms)</kbd> <span class=u>&#x2464;</span></a>
<samp class=traceback>Traceback (most recent call last):
File "&lt;stdin>", line 1, in <module>
StopIteration</samp></pre>
@@ -232,9 +232,9 @@ StopIteration</samp></pre>
<pre class=screen>
<samp class=p>>>> </samp><kbd>import itertools</kbd>
<a><samp class=p>>>> </samp><kbd>perms = itertools.permutations('ABC', 3)</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>perms = itertools.permutations('ABC', 3)</kbd> <span class=u>&#x2460;</span></a>
<samp class=p>>>> </samp><kbd>next(perms)</kbd>
<a><samp>('A', 'B', 'C')</samp> <span>&#x2461;</span></a>
<a><samp>('A', 'B', 'C')</samp> <span class=u>&#x2461;</span></a>
<samp class=p>>>> </samp><kbd>next(perms)</kbd>
<samp>('A', 'C', 'B')</samp>
<samp class=p>>>> </samp><kbd>next(perms)</kbd>
@@ -249,7 +249,7 @@ StopIteration</samp></pre>
<samp class=traceback>Traceback (most recent call last):
File "&lt;stdin>", line 1, in <module>
StopIteration</samp>
<a><samp class=p>>>> </samp><kbd>list(itertools.permutations('ABC', 3))</kbd> <span>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd>list(itertools.permutations('ABC', 3))</kbd> <span class=u>&#x2462;</span></a>
<samp>[('A', 'B', 'C'), ('A', 'C', 'B'),
('B', 'A', 'C'), ('B', 'C', 'A'),
('C', 'A', 'B'), ('C', 'B', 'A')]</samp></pre>
@@ -264,11 +264,11 @@ StopIteration</samp>
<h2 id=more-itertools>Other Fun Stuff in the <code>itertools</code> Module</h2>
<pre class=screen>
<samp class=p>>>> </samp><kbd>import itertools</kbd>
<a><samp class=p>>>> </samp><kbd>list(itertools.product('ABC', '123'))</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>list(itertools.product('ABC', '123'))</kbd> <span class=u>&#x2460;</span></a>
<samp>[('A', '1'), ('A', '2'), ('A', '3'),
('B', '1'), ('B', '2'), ('B', '3'),
('C', '1'), ('C', '2'), ('C', '3')]</samp>
<a><samp class=p>>>> </samp><kbd>list(itertools.combinations('ABC', 2))</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>list(itertools.combinations('ABC', 2))</kbd> <span class=u>&#x2461;</span></a>
<samp>[('A', 'B'), ('A', 'C'), ('B', 'C')]</samp></pre>
<ol>
<li>The <code>itertools.product()</code> function returns an iterator containing the Cartesian product of two sequences.
@@ -277,19 +277,19 @@ StopIteration</samp>
<p class=d>[<a href=examples/favorite-people.txt>download <code>favorite-people.txt</code></a>]
<pre class=screen>
<a><samp class=p>>>> </samp><kbd>names = list(open('examples/favorite-people.txt'))</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>names = list(open('examples/favorite-people.txt'))</kbd> <span class=u>&#x2460;</span></a>
<samp class=p>>>> </samp><kbd>names</kbd>
<samp>['Dora\n', 'Ethan\n', 'Wesley\n', 'John\n', 'Anne\n',
'Mike\n', 'Chris\n', 'Sarah\n', 'Alex\n', 'Lizzie\n']</samp>
<a><samp class=p>>>> </samp><kbd>names = [name.rstrip() for name in names]</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>names = [name.rstrip() for name in names]</kbd> <span class=u>&#x2461;</span></a>
<samp class=p>>>> </samp><kbd>names</kbd>
<samp>['Dora', 'Ethan', 'Wesley', 'John', 'Anne',
'Mike', 'Chris', 'Sarah', 'Alex', 'Lizzie']</samp>
<a><samp class=p>>>> </samp><kbd>names = sorted(names)</kbd> <span>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd>names = sorted(names)</kbd> <span class=u>&#x2462;</span></a>
<samp class=p>>>> </samp><kbd>names</kbd>
<samp>['Alex', 'Anne', 'Chris', 'Dora', 'Ethan',
'John', 'Lizzie', 'Mike', 'Sarah', 'Wesley']</samp>
<a><samp class=p>>>> </samp><kbd>names = sorted(names, key=len)</kbd> <span>&#x2463;</span></a>
<a><samp class=p>>>> </samp><kbd>names = sorted(names, key=len)</kbd> <span class=u>&#x2463;</span></a>
<samp class=p>>>> </samp><kbd>names</kbd>
<samp>['Alex', 'Anne', 'Dora', 'John', 'Mike',
'Chris', 'Ethan', 'Sarah', 'Lizzie', 'Wesley']</samp></pre>
@@ -305,7 +305,7 @@ StopIteration</samp>
<pre class=screen>
<p>&hellip;continuing from the previous interactive shell&hellip;
<samp class=p>>>> </samp><kbd>import itertools</kbd>
<a><samp class=p>>>> </samp><kbd>groups = itertools.groupby(names, len)</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>groups = itertools.groupby(names, len)</kbd> <span class=u>&#x2460;</span></a>
<samp class=p>>>> </samp><kbd>groups</kbd>
<samp>&lt;itertools.groupby object at 0x00BB20C0></samp>
<samp class=p>>>> </samp><kbd>list(groups)</kbd>
@@ -313,7 +313,7 @@ StopIteration</samp>
(5, &lt;itertools._grouper object at 0x00BB4050>),
(6, &lt;itertools._grouper object at 0x00BB4030>)]</samp>
<samp class=p>>>> </samp><kbd>groups = itertools.groupby(names, len)</kbd>
<a><samp class=p>>>> </samp><kbd>for name_length, name_iter in groups:</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>for name_length, name_iter in groups:</kbd> <span class=u>&#x2461;</span></a>
<samp class=p>... </samp><kbd> print('Names with {0:d} letters:'.format(name_length))</kbd>
<samp class=p>... </samp><kbd> for name in name_iter:</kbd>
<samp class=p>... </samp><kbd> print(name)</kbd>
@@ -342,13 +342,13 @@ Wesley</samp></pre>
<samp>[0, 1, 2]</samp>
<samp class=p>>>> </samp><kbd>list(range(10, 13))</kbd>
<samp>[10, 11, 12]</samp>
<a><samp class=p>>>> </samp><kbd>list(itertools.chain(range(0, 3), range(10, 13)))</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>list(itertools.chain(range(0, 3), range(10, 13)))</kbd> <span class=u>&#x2460;</span></a>
<samp>[0, 1, 2, 10, 11, 12]</samp>
<a><samp class=p>>>> </samp><kbd>list(zip(range(0, 3), range(10, 13)))</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>list(zip(range(0, 3), range(10, 13)))</kbd> <span class=u>&#x2461;</span></a>
<samp>[(0, 10), (1, 11), (2, 12)]</samp>
<a><samp class=p>>>> </samp><kbd>list(zip(range(0, 3), range(10, 14)))</kbd> <span>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd>list(zip(range(0, 3), range(10, 14)))</kbd> <span class=u>&#x2462;</span></a>
<samp>[(0, 10), (1, 11), (2, 12)]</samp>
<a><samp class=p>>>> </samp><kbd>list(itertools.zip_longest(range(0, 3), range(10, 14)))</kbd> <span>&#x2463;</span></a>
<a><samp class=p>>>> </samp><kbd>list(itertools.zip_longest(range(0, 3), range(10, 14)))</kbd> <span class=u>&#x2463;</span></a>
<samp>[(0, 10), (1, 11), (2, 12), (None, 13)]</samp></pre>
<ol>
<li>The <code>itertools.chain()</code> function takes two iterators and returns an iterator that contains all the items from the first iterator, followed by all the items from the second iterator. (Actually, it can take any number of iterators, and it chains them all in the order they were passed to the function.)
@@ -362,10 +362,10 @@ Wesley</samp></pre>
<pre class=screen>
<samp class=p>>>> </samp><kbd>characters = ('S', 'M', 'E', 'D', 'O', 'N', 'R', 'Y')</kbd>
<samp class=p>>>> </samp><kbd>guess = ('1', '2', '0', '3', '4', '5', '6', '7')</kbd>
<a><samp class=p>>>> </samp><kbd>tuple(zip(characters, guess))</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>tuple(zip(characters, guess))</kbd> <span class=u>&#x2460;</span></a>
<samp>(('S', '1'), ('M', '2'), ('E', '0'), ('D', '3'),
('O', '4'), ('N', '5'), ('R', '6'), ('Y', '7'))</samp>
<a><samp class=p>>>> </samp><kbd>dict(zip(characters, guess))</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>dict(zip(characters, guess))</kbd> <span class=u>&#x2461;</span></a>
<samp>{'E': '0', 'D': '3', 'M': '2', 'O': '4',
'N': '5', 'S': '1', 'R': '6', 'Y': '7'}</samp></pre>
<ol>
@@ -375,7 +375,7 @@ Wesley</samp></pre>
<p id=guess>The alphametics solver uses this technique to create a dictionary that maps letters in the puzzle to digits in the solution, for each possible solution.
<pre><code>characters = tuple(ord(c) for c in sorted_characters)
<pre><code class=pp>characters = tuple(ord(c) for c in sorted_characters)
digits = tuple(ord(c) for c in '0123456789')
...
for guess in itertools.permutations(digits, len(characters)):
@@ -391,10 +391,10 @@ for guess in itertools.permutations(digits, len(characters)):
<p>Python strings have many methods. You learned about some of those methods in <a href=strings.html>the Strings chapter</a>: <code>lower()</code>, <code>count()</code>, and <code>format()</code>. Now I want to introduce you to a powerful but little-known string manipulation technique: the <code>translate()</code> method.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd>translation_table = {ord('A'): ord('O')}</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>translation_table</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>translation_table = {ord('A'): ord('O')}</kbd> <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>translation_table</kbd> <span class=u>&#x2461;</span></a>
<samp>{65: 79}</samp>
<a><samp class=p>>>> </samp><kbd>'MARK'.translate(translation_table)</kbd> <span>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd>'MARK'.translate(translation_table)</kbd> <span class=u>&#x2462;</span></a>
<samp>'MORK'</samp></pre>
<ol>
<li>String translation starts with a translation table, which is just a dictionary that maps one character to another. Actually, &#8220;character&#8221; is incorrect &mdash; the translation table really maps one <em>byte</em> to another.
@@ -405,16 +405,16 @@ for guess in itertools.permutations(digits, len(characters)):
<p>What does this have to do with solving alphametic puzzles? As it turns out, everything.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd>characters = tuple(ord(c) for c in 'SMEDONRY')</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>characters = tuple(ord(c) for c in 'SMEDONRY')</kbd> <span class=u>&#x2460;</span></a>
<samp class=p>>>> </samp><kbd>characters</kbd>
<samp>(83, 77, 69, 68, 79, 78, 82, 89)</samp>
<a><samp class=p>>>> </samp><kbd>guess = tuple(ord(c) for c in '91570682')</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>guess = tuple(ord(c) for c in '91570682')</kbd> <span class=u>&#x2461;</span></a>
<samp class=p>>>> </samp><kbd>guess</kbd>
<samp>(57, 49, 53, 55, 48, 54, 56, 50)</samp>
<a><samp class=p>>>> </samp><kbd>translation_table = dict(zip(characters, guess))</kbd> <span>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd>translation_table = dict(zip(characters, guess))</kbd> <span class=u>&#x2462;</span></a>
<samp class=p>>>> </samp><kbd>translation_table</kbd>
<samp>{68: 55, 69: 53, 77: 49, 78: 54, 79: 48, 82: 56, 83: 57, 89: 50}</samp>
<a><samp class=p>>>> </samp><kbd>'SEND + MORE == MONEY'.translate(translation_table)</kbd> <span>&#x2463;</span></a>
<a><samp class=p>>>> </samp><kbd>'SEND + MORE == MONEY'.translate(translation_table)</kbd> <span class=u>&#x2463;</span></a>
<samp>'9567 + 1085 == 10652'</samp></pre>
<ol>
<li>Using a <a href=#generator-expressions>generator expression</a>, we quickly compute the byte values for each character in a string. <var>characters</var> is an example of the value of <var>sorted_characters</var> in the <code>alphametics.solve()</code> function.
@@ -455,12 +455,12 @@ for guess in itertools.permutations(digits, len(characters)):
<pre class=screen>
<samp class=p>>>> </samp><kbd>x = 5</kbd>
<a><samp class=p>>>> </samp><kbd>eval("x * 5")</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("x * 5")</kbd> <span class=u>&#x2460;</span></a>
<samp>25</samp>
<a><samp class=p>>>> </samp><kbd>eval("pow(x, 2)")</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("pow(x, 2)")</kbd> <span class=u>&#x2461;</span></a>
<samp>25</samp>
<samp class=p>>>> </samp><kbd>import math</kbd>
<a><samp class=p>>>> </samp><kbd>eval("math.sqrt(x)")</kbd> <span>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("math.sqrt(x)")</kbd> <span class=u>&#x2462;</span></a>
<samp>2.2360679774997898</samp></pre>
<ol>
<li>The expression that <code>eval()</code> takes can reference global variables defined outside the <code>eval()</code>. If called within a function, it can reference local variables too.
@@ -472,11 +472,11 @@ for guess in itertools.permutations(digits, len(characters)):
<pre class=screen>
<samp class=p>>>> </samp><kbd>import subprocess</kbd>
<a><samp class=p>>>> </samp><kbd>eval("subprocess.getoutput('ls ~')")</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("subprocess.getoutput('ls ~')")</kbd> <span class=u>&#x2460;</span></a>
<samp>'Desktop Library Pictures \
Documents Movies Public \
Music Sites'</samp>
<a><samp class=p>>>> </samp><kbd>eval("subprocess.getoutput('rm -rf /')")</kbd> <span>&#x2461;</span></a></pre>
<a><samp class=p>>>> </samp><kbd>eval("subprocess.getoutput('rm -rf /')")</kbd> <span class=u>&#x2461;</span></a></pre>
<ol>
<li>The <code>subprocess</code> module allows you to run arbitrary shell commands and get the result as a Python string.
<li>Don&#8217;t do this.
@@ -485,7 +485,7 @@ for guess in itertools.permutations(digits, len(characters)):
<p>It&#8217;s even worse than that, because there&#8217;s a global <code>__import__()</code> function that takes a module name as a string, imports the module, and returns a reference to it. Combined with the power of <code>eval()</code>, you can construct a single expression that will wipe out all your files:
<pre class=screen>
<a><samp class=p>>>> </samp><kbd>eval("__import__('subprocess').getoutput('rm -rf /')")</kbd> <span>&#x2460;</span></a></pre>
<a><samp class=p>>>> </samp><kbd>eval("__import__('subprocess').getoutput('rm -rf /')")</kbd> <span class=u>&#x2460;</span></a></pre>
<ol>
<li>Don&#8217;t do this either.
</ol>
@@ -498,14 +498,14 @@ for guess in itertools.permutations(digits, len(characters)):
<pre class=screen>
<samp class=p>>>> </samp><kbd>x = 5</kbd>
<a><samp class=p>>>> </samp><kbd>eval("x * 5", {}, {})</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("x * 5", {}, {})</kbd> <span class=u>&#x2460;</span></a>
<samp class=traceback>Traceback (most recent call last):
File "&lt;stdin>", line 1, in &lt;module>
File "&lt;string>", line 1, in &lt;module>
NameError: name 'x' is not defined</samp>
<a><samp class=p>>>> </samp><kbd>eval("x * 5", {"x": x}, {})</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("x * 5", {"x": x}, {})</kbd> <span class=u>&#x2461;</span></a>
<samp class=p>>>> </samp><kbd>import math</kbd>
<a><samp class=p>>>> </samp><kbd>eval("math.sqrt(x)", {"x": x}, {})</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("math.sqrt(x)", {"x": x}, {})</kbd> <span class=u>&#x2461;</span></a>
<samp class=traceback>Traceback (most recent call last):
File "&lt;stdin>", line 1, in &lt;module>
File "&lt;string>", line 1, in &lt;module>
@@ -519,9 +519,9 @@ NameError: name 'math' is not defined</samp></pre>
<p>Gee, that was easy. Lemme make an alphametics web service now!
<pre class=screen>
<a><samp class=p>>>> </samp><kbd>eval("pow(5, 2)", {}, {})</kbd> <span>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("pow(5, 2)", {}, {})</kbd> <span class=u>&#x2460;</span></a>
<samp>25</samp>
<a><samp class=p>>>> </samp><kbd>eval("__import__('math').sqrt(5)", {}, {})</kbd> <span>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd>eval("__import__('math').sqrt(5)", {}, {})</kbd> <span class=u>&#x2461;</span></a>
<samp>2.2360679774997898</samp></pre>
<ol>
<li>Even though you&#8217;ve passed empty dictionaries for the global and local namespaces, all of Python&#8217;s built-in functions are still available during evaluation. So <code>pow(5, 2)</code> works, because <code>5</code> and <code>2</code> are literals, and <code>pow()</code> is a built-in function.
@@ -531,7 +531,7 @@ NameError: name 'math' is not defined</samp></pre>
<p>Yeah, that means you can still do nasty things, even if you explicitly set the global and local namespaces to empty dictionaries when calling <code>eval()</code>:
<pre class=screen>
<a><samp class=p>>>> </samp><kbd>eval("__import__('subprocess').getoutput('rm -rf /')", {}, {})</kbd> <span>&#x2460;</span></a></pre>
<a><samp class=p>>>> </samp><kbd>eval("__import__('subprocess').getoutput('rm -rf /')", {}, {})</kbd> <span class=u>&#x2460;</span></a></pre>
<ol>
<li>Please don&#8217;t do this.
</ol>
@@ -540,13 +540,13 @@ NameError: name 'math' is not defined</samp></pre>
<pre class=screen>
<samp class=p>>>> </samp><kbd>eval("__import__('math').sqrt(5)",</kbd>
<a><samp class=p>... </samp><kbd> {"__builtins__":None}, {})</kbd> <span>&#x2460;</span></a>
<a><samp class=p>... </samp><kbd> {"__builtins__":None}, {})</kbd> <span class=u>&#x2460;</span></a>
<samp class=traceback>Traceback (most recent call last):
File "&lt;stdin>", line 1, in &lt;module>
File "&lt;string>", line 1, in &lt;module>
NameError: name '__import__' is not defined</samp>
<samp class=p>>>> </samp><kbd>eval("__import__('subprocess').getoutput('rm -rf /')",</kbd>
<a><samp class=p>... </samp><kbd> {"__builtins__":None}, {})</kbd> <span>&#x2461;</span></a>
<a><samp class=p>... </samp><kbd> {"__builtins__":None}, {})</kbd> <span class=u>&#x2461;</span></a>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
@@ -591,9 +591,10 @@ NameError: name '__import__' is not defined</samp></pre>
<p>Many, many thanks to Raymond Hettinger for agreeing to relicense his code so I could port it to Python 3 and use it as the basis for this chapter.
<p class=v><a href=iterators.html rel=prev title='back to &#8220;Iterators&#8221;'><span>&#x261C;</span></a> <a href=unit-testing.html rel=next title='onward to &#8220;Unit Testing&#8221;'><span>&#x261E;</span></a>
<p class=v><a href=iterators.html rel=prev title='back to &#8220;Iterators&#8221;'><span class=u>&#x261C;</span></a> <a href=unit-testing.html rel=next title='onward to &#8220;Unit Testing&#8221;'><span class=u>&#x261E;</span></a>
<p class=v><a rel=prev class=todo><span>&#x261C;</span></a> <a rel=next class=todo><span>&#x261E;</span></a>
<p class=v><a rel=prev class=todo><span class=u>&#x261C;</span></a> <a rel=next class=todo><span class=u>&#x261E;</span></a>
<p class=c>&copy; 2001&ndash;9 <a href=about.html>Mark Pilgrim</a>
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>