From a5cae9cdc2b7e643eec40e40bd567b6cab44b8e7 Mon Sep 17 00:00:00 2001 From: David Beazley Date: Thu, 28 May 2020 08:49:50 -0500 Subject: [PATCH] Editing. Added images --- Notes/02_Working_with_data/01_Datatypes.md | 132 +++++++++-------- Notes/02_Working_with_data/02_Containers.md | 56 +++++--- Notes/02_Working_with_data/03_Formatting.md | 118 +++++++++------- Notes/02_Working_with_data/04_Sequences.md | 96 +++++++------ Notes/02_Working_with_data/05_Collections.md | 17 ++- .../06_List_comprehension.md | 81 ++++++----- Notes/02_Working_with_data/07_Objects.md | 133 ++++++++++++------ Notes/02_Working_with_data/references.png | Bin 0 -> 40244 bytes Notes/02_Working_with_data/shallow.png | Bin 0 -> 36369 bytes 9 files changed, 377 insertions(+), 256 deletions(-) create mode 100644 Notes/02_Working_with_data/references.png create mode 100644 Notes/02_Working_with_data/shallow.png diff --git a/Notes/02_Working_with_data/01_Datatypes.md b/Notes/02_Working_with_data/01_Datatypes.md index 93f5fca..c07bfd3 100644 --- a/Notes/02_Working_with_data/01_Datatypes.md +++ b/Notes/02_Working_with_data/01_Datatypes.md @@ -1,5 +1,9 @@ +[Contents](../Contents) \| [Previous (1.6 Files)](../01_Introduction/06_Files) \| [Next (2.2 Containers)](02_Containers) + # 2.1 Datatypes and Data structures +This section introduces data structures in the form of tuples and dictionaries. + ### Primitive Datatypes Python has a few primitive types of data: @@ -16,7 +20,8 @@ We learned about these in the introduction. email_address = None ``` -This type is often used as a placeholder for optional or missing value. +`None` is often used as a placeholder for optional or missing value. It +evaluates as `False` in conditionals. ```python if email_address: @@ -25,8 +30,7 @@ if email_address: ### Data Structures -Real programs have more complex data than the ones that can be easily represented by the datatypes learned so far. -For example information about a stock: +Real programs have more complex data. For example information about a stock holding: ```code 100 shares of GOOG at $490.10 @@ -61,7 +65,7 @@ t = () # An empty tuple w = ('GOOG', ) # A 1-item tuple ``` -Tuples are usually used to represent *simple* records or structures. +Tuples are often used to represent *simple* records or structures. Typically, it is a single *object* of multiple parts. A good analogy: *A tuple is like a single row in a database table.* Tuple contents are ordered (like an array). @@ -73,9 +77,9 @@ shares = s[1] # 100 price = s[2] # 490.1 ``` -However, th contents can't be modified. +However, the contents can't be modified. -```pycon +```python >>> s[1] = 75 TypeError: object does not support item assignment ``` @@ -88,7 +92,7 @@ s = (s[0], 75, s[2]) ### Tuple Packing -Tuples are focused more on packing related items together into a single *entity*. +Tuples are more about packing related items together into a single *entity*. ```python s = ('GOOG', 100, 490.1) @@ -105,7 +109,7 @@ name, shares, price = s print('Cost', shares * price) ``` -The number of variables must match the tuple structure. +The number of variables on the left must match the tuple structure. ```python name, shares = s # ERROR @@ -116,19 +120,20 @@ ValueError: too many values to unpack ### Tuples vs. Lists -Tuples are NOT just read-only lists. Tuples are most ofter used for a *single item* consisting of multiple parts. -Lists are usually a collection of distinct items, usually all of the same type. +Tuples look like read-only lists. However, tuples are most often used +for a *single item* consisting of multiple parts. Lists are usually a +collection of distinct items, usually all of the same type. ```python -record = ('GOOG', 100, 490.1) # A tuple representing a stock in a portfolio +record = ('GOOG', 100, 490.1) # A tuple representing a record in a portfolio symbols = [ 'GOOG', 'AAPL', 'IBM' ] # A List representing three stock symbols ``` ### Dictionaries -A dictionary is a hash table or associative array. -It is a collection of values indexed by *keys*. These keys serve as field names. +A dictionary is mapping of keys to values. It's also sometimes called a hash table or +associative array. The keys serve as indices for accessing values. ```python s = { @@ -140,9 +145,9 @@ s = { ### Common operations -To read values from a dictionary use the key names. +To get values from a dictionary use the key names. -```pycon +```python >>> print(s['name'], s['shares']) GOOG 100 >>> s['price'] @@ -152,7 +157,7 @@ GOOG 100 To add or modify values assign using the key names. -```pycon +```python >>> s['shares'] = 75 >>> s['date'] = '6/6/2007' >>> @@ -160,7 +165,7 @@ To add or modify values assign using the key names. To delete a value use the `del` statement. -```pycon +```python >>> del s['date'] >>> ``` @@ -178,11 +183,11 @@ s[2] ## Exercises -### Note +In the last few exercises, you wrote a program that read a datafile +`Data/portfolio.csv`. Using the `csv` module, it is easy to read the +file row-by-row. -In the last few exercises, you wrote a program that read a datafile `Data/portfolio.csv`. Using the `csv` module, it is easy to read the file row-by-row. - -```pycon +```python >>> import csv >>> f = open('Data/portfolio.csv') >>> rows = csv.reader(f) @@ -194,11 +199,13 @@ In the last few exercises, you wrote a program that read a datafile `Data/portfo >>> ``` -Although reading the file is easy, you often want to do more with the data than read it. -For instance, perhaps you want to store it and start performing some calculations on it. -Unfortunately, a raw "row" of data doesn’t give you enough to work with. For example, even a simple math calculation doesn’t work: +Although reading the file is easy, you often want to do more with the +data than read it. For instance, perhaps you want to store it and +start performing some calculations on it. Unfortunately, a raw "row" +of data doesn’t give you enough to work with. For example, even a +simple math calculation doesn’t work: -```pycon +```python >>> row = ['AA', '100', '32.20'] >>> cost = row[1] * row[2] Traceback (most recent call last): @@ -207,8 +214,9 @@ TypeError: can't multiply sequence by non-int of type 'str' >>> ``` -To do more, you typically want to interpret the raw data in some way and turn it into a more useful kind of object so that you can work with it later. -Two simple options are tuples or dictionaries. +To do more, you typically want to interpret the raw data in some way +and turn it into a more useful kind of object so that you can work +with it later. Two simple options are tuples or dictionaries. ### Exercise 2.1: Tuples @@ -216,16 +224,17 @@ At the interactive prompt, create the following tuple that represents the above row, but with the numeric columns converted to proper numbers: -```pycon +```python >>> t = (row[0], int(row[1]), float(row[2])) >>> t ('AA', 100, 32.2) >>> ``` -Using this, you can now calculate the total cost by multiplying the shares and the price: +Using this, you can now calculate the total cost by multiplying the +shares and the price: -```pycon +```python >>> cost = t[1] * t[2] >>> cost 3220.0000000000005 @@ -244,15 +253,16 @@ surprising if you haven’t seen it before. This happens in all programming languages that use floating point decimals, but it often gets hidden when printing. For example: -```pycon +```python >>> print(f'{cost:0.2f}') 3220.00 >>> ``` -Tuples are read-only. Verify this by trying to change the number of shares to 75. +Tuples are read-only. Verify this by trying to change the number of +shares to 75. -```pycon +```python >>> t[1] = 75 Traceback (most recent call last): File "", line 1, in @@ -260,9 +270,10 @@ TypeError: 'tuple' object does not support item assignment >>> ``` -Although you can’t change tuple contents, you can always create a completely new tuple that replaces the old one. +Although you can’t change tuple contents, you can always create a +completely new tuple that replaces the old one. -```pycon +```python >>> t = (t[0], 75, t[2]) >>> t ('AA', 75, 32.2) @@ -274,9 +285,10 @@ value is discarded. Although the above assignment might look like you are modifying the tuple, you are actually creating a new tuple and throwing the old one away. -Tuples are often used to pack and unpack values into variables. Try the following: +Tuples are often used to pack and unpack values into variables. Try +the following: -```pycon +```python >>> name, shares, price = t >>> name 'AA' @@ -289,7 +301,7 @@ Tuples are often used to pack and unpack values into variables. Try the followin Take the above variables and pack them back into a tuple -```pycon +```python >>> t = (name, 2*shares, price) >>> t ('AA', 150, 32.2) @@ -300,7 +312,7 @@ Take the above variables and pack them back into a tuple An alternative to a tuple is to create a dictionary instead. -```pycon +```python >>> d = { 'name' : row[0], 'shares' : int(row[1]), @@ -313,25 +325,27 @@ An alternative to a tuple is to create a dictionary instead. Calculate the total cost of this holding: -```pycon +```python >>> cost = d['shares'] * d['price'] >>> cost 3220.0000000000005 >>> ``` -Compare this example with the same calculation involving tuples above. Change the number of shares to 75. +Compare this example with the same calculation involving tuples +above. Change the number of shares to 75. -```pycon +```python >>> d['shares'] = 75 >>> d {'name': 'AA', 'shares': 75, 'price': 75} >>> ``` -Unlike tuples, dictionaries can be freely modified. Add some attributes: +Unlike tuples, dictionaries can be freely modified. Add some +attributes: -```pycon +```python >>> d['date'] = (6, 11, 2007) >>> d['account'] = 12345 >>> d @@ -343,15 +357,16 @@ Unlike tuples, dictionaries can be freely modified. Add some attributes: If you turn a dictionary into a list, you’ll get all of its keys: -```pycon +```python >>> list(d) ['name', 'shares', 'price', 'date', 'account'] >>> ``` -Similarly, if you use the `for` statement to iterate on a dictionary, you will get the keys: +Similarly, if you use the `for` statement to iterate on a dictionary, +you will get the keys: -```pycon +```python >>> for k in d: print('k =', k) @@ -365,7 +380,7 @@ k = account Try this variant that performs a lookup at the same time: -```pycon +```python >>> for k in d: print(k, '=', d[k]) @@ -379,7 +394,7 @@ account = 12345 You can also obtain all of the keys using the `keys()` method: -```pycon +```python >>> keys = d.keys() >>> keys dict_keys(['name', 'shares', 'price', 'date', 'account']) @@ -388,20 +403,24 @@ dict_keys(['name', 'shares', 'price', 'date', 'account']) `keys()` is a bit unusual in that it returns a special `dict_keys` object. -This is an overlay on the original dictionary that always gives you the current keys—even if the dictionary changes. For example, try this: +This is an overlay on the original dictionary that always gives you +the current keys—even if the dictionary changes. For example, try +this: -```pycon +```python >>> del d['account'] >>> keys dict_keys(['name', 'shares', 'price', 'date']) >>> ``` -Carefully notice that the `'account'` disappeared from `keys` even though you didn’t call `d.keys()` again. +Carefully notice that the `'account'` disappeared from `keys` even +though you didn’t call `d.keys()` again. -A more elegant way to work with keys and values together is to use the `items()` method. This gives you `(key, value)` tuples: +A more elegant way to work with keys and values together is to use the +`items()` method. This gives you `(key, value)` tuples: -```pycon +```python >>> items = d.items() >>> items dict_items([('name', 'AA'), ('shares', 75), ('price', 32.2), ('date', (6, 11, 2007))]) @@ -415,9 +434,10 @@ date = (6, 11, 2007) >>> ``` -If you have tuples such as `items`, you can create a dictionary using the `dict()` function. Try it: +If you have tuples such as `items`, you can create a dictionary using +the `dict()` function. Try it: -```pycon +```python >>> items dict_items([('name', 'AA'), ('shares', 75), ('price', 32.2), ('date', (6, 11, 2007))]) >>> d = dict(items) diff --git a/Notes/02_Working_with_data/02_Containers.md b/Notes/02_Working_with_data/02_Containers.md index 5d04d36..99d38d6 100644 --- a/Notes/02_Working_with_data/02_Containers.md +++ b/Notes/02_Working_with_data/02_Containers.md @@ -1,5 +1,9 @@ +[Contents](../Contents) \| [Previous (2.1 Datatypes)](01_Datatypes) \| [Next (2.3 Formatting)](03_Formatting) + # 2.2 Containers +This section discusses lists, dictionaries, and sets. + ### Overview Programs often have to work with many objects. @@ -11,11 +15,11 @@ There are three main choices to use. * Lists. Ordered data. * Dictionaries. Unordered data. -* Sets. Unordered collection +* Sets. Unordered collection of unique items. ### Lists as a Container -Use a list when the order of the data matters. Remember that lists can hold any kind of objects. +Use a list when the order of the data matters. Remember that lists can hold any kind of object. For example, a list of tuples. ```python @@ -47,7 +51,7 @@ An example when reading records from a file. ```python records = [] # Initial empty list -with open('portfolio.csv', 'rt') as f: +with open('Data/portfolio.csv', 'rt') as f: for line in f: row = line.split(',') records.append((row[0], int(row[1])), float(row[2])) @@ -69,7 +73,7 @@ prices = { Here are some simple lookups: -```pycon +```python >>> prices['IBM'] 93.37 >>> prices['GOOG'] @@ -95,7 +99,7 @@ An example populating the dict from the contents of a file. ```python prices = {} # Initial empty dict -with open('prices.csv', 'rt') as f: +with open('Data/prices.csv', 'rt') as f: for line in f: row = line.split(',') prices[row[0]] = float(row[1]) @@ -143,12 +147,13 @@ holidays = { Then to access: -```pycon ->>> holidays[3, 14] 'Pi day' +```python +>>> holidays[3, 14] +'Pi day' >>> ``` -*Neither a list nor another dictionary can serve as a dictionary key, because lists and dictionaries are mutable.* +*Neither a list, a set, nor another dictionary can serve as a dictionary key, because lists and dictionaries are mutable.* ### Sets @@ -162,7 +167,7 @@ tech_stocks = set(['IBM', 'AAPL', 'MSFT']) Sets are useful for membership tests. -```pycon +```python >>> tech_stocks set(['AAPL', 'IBM', 'MSFT']) >>> 'IBM' in tech_stocks @@ -194,6 +199,9 @@ s1 - s2 # Set difference ## Exercises +In these exercises, you start building one of the major programs used +for the rest of this course. Do your work in the file `Work/report.py`. + ### Exercise 2.4: A list of tuples The file `Data/portfolio.csv` contains a list of stocks in a @@ -227,7 +235,8 @@ that file, define a function `read_portfolio(filename)` that opens a given portfolio file and reads it into a list of tuples. To do this, you’re going to make a few minor modifications to the above code. -First, instead of defining `total_cost = 0`, you’ll make a variable that’s initially set to an empty list. For example: +First, instead of defining `total_cost = 0`, you’ll make a variable +that’s initially set to an empty list. For example: ```python portfolio = [] @@ -251,7 +260,7 @@ interpreter): *Hint: Use `-i` when executing the file in the terminal* -```pycon +```python >>> portfolio = read_portfolio('Data/portfolio.csv') >>> portfolio [('AA', 100, 32.2), ('IBM', 50, 91.1), ('CAT', 150, 83.44), ('MSFT', 200, 51.23), @@ -291,14 +300,15 @@ That said, you can also rewrite the last for-loop using a statement like this: ### Exercise 2.5: List of Dictionaries -Take the function you wrote in part (a) and modify to represent each +Take the function you wrote in Exercise 2.4 and modify to represent each stock in the portfolio with a dictionary instead of a tuple. In this dictionary use the field names of "name", "shares", and "price" to represent the different columns in the input file. -Experiment with this new function in the same manner as you did in Exercise 2.4. +Experiment with this new function in the same manner as you did in +Exercise 2.4. -```pycon +```python >>> portfolio = read_portfolio('portfolio.csv') >>> portfolio [{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1}, @@ -327,7 +337,7 @@ often preferred because the resulting code is easier to read later. Viewing large dictionaries and lists can be messy. To clean up the output for debugging, considering using the `pprint` function. -```pycon +```python >>> from pprint import pprint >>> pprint(portfolio) [{'name': 'AA', 'price': 32.2, 'shares': 100}, @@ -346,7 +356,7 @@ A dictionary is a useful way to keep track of items where you want to look up items using an index other than an integer. In the Python shell, try playing with a dictionary: -```pycon +```python >>> prices = { } >>> prices['IBM'] = 92.45 >>> prices['MSFT'] = 45.12 @@ -388,7 +398,7 @@ A few little tips that you’ll need for this part. First, make sure you use the `csv` module just as you did before—there’s no need to reinvent the wheel here. -```pycon +```python >>> import csv >>> f = open('Data/prices.csv', 'r') >>> rows = csv.reader(f) @@ -409,7 +419,8 @@ an empty list—meaning no data was present on that line. There’s a possibility that this could cause your program to die with an exception. Use the `try` and `except` statements to catch this as -appropriate. +appropriate. Thought: would it be better to guard against bad data with +an `if`-statement instead? Once you have written your `read_prices()` function, test it interactively to make sure it works: @@ -425,10 +436,11 @@ interactively to make sure it works: ### Exercise 2.7: Finding out if you can retire -Tie all of this work together by adding the statements to your -`report.py` program. It takes the list of stocks in Exercise 2.5 and -the dictionary of prices in Exercise 2.6 and computes the current -value of the portfolio along with the gain/loss. +Tie all of this work together by adding a few additional statements to +your `report.py` program that compute gain/loss. These statements +should take the list of stocks in Exercise 2.5 and the dictionary of +prices in Exercise 2.6 and computes the current value of the portfolio +along with the gain/loss. [Contents](../Contents) \| [Previous (2.1 Datatypes)](01_Datatypes) \| [Next (2.3 Formatting)](03_Formatting) diff --git a/Notes/02_Working_with_data/03_Formatting.md b/Notes/02_Working_with_data/03_Formatting.md index 97ab2cd..a468ac1 100644 --- a/Notes/02_Working_with_data/03_Formatting.md +++ b/Notes/02_Working_with_data/03_Formatting.md @@ -1,7 +1,9 @@ +[Contents](../Contents) \| [Previous (2.2 Containers)](02_Containers) \| [Next (2.4 Sequences)](04_Sequences) + # 2.3 Formatting -This is a slight digression, but when you work with data, you often want to -produce structured output (tables, etc.). For example: +This section is a slight digression, but when you work with data, you +often want to produce structured output (tables, etc.). For example: ```code Name Shares Price @@ -61,7 +63,7 @@ Common modifiers adjust the field width and decimal precision. This is a partia ### Dictionary Formatting -You can use the `format_map()` method on strings. +You can use the `format_map()` method to apply string formatting to a dictionary of values: ```python >>> s = { @@ -74,7 +76,23 @@ You can use the `format_map()` method on strings. >>> ``` -It uses the same `f-strings` but takes the values from the supplied dictionary. +It uses the same codes as `f-strings` but takes the values from the +supplied dictionary. + +### format() method + +There is a method `format()` that can apply formatting to arguments or +keyword arguments. + +```python +>>> '{name:>10s} {shares:10d} {price:10.2f}'.format(name='IBM', shares=100, price=91.1) +' IBM 100 91.10' +>>> '{:10s} {:10d} {:10.2f}'.format('IBM', 100, 91.1) +' IBM 100 91.10' +>>> +``` + +Frankly, `format()` is a bit verbose. I prefer f-strings. ### C-Style Formatting @@ -89,7 +107,8 @@ You can also use the formatting operator `%`. '3.14' ``` -This requires a single item or a tuple on the right. Format codes are modeled after the C `printf()` as well. +This requires a single item or a tuple on the right. Format codes are +modeled after the C `printf()` as well. *Note: This is the only formatting available on byte strings.* @@ -101,25 +120,6 @@ b'Dave has 37 messages' ## Exercises -In Exercise 2.7, you wrote a program called `report.py` that computed the gain/loss of a -stock portfolio. In this exercise, you're going to modify it to produce a table like this: - -```code - Name Shares Price Change - ---------- ---------- ---------- ---------- - AA 100 9.22 -22.98 - IBM 50 106.28 15.18 - CAT 150 35.46 -47.98 - MSFT 200 20.89 -30.34 - GE 95 13.48 -26.89 - MSFT 50 20.89 -44.21 - IBM 100 106.28 35.84 -``` - -In this report, "Price" is the current share price of the stock and -"Change" is the change in the share price from the initial purchase -price. - ### Exercise 2.8: How to format numbers A common problem with printing numbers is specifying the number of @@ -145,7 +145,7 @@ Full documentation on the formatting codes used f-strings can be found [here](https://docs.python.org/3/library/string.html#format-specification-mini-language). Formatting is also sometimes performed using the `%` operator of strings. -```pycon +```python >>> print('%0.4f' % value) 42863.1000 >>> print('%16.2f' % value) @@ -159,7 +159,7 @@ Documentation on various codes used with `%` can be found Although it’s commonly used with `print`, string formatting is not tied to printing. If you want to save a formatted string. Just assign it to a variable. -```pycon +```python >>> f = '%0.4f' % value >>> f '42863.1000' @@ -168,6 +168,26 @@ If you want to save a formatted string. Just assign it to a variable. ### Exercise 2.9: Collecting Data +In Exercise 2.7, you wrote a program called `report.py` that computed the gain/loss of a +stock portfolio. In this exercise, you're going to start modifying it to produce a table like this: + +``` + Name Shares Price Change +---------- ---------- ---------- ---------- + AA 100 9.22 -22.98 + IBM 50 106.28 15.18 + CAT 150 35.46 -47.98 + MSFT 200 20.89 -30.34 + GE 95 13.48 -26.89 + MSFT 50 20.89 -44.21 + IBM 100 106.28 35.84 +``` + +In this report, "Price" is the current share price of the stock and +"Change" is the change in the share price from the initial purchase +price. + + In order to generate the above report, you’ll first want to collect all of the data shown in the table. Write a function `make_report()` that takes a list of stocks and dictionary of prices as input and @@ -176,7 +196,7 @@ returns a list of tuples containing the rows of the above table. Add this function to your `report.py` file. Here’s how it should work if you try it interactively: -```pycon +```python >>> portfolio = read_portfolio('Data/portfolio.csv') >>> prices = read_prices('Data/prices.csv') >>> report = make_report(portfolio, prices) @@ -197,7 +217,7 @@ if you try it interactively: Redo the for-loop in Exercise 2.9, but change the print statement to format the tuples. -```pycon +```python >>> for r in report: print('%10s %10d %10.2f %10.2f' % r) @@ -211,7 +231,7 @@ format the tuples. You can also expand the values and use f-strings. For example: -```pycon +```python >>> for name, shares, price, change in report: print(f'{name:>10s} {shares:>10d} {price:>10.2f} {change:>10.2f}') @@ -251,32 +271,32 @@ This string is just a bunch of "-" characters under each field name. For example When you’re done, your program should produce the table shown at the top of this exercise. -```code - Name Shares Price Change - ---------- ---------- ---------- ---------- - AA 100 9.22 -22.98 - IBM 50 106.28 15.18 - CAT 150 35.46 -47.98 - MSFT 200 20.89 -30.34 - GE 95 13.48 -26.89 - MSFT 50 20.89 -44.21 - IBM 100 106.28 35.84 +``` + Name Shares Price Change +---------- ---------- ---------- ---------- + AA 100 9.22 -22.98 + IBM 50 106.28 15.18 + CAT 150 35.46 -47.98 + MSFT 200 20.89 -30.34 + GE 95 13.48 -26.89 + MSFT 50 20.89 -44.21 + IBM 100 106.28 35.84 ``` ### Exercise 2.12: Formatting Challenge How would you modify your code so that the price includes the currency symbol ($) and the output looks like this: -```code - Name Shares Price Change - ---------- ---------- ---------- ---------- - AA 100 $9.22 -22.98 - IBM 50 $106.28 15.18 - CAT 150 $35.46 -47.98 - MSFT 200 $20.89 -30.34 - GE 95 $13.48 -26.89 - MSFT 50 $20.89 -44.21 - IBM 100 $106.28 35.84 +``` + Name Shares Price Change +---------- ---------- ---------- ---------- + AA 100 $9.22 -22.98 + IBM 50 $106.28 15.18 + CAT 150 $35.46 -47.98 + MSFT 200 $20.89 -30.34 + GE 95 $13.48 -26.89 + MSFT 50 $20.89 -44.21 + IBM 100 $106.28 35.84 ``` [Contents](../Contents) \| [Previous (2.2 Containers)](02_Containers) \| [Next (2.4 Sequences)](04_Sequences) diff --git a/Notes/02_Working_with_data/04_Sequences.md b/Notes/02_Working_with_data/04_Sequences.md index cb53e8b..df2dd3c 100644 --- a/Notes/02_Working_with_data/04_Sequences.md +++ b/Notes/02_Working_with_data/04_Sequences.md @@ -1,14 +1,16 @@ +[Contents](../Contents) \| [Previous (2.3 Formatting)](03_Formatting) \| [Next (2.5 Collections)](05_Collections) + # 2.4 Sequences ### Sequence Datatypes Python has three *sequence* datatypes. -* String: `'Hello'`. A string is considered a sequence of characters. +* String: `'Hello'`. A string is a sequence of characters. * List: `[1, 4, 5]`. * Tuple: `('GOOG', 100, 490.1)`. -All sequences are ordered and have length. +All sequences are ordered, indexed by integers, and have a length. ```python a = 'Hello' # String @@ -28,7 +30,7 @@ len(c) # 3 Sequences can be replicated: `s * n`. -```pycon +```python >>> a = 'Hello' >>> a * 3 'HelloHelloHello' @@ -40,7 +42,7 @@ Sequences can be replicated: `s * n`. Sequences of the same type can be concatenated: `s + t`. -```pycon +```python >>> a = (1, 2, 3) >>> b = (4, 5) >>> a + b @@ -56,7 +58,7 @@ TypeError: can only concatenate tuple (not "list") to tuple ### Slicing Slicing means to take a subsequence from a sequence. -The syntax used is `s[start:end]`. Where `start` and `end` are the indexes of the subsequence you want. +The syntax is `s[start:end]`. Where `start` and `end` are the indexes of the subsequence you want. ```python a = [0,1,2,3,4,5,6,7,8] @@ -67,12 +69,12 @@ a[:3] # [0,1,2] ``` * Indices `start` and `end` must be integers. -* Slices do *not* include the end value. +* Slices do *not* include the end value. It is like a half-open interval from math. * If indices are omitted, they default to the beginning or end of the list. ### Slice re-assignment -Slices can also be reassigned and deleted. +On lists, slices can be reassigned and deleted. ```python # Reassignment @@ -90,9 +92,9 @@ del a[2:4] # [0,1,4,5,6,7,8] ### Sequence Reductions -There are some functions to reduce a sequence to a single value. +There are some common functions to reduce a sequence to a single value. -```pycon +```python >>> s = [1, 2, 3, 4] >>> sum(s) 10 @@ -106,9 +108,9 @@ There are some functions to reduce a sequence to a single value. ### Iteration over a sequence -The for-loop iterates over the elements in the sequence. +The for-loop iterates over the elements in a sequence. -```pycon +```python >>> s = [1, 4, 9, 16] >>> for i in s: ... print(i) @@ -121,7 +123,7 @@ The for-loop iterates over the elements in the sequence. ``` On each iteration of the loop, you get a new item to work with. -This new value is placed into an iteration variable. In this example, the +This new value is placed into the iteration variable. In this example, the iteration variable is `x`: ```python @@ -129,12 +131,12 @@ for x in s: # `x` is an iteration variable ...statements ``` -In each iteration, it overwrites the previous value (if any). +On each iteration, the previous value of the iteration variable is overwritten (if any). After the loop finishes, the variable retains the last value. -### `break` statement +### break statement -You can use the `break` statement to break out of a loop before it finishes iterating all of the elements. +You can use the `break` statement to break out of a loop early. ```python for name in namelist: @@ -145,14 +147,14 @@ for name in namelist: statements ``` -When the `break` statement is executed, it will exit the loop and move +When the `break` statement executes, it exits the loop and moves on the next `statements`. The `break` statement only applies to the inner-most loop. If this loop is within another loop, it will not break the outer loop. -### `continue` statement +### continue statement -To skip one element and move to the next one you use the `continue` statement. +To skip one element and move to the next one, use the `continue` statement. ```python for line in lines: @@ -188,10 +190,11 @@ for k in range(10,50,2): * The ending value is never included. It mirrors the behavior of slices. * `start` is optional. Default `0`. * `step` is optional. Default `1`. +* `range()` computes values as needed. It does not actually store a large range of numbers. -### `enumerate()` function +### enumerate() function -The `enumerate` function provides a loop with an extra counter value. +The `enumerate` function adds an extra counter value to iteration. ```python names = ['Elwood', 'Jake', 'Curtis'] @@ -201,7 +204,7 @@ for i, name in enumerate(names): # i = 2, name = 'Curtis' ``` -How to use enumerate: `enumerate(sequence [, start = 0])`. `start` is optional. +The general form is `enumerate(sequence [, start = 0])`. `start` is optional. A good example of using `enumerate()` is tracking line numbers while reading a file: ```python @@ -223,7 +226,7 @@ Using `enumerate` is less typing and runs slightly faster. ### For and tuples -You can loop with multiple iteration variables. +You can iterate with multiple iteration variables. ```python points = [ @@ -236,11 +239,12 @@ for x, y in points: # ... ``` -When using multiple variables, each tuple will be *unpacked* into a set of iteration variables. +When using multiple variables, each tuple is *unpacked* into a set of iteration variables. +The number of variables must match the of items in each tuple. -### `zip()` function +### zip() function -The `zip` function takes sequences and makes an iterator that combines them. +The `zip` function takes multiple sequences and makes an iterator that combines them. ```python columns = ['name', 'shares', 'price'] @@ -268,7 +272,7 @@ d = dict(zip(columns, values)) Try some basic counting examples: -```pycon +```python >>> for n in range(10): # Count 0 ... 9 print(n, end=' ') @@ -288,7 +292,7 @@ Try some basic counting examples: Interactively experiment with some of the sequence reduction operations. -```pycon +```python >>> data = [4, 9, 1, 25, 16, 100, 49] >>> min(data) 1 @@ -301,7 +305,7 @@ Interactively experiment with some of the sequence reduction operations. Try looping over the data. -```pycon +```python >>> for x in data: print(x) @@ -322,7 +326,7 @@ Sometimes the `for` statement, `len()`, and `range()` get used by novices in some kind of horrible code fragment that looks like it emerged from the depths of a rusty C program. -```pycon +```python >>> for n in range(len(data)): print(data[n]) @@ -338,10 +342,10 @@ it’s inefficient with memory and it runs a lot slower. Just use a normal `for` loop if you want to iterate over data. Use `enumerate()` if you happen to need the index for some reason. -### Exercise 2.15: A practical `enumerate()` example +### Exercise 2.15: A practical enumerate() example Recall that the file `Data/missing.csv` contains data for a stock -portfolio, but has some rows with missing data. Using `enumerate()` +portfolio, but has some rows with missing data. Using `enumerate()`, modify your `pcost.py` program so that it prints a line number with the warning message when it encounters bad input. @@ -352,7 +356,7 @@ Row 7: Couldn't convert: ['IBM', '', '70.44'] >>> ``` -To do this, you’ll need to change just a few parts of your code. +To do this, you’ll need to change a few parts of your code. ```python ... @@ -363,12 +367,12 @@ for rowno, row in enumerate(rows, start=1): print(f'Row {rowno}: Bad row: {row}') ``` -### Exercise 2.16: Using the `zip()` function +### Exercise 2.16: Using the zip() function -In the file `portfolio.csv`, the first line contains column +In the file `Data/portfolio.csv`, the first line contains column headers. In all previous code, we’ve been discarding them. -```pycon +```python >>> f = open('Data/portfolio.csv') >>> rows = csv.reader(f) >>> headers = next(rows) @@ -381,7 +385,7 @@ However, what if you could use the headers for something useful? This is where the `zip()` function enters the picture. First try this to pair the file headers with a row of data: -```pycon +```python >>> row = next(rows) >>> row ['AA', '100', '32.20'] @@ -395,10 +399,10 @@ We’ve used `list()` here to turn the result into a list so that you can see it. Normally, `zip()` creates an iterator that must be consumed by a for-loop. -This pairing is just an intermediate step to building a +This pairing is an intermediate step to building a dictionary. Now try this: -```pycon +```python >>> record = dict(zip(headers, row)) >>> record {'price': '32.20', 'name': 'AA', 'shares': '100'} @@ -462,14 +466,14 @@ out of it. As long as the file has the required columns, the code will work. Modify the `report.py` program you wrote in Section 2.3 that it uses the same technique to pick out column headers. -Try running the `report.py` program on the `Data/portfoliodate.csv` file and see that it -produces the same answer as before. +Try running the `report.py` program on the `Data/portfoliodate.csv` +file and see that it produces the same answer as before. ### Exercise 2.17: Inverting a dictionary A dictionary maps keys to values. For example, a dictionary of stock prices. -```pycon +```python >>> prices = { 'GOOG' : 490.1, 'AA' : 23.45, @@ -481,7 +485,7 @@ A dictionary maps keys to values. For example, a dictionary of stock prices. If you use the `items()` method, you can get `(key,value)` pairs: -```pycon +```python >>> prices.items() dict_items([('GOOG', 490.1), ('AA', 23.45), ('IBM', 91.1), ('MSFT', 34.23)]) >>> @@ -490,7 +494,7 @@ dict_items([('GOOG', 490.1), ('AA', 23.45), ('IBM', 91.1), ('MSFT', 34.23)]) However, what if you wanted to get a list of `(value, key)` pairs instead? *Hint: use `zip()`.* -```pycon +```python >>> pricelist = list(zip(prices.values(),prices.keys())) >>> pricelist [(490.1, 'GOOG'), (23.45, 'AA'), (91.1, 'IBM'), (34.23, 'MSFT')] @@ -500,7 +504,7 @@ However, what if you wanted to get a list of `(value, key)` pairs instead? Why would you do this? For one, it allows you to perform certain kinds of data processing on the dictionary data. -```pycon +```python >>> min(pricelist) (23.45, 'AA') >>> max(pricelist) @@ -523,7 +527,7 @@ values. Note that `zip()` is not limited to pairs. For example, you can use it with any number of input lists: -```pycon +```python >>> a = [1, 2, 3, 4] >>> b = ['w', 'x', 'y', 'z'] >>> c = [0.2, 0.4, 0.6, 0.8] @@ -534,7 +538,7 @@ with any number of input lists: Also, be aware that `zip()` stops once the shortest input sequence is exhausted. -```pycon +```python >>> a = [1, 2, 3, 4, 5, 6] >>> b = ['x', 'y', 'z'] >>> list(zip(a,b)) diff --git a/Notes/02_Working_with_data/05_Collections.md b/Notes/02_Working_with_data/05_Collections.md index c2c1a21..183ad11 100644 --- a/Notes/02_Working_with_data/05_Collections.md +++ b/Notes/02_Working_with_data/05_Collections.md @@ -1,3 +1,5 @@ +[Contents](../Contents) \| [Previous (2.4 Sequences)](04_Sequences) \| [Next (2.6 List Comprehensions)](06_List_comprehension) + # 2.5 collections module The `collections` module provides a number of useful objects for data handling. @@ -20,6 +22,8 @@ portfolio = [ There are two `IBM` entries and two `GOOG` entries in this list. The shares need to be combined together somehow. +### Counters + Solution: Use a `Counter`. ```python @@ -94,7 +98,7 @@ bash % python3 -i report.py Suppose you wanted to tabulate the total number of shares of each stock. This is easy using `Counter` objects. Try it: -```pycon +```python >>> portfolio = read_portfolio('Data/portfolio.csv') >>> from collections import Counter >>> holdings = Counter() @@ -129,7 +133,7 @@ If you want to rank the values, do this: Let’s grab another portfolio of stocks and make a new Counter: -```pycon +```python >>> portfolio2 = read_portfolio('Data/portfolio2.csv') >>> holdings2 = Counter() >>> for s in portfolio2: @@ -142,7 +146,7 @@ Counter({'HPQ': 250, 'GE': 125, 'AA': 50, 'MSFT': 25}) Finally, let’s combine all of the holdings doing one simple operation: -```pycon +```python >>> holdings Counter({'MSFT': 250, 'IBM': 150, 'CAT': 150, 'AA': 100, 'GE': 95}) >>> holdings2 @@ -157,4 +161,11 @@ This is only a small taste of what counters provide. However, if you ever find yourself needing to tabulate values, you should consider using one. +### Commentary: collections module + +The `collections` module is one of the most useful library modules +in all of Python. In fact, we could do an extended tutorial on just +that. However, doing so now would also be a distraction. For now, +put `collections` on your list of bedtime reading for later. + [Contents](../Contents) \| [Previous (2.4 Sequences)](04_Sequences) \| [Next (2.6 List Comprehensions)](06_List_comprehension) diff --git a/Notes/02_Working_with_data/06_List_comprehension.md b/Notes/02_Working_with_data/06_List_comprehension.md index e93689c..81d0578 100644 --- a/Notes/02_Working_with_data/06_List_comprehension.md +++ b/Notes/02_Working_with_data/06_List_comprehension.md @@ -1,13 +1,16 @@ +[Contents](../Contents) \| [Previous (2.5 Collections)](05_Collections) \| [Next (2.7 Object Model)](07_Objects) + # 2.6 List Comprehensions A common task is processing items in a list. This section introduces list comprehensions, -a useful tool for doing just that. +a powerful tool for doing just that. ### Creating new lists -A list comprehension creates a new list by applying an operation to each element of a sequence. +A list comprehension creates a new list by applying an operation to +each element of a sequence. -```pycon +```python >>> a = [1, 2, 3, 4, 5] >>> b = [2*x for x in a ] >>> b @@ -17,7 +20,7 @@ A list comprehension creates a new list by applying an operation to each element Another example: -```pycon +```python >>> names = ['Elwood', 'Jake'] >>> a = [name.lower() for name in names] >>> a @@ -31,7 +34,7 @@ The general syntax is: `[ for in ]`. You can also filter during the list comprehension. -```pycon +```python >>> a = [1, -5, 4, 2, -2, 10] >>> b = [2*x for x in a if x > 0 ] >>> b @@ -42,7 +45,7 @@ You can also filter during the list comprehension. ### Use cases List comprehensions are hugely useful. For example, you can collect values of a specific -record field: +dictionary fields: ```python stocknames = [s['name'] for s in stocks] @@ -91,20 +94,22 @@ it's fine to view it as a cool list shortcut. ## Exercises -Start by running your `report.py` program so that you have the portfolio of stocks loaded in the interactive mode. +Start by running your `report.py` program so that you have the +portfolio of stocks loaded in the interactive mode. ```bash bash % python3 -i report.py ``` -Now, at the Python interactive prompt, type statements to perform the operations described below. -These operations perform various kinds of data reductions, transforms, and queries on the portfolio data. +Now, at the Python interactive prompt, type statements to perform the +operations described below. These operations perform various kinds of +data reductions, transforms, and queries on the portfolio data. ### Exercise 2.19: List comprehensions Try a few simple list comprehensions just to become familiar with the syntax. -```pycon +```python >>> nums = [1,2,3,4] >>> squares = [ x * x for x in nums ] >>> squares @@ -115,31 +120,35 @@ Try a few simple list comprehensions just to become familiar with the syntax. >>> ``` -Notice how the list comprehensions are creating a new list with the data suitably transformed or filtered. +Notice how the list comprehensions are creating a new list with the +data suitably transformed or filtered. ### Exercise 2.20: Sequence Reductions Compute the total cost of the portfolio using a single Python statement. -```pycon +```python +>>> portfolio = read_portfolio('Data/portfolio.csv') >>> cost = sum([ s['shares'] * s['price'] for s in portfolio ]) >>> cost 44671.15 >>> ``` -After you have done that, show how you can compute the current value of the portfolio using a single statement. +After you have done that, show how you can compute the current value +of the portfolio using a single statement. -```pycon +```python >>> value = sum([ s['shares'] * prices[s['name']] for s in portfolio ]) >>> value 28686.1 >>> ``` -Both of the above operations are an example of a map-reduction. The list comprehension is mapping an operation across the list. +Both of the above operations are an example of a map-reduction. The +list comprehension is mapping an operation across the list. -```pycon +```python >>> [ s['shares'] * s['price'] for s in portfolio ] [3220.0000000000005, 4555.0, 12516.0, 10246.0, 3835.1499999999996, 3254.9999999999995, 7044.0] >>> @@ -161,7 +170,7 @@ Try the following examples of various data queries. First, a list of all portfolio holdings with more than 100 shares. -```pycon +```python >>> more100 = [ s for s in portfolio if s['shares'] > 100 ] >>> more100 [{'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 51.23, 'name': 'MSFT', 'shares': 200}] @@ -170,7 +179,7 @@ First, a list of all portfolio holdings with more than 100 shares. All portfolio holdings for MSFT and IBM stocks. -```pycon +```python >>> msftibm = [ s for s in portfolio if s['name'] in {'MSFT','IBM'} ] >>> msftibm [{'price': 91.1, 'name': 'IBM', 'shares': 50}, {'price': 51.23, 'name': 'MSFT', 'shares': 200}, @@ -180,7 +189,7 @@ All portfolio holdings for MSFT and IBM stocks. A list of all portfolio holdings that cost more than $10000. -```pycon +```python >>> cost10k = [ s for s in portfolio if s['shares'] * s['price'] > 10000 ] >>> cost10k [{'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 51.23, 'name': 'MSFT', 'shares': 200}] @@ -191,7 +200,7 @@ A list of all portfolio holdings that cost more than $10000. Show how you could build a list of tuples `(name, shares)` where `name` and `shares` are taken from `portfolio`. -```pycon +```python >>> name_shares =[ (s['name'], s['shares']) for s in portfolio ] >>> name_shares [('AA', 100), ('IBM', 50), ('CAT', 150), ('MSFT', 200), ('GE', 95), ('MSFT', 50), ('IBM', 100)] @@ -201,9 +210,9 @@ Show how you could build a list of tuples `(name, shares)` where `name` and `sha If you change the the square brackets (`[`,`]`) to curly braces (`{`, `}`), you get something known as a set comprehension. This gives you unique or distinct values. -For example, this determines the set of stock names that appear in `portfolio`: +For example, this determines the set of unique stock names that appear in `portfolio`: -```pycon +```python >>> names = { s['name'] for s in portfolio } >>> names { 'AA', 'GE', 'IBM', 'MSFT', 'CAT'] } @@ -213,7 +222,7 @@ For example, this determines the set of stock names that appear in `portfolio`: If you specify `key:value` pairs, you can build a dictionary. For example, make a dictionary that maps the name of a stock to the total number of shares held. -```pycon +```python >>> holdings = { name: 0 for name in names } >>> holdings {'AA': 0, 'GE': 0, 'IBM': 0, 'MSFT': 0, 'CAT': 0} @@ -222,7 +231,7 @@ For example, make a dictionary that maps the name of a stock to the total number This latter feature is known as a **dictionary comprehension**. Let’s tabulate: -```pycon +```python >>> for s in portfolio: holdings[s['name']] += s['shares'] @@ -231,9 +240,10 @@ This latter feature is known as a **dictionary comprehension**. Let’s tabulate >>> ``` -Try this example that filters the `prices` dictionary down to only those names that appear in the portfolio: +Try this example that filters the `prices` dictionary down to only +those names that appear in the portfolio: -```pycon +```python >>> portfolio_prices = { name: prices[name] for name in names } >>> portfolio_prices {'AA': 9.22, 'GE': 13.48, 'IBM': 106.28, 'MSFT': 20.89, 'CAT': 35.46} @@ -242,12 +252,14 @@ Try this example that filters the `prices` dictionary down to only those names t ### Exercise 2.23: Extracting Data From CSV Files -Knowing how to use various combinations of list, set, and dictionary comprehensions can be useful in various forms of data processing. -Here’s an example that shows how to extract selected columns from a CSV file. +Knowing how to use various combinations of list, set, and dictionary +comprehensions can be useful in various forms of data processing. +Here’s an example that shows how to extract selected columns from a +CSV file. First, read a row of header information from a CSV file: -```pycon +```python >>> import csv >>> f = open('Data/portfoliodate.csv') >>> rows = csv.reader(f) @@ -259,23 +271,24 @@ First, read a row of header information from a CSV file: Next, define a variable that lists the columns that you actually care about: -```pycon +```python >>> select = ['name', 'shares', 'price'] >>> ``` Now, locate the indices of the above columns in the source CSV file: -```pycon +```python >>> indices = [ headers.index(colname) for colname in select ] >>> indices [0, 3, 4] >>> ``` -Finally, read a row of data and turn it into a dictionary using a dictionary comprehension: +Finally, read a row of data and turn it into a dictionary using a +dictionary comprehension: -```pycon +```python >>> row = next(rows) >>> record = { colname: row[index] for colname, index in zip(select, indices) } # dict-comprehension >>> record @@ -286,7 +299,7 @@ Finally, read a row of data and turn it into a dictionary using a dictionary com If you’re feeling comfortable with what just happened, read the rest of the file: -```pycon +```python >>> portfolio = [ { colname: row[index] for colname, index in zip(select, indices) } for row in rows ] >>> portfolio [{'price': '91.10', 'name': 'IBM', 'shares': '50'}, {'price': '83.44', 'name': 'CAT', 'shares': '150'}, diff --git a/Notes/02_Working_with_data/07_Objects.md b/Notes/02_Working_with_data/07_Objects.md index 2818ff4..72d1cf8 100644 --- a/Notes/02_Working_with_data/07_Objects.md +++ b/Notes/02_Working_with_data/07_Objects.md @@ -1,3 +1,5 @@ +[Contents](../Contents) \| [Previous (2.6 List Comprehensions)](06_List_comprehension) \| [Next (3 Program Organization)](../03_Program_organization/00_Overview) + # 2.7 Objects This section introduces more details about Python's internal object model and @@ -31,9 +33,11 @@ A picture of the underlying memory operations. In this example, there is only one list object `[1,2,3]`, but there are four different references to it. +![References](references.png) + This means that modifying a value affects *all* references. -```pycon +```python >>> a.append(999) >>> a [1,2,3,999] @@ -44,14 +48,15 @@ This means that modifying a value affects *all* references. >>> ``` -Notice how a change in the original list shows up everywhere else (yikes!). -This is because no copies were ever made. Everything is pointing to the same thing. +Notice how a change in the original list shows up everywhere else +(yikes!). This is because no copies were ever made. Everything is +pointing to the same thing. ### Reassigning values Reassigning a value *never* overwrites the memory used by the previous value. -```pycon +```python a = [1,2,3] b = a a = [4,5,6] @@ -69,13 +74,14 @@ foot at some point. Typical scenario. You modify some data thinking that it's your own private copy and it accidentally corrupts some data in some other part of the program. -*Comment: This is one of the reasons why the primitive datatypes (int, float, string) are immutable (read-only).* +*Comment: This is one of the reasons why the primitive datatypes (int, + float, string) are immutable (read-only).* ### Identity and References -Use ths `is` operator to check if two values are exactly the same object. +Use the `is` operator to check if two values are exactly the same object. -```pycon +```python >>> a = [1,2,3] >>> b = a >>> a is b @@ -86,7 +92,7 @@ True `is` compares the object identity (an integer). The identity can be obtained using `id()`. -```pycon +```python >>> id(a) 3588944 >>> id(b) @@ -94,11 +100,27 @@ obtained using `id()`. >>> ``` +Note: It is almost always better to use `==` for checking objects. The behavior +of `is` is often unexpected: + +```python +>>> a = [1,2,3] +>>> b = a +>>> c = [1,2,3] +>>> a is b +True +>>> a is c +False +>>> a == c +True +>>> +``` + ### Shallow copies Lists and dicts have methods for copying. -```pycon +```python >>> a = [2,3,[100,101],4] >>> b = list(a) # Make a copy >>> a is b @@ -118,14 +140,16 @@ True ``` For example, the inner list `[100, 101]` is being shared. -This is knows as a shallow copy. +This is known as a shallow copy. Here is a picture. + +![Shallow copy](shallow.png) ### Deep copies Sometimes you need to make a copy of an object and all the objects contained withn it. You can use the `copy` module for this: -```pycon +```python >>> a = [2,3,[100,101],4] >>> import copy >>> b = copy.deepcopy(a) @@ -142,7 +166,7 @@ False Variable names do not have a *type*. It's only a name. However, values *do* have an underlying type. -```pycon +```python >>> a = 42 >>> b = 'Hello World' >>> type(a) @@ -151,7 +175,7 @@ However, values *do* have an underlying type. ``` -`type()` will tell you what it is. The type name is usually a function +`type()` will tell you what it is. The type name is usually used as a function that creates or converts a value to that type. ### Type Checking @@ -159,18 +183,21 @@ that creates or converts a value to that type. How to tell if an object is a specific type. ```python -if isinstance(a,list): +if isinstance(a, list): print('a is a list') ``` -Checking for one of many types. +Checking for one of many possible types. ```python if isinstance(a, (list,tuple)): print('a is a list or tuple') ``` -*Caution: Don't go overboard with type checking. It can lead to excessive complexity.* +*Caution: Don't go overboard with type checking. It can lead to +excessive code complexity. Usually you'd only do it if doing +so would prevent common mistakes made by others using your code. +* ### Everything is an object @@ -182,7 +209,7 @@ is said that all objects are "first-class". A simple example: -```pycon +```python >>> import math >>> items = [abs, math, ValueError ] >>> items @@ -201,8 +228,9 @@ Failed! >>> ``` -Here, `items` is a list containing a function, a module and an exception. -You can use the items in the list in place of the original names: +Here, `items` is a list containing a function, a module and an +exception. You can directly use the items in the list in place of the +original names: ```python items[0](-45) # abs @@ -210,6 +238,8 @@ items[1].sqrt(2) # math except items[2]: # ValueError ``` +With great power come responsibility. Just because you can do that doesn't me you should. + ## Exercises In this set of exercises, we look at some of the power that comes from first-class @@ -242,7 +272,7 @@ using some list basic operations. Make a Python list that contains the names of the conversion functions you would use to convert each column into the appropriate type: -```pycon +```python >>> types = [str, int, float] >>> ``` @@ -254,7 +284,7 @@ a value `x` into a given type (e.g., `str(x)`, `int(x)`, `float(x)`). Now, read a row of data from the above file: -```pycon +```python >>> import csv >>> f = open('Data/portfolio.csv') >>> rows = csv.reader(f) @@ -265,9 +295,10 @@ Now, read a row of data from the above file: >>> ``` -As noted, this row isn’t enough to do calculations because the types are wrong. For example: +As noted, this row isn’t enough to do calculations because the types +are wrong. For example: -```pycon +```python >>> row[1] * row[2] Traceback (most recent call last): File "", line 1, in @@ -275,9 +306,10 @@ TypeError: can't multiply sequence by non-int of type 'str' >>> ``` -However, maybe the data can be paired up with the types you specified in `types`. For example: +However, maybe the data can be paired up with the types you specified +in `types`. For example: -```pycon +```python >>> types[1] >>> row[1] @@ -287,7 +319,7 @@ However, maybe the data can be paired up with the types you specified in `types` Try converting one of the values: -```pycon +```python >>> types[1](row[1]) # Same as int(row[1]) 100 >>> @@ -295,7 +327,7 @@ Try converting one of the values: Try converting a different value: -```pycon +```python >>> types[2](row[2]) # Same as float(row[2]) 32.2 >>> @@ -303,7 +335,7 @@ Try converting a different value: Try the calculation with converted values: -```pycon +```python >>> types[1](row[1])*types[2](row[2]) 3220.0000000000005 >>> @@ -311,7 +343,7 @@ Try the calculation with converted values: Zip the column types with the fields and look at the result: -```pycon +```python >>> r = list(zip(types, row)) >>> r [(, 'AA'), (, '100'), (,'32.20')] @@ -321,10 +353,10 @@ Zip the column types with the fields and look at the result: You will notice that this has paired a type conversion with a value. For example, `int` is paired with the value `'100'`. -The zipped list is useful if you want to perform conversions on all of the values, one -after the other. Try this: +The zipped list is useful if you want to perform conversions on all of +the values, one after the other. Try this: -```pycon +```python >>> converted = [] >>> for func, val in zip(types, row): converted.append(func(val)) @@ -336,14 +368,15 @@ after the other. Try this: >>> ``` -Make sure you understand what’s happening in the above code. -In the loop, the `func` variable is one of the type conversion functions (e.g., -`str`, `int`, etc.) and the `val` variable is one of the values like -`'AA'`, `'100'`. The expression `func(val)` is converting a value (kind of like a type cast). +Make sure you understand what’s happening in the above code. In the +loop, the `func` variable is one of the type conversion functions +(e.g., `str`, `int`, etc.) and the `val` variable is one of the values +like `'AA'`, `'100'`. The expression `func(val)` is converting a +value (kind of like a type cast). The above code can be compressed into a single list comprehension. -```pycon +```python >>> converted = [func(val) for func, val in zip(types, row)] >>> converted ['AA', 100, 32.2] @@ -352,10 +385,11 @@ The above code can be compressed into a single list comprehension. ### Exercise 2.25: Making dictionaries -Remember how the `dict()` function can easily make a dictionary if you have a sequence of key names and values? -Let’s make a dictionary from the column headers: +Remember how the `dict()` function can easily make a dictionary if you +have a sequence of key names and values? Let’s make a dictionary from +the column headers: -```pycon +```python >>> headers ['name', 'shares', 'price'] >>> converted @@ -365,9 +399,10 @@ Let’s make a dictionary from the column headers: >>> ``` -Of course, if you’re up on your list-comprehension fu, you can do the whole conversion in a single shot using a dict-comprehension: +Of course, if you’re up on your list-comprehension fu, you can do the +whole conversion in a single step using a dict-comprehension: -```pycon +```python >>> { name: func(val) for name, func, val in zip(headers, types, row) } {'price': 32.2, 'name': 'AA', 'shares': 100} >>> @@ -375,11 +410,13 @@ Of course, if you’re up on your list-comprehension fu, you can do the whole co ### Exercise 2.26: The Big Picture -Using the techniques in this exercise, you could write statements that easily convert fields from just about any column-oriented datafile into a Python dictionary. +Using the techniques in this exercise, you could write statements that +easily convert fields from just about any column-oriented datafile +into a Python dictionary. Just to illustrate, suppose you read data from a different datafile like this: -```pycon +```python >>> f = open('Data/dowstocks.csv') >>> rows = csv.reader(f) >>> headers = next(rows) @@ -393,7 +430,7 @@ Just to illustrate, suppose you read data from a different datafile like this: Let’s convert the fields using a similar trick: -```pycon +```python >>> types = [str, float, str, str, float, float, float, float, int] >>> converted = [func(val) for func, val in zip(types, row)] >>> record = dict(zip(headers, converted)) @@ -408,6 +445,10 @@ Let’s convert the fields using a similar trick: >>> ``` -Spend some time to ponder what you’ve done in this exercise. We’ll revisit these ideas a little later. +Bonus: How would you modify this example to additionally parse the +`date` entry into a tuple such as `(6, 11, 2007)`? + +Spend some time to ponder what you’ve done in this exercise. We’ll +revisit these ideas a little later. [Contents](../Contents) \| [Previous (2.6 List Comprehensions)](06_List_comprehension) \| [Next (3 Program Organization)](../03_Program_organization/00_Overview) diff --git a/Notes/02_Working_with_data/references.png b/Notes/02_Working_with_data/references.png new file mode 100644 index 0000000000000000000000000000000000000000..7204bd0a9c2ec15e1333951947483486c7ae9c04 GIT binary patch literal 40244 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 literal 0 HcmV?d00001 diff --git a/Notes/02_Working_with_data/shallow.png b/Notes/02_Working_with_data/shallow.png new file mode 100644 index 0000000000000000000000000000000000000000..bdfa56d8a104cea2ffa93b0e97572e139618ee03 GIT binary patch literal 36369 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

5=sg%bIN@GuK--b6%z;NC*{k7LKQI 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