mirror of
https://github.com/kennethreitz/dive-into-python3.git
synced 2026-06-05 23:10:17 +00:00
more in comprehensions chapter
This commit is contained in:
+70
-58
@@ -16,13 +16,13 @@ body{counter-reset:h1 3}
|
||||
<p id=level>Difficulty level: <span class=u title=beginner>♦♦♢♢♢</span>
|
||||
<h1>Comprehensions</h1>
|
||||
<blockquote class=q>
|
||||
<p><span class=u>❝</span> FIXME <span class=u>❞</span><br>— FIXME
|
||||
<p><span class=u>❝</span> Our imagination is stretched to the utmost, not, as in fiction, to imagine things which are not really there, but just to comprehend those things which are. <span class=u>❞</span><br>— <a href=http://en.wikiquote.org/wiki/Richard_Feynman>Richard Feynman</a>
|
||||
</blockquote>
|
||||
<p id=toc>
|
||||
<h2 id=divingin>Diving In</h2>
|
||||
<p class=f>This chapter will teach you about list comprehensions, dictionary comprehensions, and set comprehensions: three related concepts centered around one very powerful technique. But first, I want to take a little detour into two modules that will help you navigate your local file system.
|
||||
|
||||
<h2 id=os>The <code>os</code> module</h2>
|
||||
<h2 id=os>Working With Files And Directories</h2>
|
||||
|
||||
<p>Python 3 comes with a module called <code>os</code>, which stands for “operating system.” The <a href=http://docs.python.org/3.1/library/os.html><code>os</code> module</a> contains a plethora of functions to get information on — and in some cases, to manipulate — local directories, files, processes, and environment variables. Python does its best to offer a unified <abbr>API</abbr> across <a href=installing-python.html>all supported operating systems</a> so your programs can run on any computer with as little platform-specific code as possible.
|
||||
|
||||
@@ -61,7 +61,7 @@ body{counter-reset:h1 3}
|
||||
<li>When I called the <code>os.chdir()</code> function, I used a Linux-style pathname (forward slashes, no drive letter) even though I’m on Windows. This is one of the places where Python tries to paper over the differences between operating systems.
|
||||
</ol>
|
||||
|
||||
<h3 id=ospath>The <code>os.path</code> module</h3>
|
||||
<h3 id=ospath>Working With Filenames and Directory Names</h3>
|
||||
|
||||
<p>While we’re on the subject of directories, I want to point out the <code>os.path</code> submodule. <code>os.path</code> contains functions for manipulating filenames and directory names.
|
||||
|
||||
@@ -106,7 +106,7 @@ body{counter-reset:h1 3}
|
||||
<li><code>os.path</code> also contains the <code>os.path.splitext()</code> function, which splits a filename and returns a tuple containing the filename and the file extension. You use the same technique to assign each of them to separate variables.
|
||||
</ol>
|
||||
|
||||
<h2 id=glob>The <code>glob</code> module</h2>
|
||||
<h3 id=glob>Listing Directories</h3>
|
||||
|
||||
<p>The <code>glob</code> module is another tool in the Python standard library. It’s an easy way to get the contents of a directory programmatically, and it uses the sort of wildcards that you may already be familiar with from working on the command line.
|
||||
|
||||
@@ -142,70 +142,82 @@ body{counter-reset:h1 3}
|
||||
<li>You can include multiple wildcards in your glob pattern. This example finds all the files in the current working directory that end in a <code>.py</code> extension and contain the word <code>test</code> anywhere in their filename.
|
||||
</ol>
|
||||
|
||||
<p>Now you’re ready to learn about comprehensions.
|
||||
<h3 id=osstat>Getting File Metadata</h3>
|
||||
|
||||
<p>Every modern file system stores metadata about each file: creation date, last-modified date, file size, and so on. Python provides a single <abbr>API</abbr> to access this metadata. You don’t need to open the file; all you need is the filename.
|
||||
|
||||
<pre class=screen>
|
||||
<samp class=p>>>> </samp><kbd class=pp>import os</kbd>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>print(os.getcwd())</kbd> <span class=u>①</span></a>
|
||||
<samp class=pp>c:\Users\pilgrim\diveintopython3\examples</samp>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>metadata = os.stat('feed.xml')</kbd> <span class=u>②</span></a>
|
||||
<samp class=p>>>> </samp><kbd class=pp>metadata.st_mtime</kbd>
|
||||
<samp class=pp>1247520344.9537716</samp>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>import time</kbd> <span class=u>③</span></a>
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>time.localtime(metadata.st_mtime)</kbd> <span class=u>④</span></a>
|
||||
<samp class=pp>time.struct_time(tm_year=2009, tm_mon=7, tm_mday=13, tm_hour=17,
|
||||
tm_min=25, tm_sec=44, tm_wday=0, tm_yday=194, tm_isdst=1)</samp>
|
||||
</pre>
|
||||
<ol>
|
||||
<li>FIXME
|
||||
<li>FIXME
|
||||
<li>FIXME
|
||||
<li>FIXME
|
||||
</ol>
|
||||
|
||||
<pre class=screen>
|
||||
# continued from the previous example
|
||||
<a><samp class=p>>>> </samp><kbd class=pp>metadata.st_size</kbd> <span class=u>①</span></a>
|
||||
<samp class=pp>3070</samp>
|
||||
<samp class=p>>>> </samp><kbd class=pp>import humansize</kbd>
|
||||
<a.<samp class=p>>>> </samp><kbd class=pp>humansize.approximate_size(metadata.st_size)</kbd> <span class=u>②</span></a>
|
||||
<samp class=pp>'3.0 KiB'</samp></pre>
|
||||
<ol>
|
||||
<li>FIXME
|
||||
<li>FIXME
|
||||
</ol>
|
||||
|
||||
<h3 id=abspath>Constructing Absolute Pathnames</h3>
|
||||
|
||||
<p>In the previous example, the <code>glob.glob()</code> function returned a list of relative pathnames. The first example had pathnames like <code>'examples\feed.xml'</code>, and the second example had even shorter relative pathnames like <code>'romantest1.py'</code>. As long as you stay in the same current working directory, these relative pathnames will work for opening files or getting file metadata. But if you want to construct an absolute pathname — <i>i.e.</i> one that includes all the directory names back to the root directory or drive letter — then you’ll need the <code>os.path.abspath()</code> function.
|
||||
|
||||
<pre class=screen>
|
||||
<samp class=p>>>> </samp><kbd class=pp>import os</kbd>
|
||||
<samp class=p>>>> </samp><kbd class=pp>print(os.getcwd())</kbd>
|
||||
<samp class=pp>c:\Users\pilgrim\diveintopython3\examples</samp>
|
||||
<samp class=p>>>> </samp><kbd class=pp>print(os.path.abspath('feed.xml'))</kbd>
|
||||
<samp class=pp>c:\Users\pilgrim\diveintopython3\examples\feed.xml</samp></pre>
|
||||
|
||||
<h2 id=list-comprehensions>List Comprehensions</h2>
|
||||
|
||||
<p>One of the most powerful features of Python is the list comprehension, which provides a compact way of mapping a list into another list by applying a function to each
|
||||
of the elements of the list.
|
||||
|
||||
<pre class=screen><samp class=p>>>> </samp><kbd>li = [1, 9, 8, 4]</kbd>
|
||||
<samp class=p>>>> </samp><kbd>[elem * 2 for elem in li]</kbd> <span>①</span>
|
||||
[2, 18, 16, 8]
|
||||
<samp class=p>>>> </samp><kbd>li</kbd> <span>②</span>
|
||||
[1, 9, 8, 4]
|
||||
<samp class=p>>>> </samp><kbd>li = [elem * 2 for elem in li]</kbd> <span>③</span>
|
||||
<samp class=p>>>> </samp><kbd>li</kbd>
|
||||
[2, 18, 16, 8]</pre>
|
||||
<ol>
|
||||
<li>To make sense of this, look at it from right to left. <var>li</var> is the list you're mapping. Python loops through <var>li</var> one element at a time, temporarily assigning the value of each element to the variable <var>elem</var>. Python then applies the function <code><var>elem</var>*2</code> and appends that result to the returned list.
|
||||
<li>Note that list comprehensions do not change the original list.
|
||||
<li>It is safe to assign the result of a list comprehension to the variable that you're mapping. Python constructs the new list in memory, and when the list comprehension is complete, it assigns the result to the variable.
|
||||
</ol>
|
||||
|
||||
<p>FIXME Here are the list comprehensions in the <code>buildConnectionString</code> function that you declared in <a href="#odbchelper">Chapter 2</a>:
|
||||
|
||||
<pre><code>["%s=%s" % (k, v) for k, v in params.items()]</code></pre>
|
||||
|
||||
<p>First, notice that you're calling the <code>items</code> function of the <var>params</var> dictionary. This function returns a list of tuples of all the data in the dictionary.
|
||||
<p>A <dfn>list comprehension</dfn> provides a compact way of mapping a list into another list by applying a function to each of the elements of the list.
|
||||
|
||||
<pre class=screen>
|
||||
<samp class=p>>>> </samp><kbd>params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}</kbd>
|
||||
<samp class=p>>>> </samp><kbd>params.keys()</kbd> <span>①</span>
|
||||
['server', 'uid', 'database', 'pwd']
|
||||
<samp class=p>>>> </samp><kbd>params.values()</kbd> <span>②</span>
|
||||
['mpilgrim', 'sa', 'master', 'secret']
|
||||
<samp class=p>>>> </samp><kbd>params.items()</kbd> <span>③</span>
|
||||
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]</pre>
|
||||
<samp class=p>>>> </samp><kbd>a_list = [1, 9, 8, 4]</kbd>
|
||||
<a><samp class=p>>>> </samp><kbd>[elem * 2 for elem in a_list]</kbd> <span>①</span></a>
|
||||
<samp class=pp>[2, 18, 16, 8]</samp>
|
||||
<a><samp class=p>>>> </samp><kbd>a_list</kbd> <span>②</span></a>
|
||||
<samp class=pp>[1, 9, 8, 4]</samp>
|
||||
<a><samp class=p>>>> </samp><kbd>a_list = [elem * 2 for elem in a_list]</kbd> <span>③</span></a>
|
||||
<samp class=p>>>> </samp><kbd>a_list</kbd>
|
||||
<samp class=pp>[2, 18, 16, 8]</samp></pre>
|
||||
<ol>
|
||||
<li>The <code>keys</code> method of a dictionary returns a list of all the keys. The list is not in the order in which the dictionary was defined
|
||||
(remember that elements in a dictionary are unordered), but it is a list.
|
||||
<li>The <code>values</code> method returns a list of all the values. The list is in the same order as the list returned by <code>keys</code>, so <code>params.values()[n] == params[params.keys()[n]]</code> for all values of <var>n</var>.
|
||||
<li>The <code>items</code> method returns a list of tuples of the form <code>(<var>key</var>, <var>value</var>)</code>. The list contains all the data in the dictionary.
|
||||
</ol>
|
||||
|
||||
<p>Now let's see what <code>buildConnectionString</code> does. It takes a list, <code><var>params</var>.<code>items</code>()</code>, and maps it to a new list by applying string formatting to each element. The new list will have the same number of elements
|
||||
as <code><var>params</var>.<code>items</code>()</code>, but each element in the new list will be a string that contains both a key and its associated value from the <var>params</var> dictionary.
|
||||
|
||||
<pre class=screen>
|
||||
<samp class=p>>>> </samp><kbd>params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}</kbd>
|
||||
<samp class=p>>>> </samp><kbd>params.items()</kbd>
|
||||
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
|
||||
<samp class=p>>>> </samp><kbd>[k for k, v in params.items()]</kbd> <span>①</span>
|
||||
['server', 'uid', 'database', 'pwd']
|
||||
<samp class=p>>>> </samp><kbd>[v for k, v in params.items()]</kbd> <span>②</span>
|
||||
['mpilgrim', 'sa', 'master', 'secret']
|
||||
<samp class=p>>>> </samp><kbd>["%s=%s" % (k, v) for k, v in params.items()]</kbd> <span>③</span>
|
||||
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']</pre>
|
||||
<ol>
|
||||
<li>Note that you're using two variables to iterate through the <code>params.items()</code> list. This is another use of <a href="#odbchelper.multiassign" title="3.4.2. Assigning Multiple Values at Once">multi-variable assignment</a>. The first element of <code>params.items()</code> is <code>('server', 'mpilgrim')</code>, so in the first iteration of the list comprehension, <var>k</var> will get <code>'server'</code> and <var>v</var> will get <code>'mpilgrim'</code>. In this case, you're ignoring the value of <var>v</var> and only including the value of <var>k</var> in the returned list, so this list comprehension ends up being equivalent to <code><var>params</var>.<code>keys</code>()</code>.
|
||||
<li>Here you're doing the same thing, but ignoring the value of <var>k</var>, so this list comprehension ends up being equivalent to <code><var>params</var>.<code>values</code>()</code>.
|
||||
<li>Combining the previous two examples with some simple <a href="#odbchelper.stringformatting" title="3.5. Formatting Strings">string formatting</a>, you get a list of strings that include both the key and value of each element of the dictionary. This looks suspiciously
|
||||
like the <a href="#odbchelper.output">output</a> of the program. All that remains is to join the elements in this list into a single string.
|
||||
<li>To make sense of this, look at it from right to left. <var>a_list</var> is the list you’re mapping. The Python interpreter loops through <var>a_list</var> one element at a time, temporarily assigning the value of each element to the variable <var>elem</var>. Python then applies the function <code><var>elem</var> * 2</code> and appends that result to the returned list.
|
||||
<li>A list comprehension creates a new list; it does not change the original list.
|
||||
<li>It is safe to assign the result of a list comprehension to the variable that you’re mapping. Python constructs the new list in memory, and when the list comprehension is complete, it assigns the result to the original variable.
|
||||
</ol>
|
||||
|
||||
<p>FIXME
|
||||
|
||||
<pre class=screen>
|
||||
<samp class=p>>>> </samp><kbd class=pp>glob.glob('*.xml')</kbd>
|
||||
<samp class=pp>['feed-broken.xml', 'feed-ns0.xml', 'feed.xml']</samp>
|
||||
<samp class=p>>>> </samp><kbd class=pp>[os.path.abspath(filename) for filename in glob.glob('*.xml')]</kbd>
|
||||
<samp class=pp>['c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-broken.xml',
|
||||
'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-ns0.xml',
|
||||
'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml']</samp>
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
>>> print("\n".join(["{0:>8} {1}".format(humansize.approximate_size(os.stat(f).st_size, False), os.path.abspath(f)) for f in glob.glob('*.py')]))
|
||||
2.5 KB c:\Users\pilgrim\diveintopython3\examples\alphametics.py
|
||||
|
||||
Reference in New Issue
Block a user