From ea25f0d39b0e1c239a4dac77b76730ee56678805 Mon Sep 17 00:00:00 2001 From: David Beazley Date: Sun, 24 May 2020 16:56:48 -0500 Subject: [PATCH] Section 1 --- Notes/01_Introduction/01_Python.md | 4 +- Notes/01_Introduction/02_Hello_world.md | 506 ++++++++++++++++++++++++ Notes/01_Introduction/03_Numbers.md | 235 +++++++++++ Notes/01_Introduction/04_Strings.md | 423 ++++++++++++++++++++ Notes/01_Introduction/05_Lists.md | 405 +++++++++++++++++++ Notes/01_Introduction/06_Files.md | 217 ++++++++++ Notes/01_Introduction/07_Functions.md | 257 ++++++++++++ 7 files changed, 2044 insertions(+), 3 deletions(-) create mode 100644 Notes/01_Introduction/02_Hello_world.md create mode 100644 Notes/01_Introduction/03_Numbers.md create mode 100644 Notes/01_Introduction/04_Strings.md create mode 100644 Notes/01_Introduction/05_Lists.md create mode 100644 Notes/01_Introduction/06_Files.md create mode 100644 Notes/01_Introduction/07_Functions.md diff --git a/Notes/01_Introduction/01_Python.md b/Notes/01_Introduction/01_Python.md index 9b9dd18..991196c 100644 --- a/Notes/01_Introduction/01_Python.md +++ b/Notes/01_Introduction/01_Python.md @@ -183,6 +183,4 @@ exercise work. For example: >>> ``` -\[ **[Solution](soln1_1.html)** | **[Next](ex1_2.html)** | -**[Index](index.html)** \] - +[Next]("02_Hello_World.html") diff --git a/Notes/01_Introduction/02_Hello_world.md b/Notes/01_Introduction/02_Hello_world.md new file mode 100644 index 0000000..80c2723 --- /dev/null +++ b/Notes/01_Introduction/02_Hello_world.md @@ -0,0 +1,506 @@ +# 1.2 A First Python Program + +## Notes + +### Running Python + +Python programs run inside an interpreter. + +The interpreter is a simple "console-based" application that normally starts from a command shell. + +```bash +python3 +Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04) +[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin +Type "help", "copyright", "credits" or "license" for more information. +>>> +``` + +Expert programmers usually have no problem using the interpreter in +this way, but it's not so user-friendly for beginners. That said, +you'll become a better Python programmer if you're able to use the +interpreter from the shell earlier rather than later. + +### Interactive Mode + +When you start Python, you get an *interactive* mode where you can experiment. + +If you start typing statements, they will run immediately. There is no edit/compile/run/debug cycle. + +```python +>>> print('hello world') +hello world +>>> 37*42 +1554 +>>> for i in range(5): +... print(i) +... +0 +1 +2 +3 +4 +>>> +``` + +This *read-eval* loop is very useful for debugging and exploration. + +Let's take a closer look at the elements: + +- `>>>` is the interpreter prompt for starting a new statement. +- `...` is the interpreter prompt for continuing a statements. Enter a blank like to finish typing and run the statements. + +The `...` prompt may or may not be shown depending on how you are using Python. For this course, +it is shown as blanks to make it easier to cut/paste code samples. + +The underscore `_` holds the last result. + +```pycon +>>> 37 * 42 +1554 +>>> _ * 2 +3108 +>>> _ + 50 +3158 +>>> +``` + +*This is only true in the interactive mode.* You never use `_` in a program. + + +Type `help(command)` to get information about `command`. Type `help()` with no name for interactive help. + +```code +>>> help(range) +Help on class range in module builtins: + +class range(object) + | range(stop) -> range object + | range(start, stop[, step]) -> range object + | + | Return an object that produces a sequence of integers from start (inclusive) + | to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1. + | start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3. + | These are exactly the valid indices for a list of 4 elements. + | When step is given, it specifies the increment (or decrement). +``` + +The official documentation is at [http://docs.python.org](http://docs.python.org). + +### Creating programs + +Programs are put in `.py` files. + +```python +# hello.py +print('hello world') +``` + +You can create these files with your favorite text editor. + +### Running Programs + +To execute a program, run it in the terminal with the `python` command. +For example, in command-line Unix: + +```bash +bash % python3 hello.py +hello world +bash % +``` + +Or from the Windows shell: + +```shell +C:\SomeFolder>hello.py +hello world + +C:\SomeFolder>c:\python36\python hello.py +hello world +``` + +Note: On Windows, you may need to specify a full path to the Python interpreter such as `c:\python36\python`. + +### A Sample Program + +Let's begin this part by trying to solve the following problem: + +> One morning, you go out and place a dollar bill on the sidewalk by the Sears tower. +> Each day thereafter, you go out double the number of bills. +> How long does it take for the stack of bills to exceed the height of the tower? + +Here's a solution: + +```python +# sears.py +bill_thickness = 0.11 * 0.001 # Meters (0.11 mm) +sears_height = 442 # Height (meters) +num_bills = 1 +day = 1 + +while num_bills * bill_thickness < sears_height: + print(day, num_bills, num_bills * bill_thickness) + day = day + 1 + num_bills = num_bills * 2 + +print('Number of days', day) +print('Number of bills', num_bills) +print('Final height', num_bills * bill_thickness) +``` + +When we run it, we have the solution. + +```bash +bash % python3 sears.py 1 1 0.00011 +2 2 0.00022 +3 4 0.00044 +4 8 0.00088 +5 16 0.00176 +6 32 0.00352 +... +21 1048576 115.34336 +22 2097152 230.68672 Number of days 23 Number of bills 4194304 Final height 461.37344 +``` + +Using this program as a guide, you can learn a few core concepts about Python. + +### Statements + +A python program is a sequence of statements: + +```python +a = 3 + 4 +b = a * 2 +print(b) +``` + +Each statement is terminated by a newline. Statements are executed one after the other until you reach the end of the file. + +### Comments + +Comments are text that will not be executed. + +```python +a = 3 + 4 +# This is a comment +b = a * 2 +print(b) +``` + +Comments are denoted by `#` and extend to the end of the line. + +### Variables + +A variable is a name for a value. + +You can use letters (lower and upper-case) from a to z. As well as the character underscore `_`. + +Numbers can also be part of the name of a variable, except as the first character. + +```python +height = 442 # valid +_height = 442 # valid +height2 = 442 # valid +2height = 442 # invalid +``` + +### Types + +Variables do not need to be declared with the type of the value. The type +is associated with the value on the right hand side, not name of the variable. + +```python +height = 442 # An integer +height = 442 # Floating point +height = 'Really tall' # A string +``` + +### Case Sensitivity + +Python is case sensitive. Upper and lower-case letters are considered different letters. + +These are all different variables: + +```python +name = 'Jake' +Name = 'Elwood' +NAME = 'Guido' +``` + +Language statements are always lower-case. + +```python +while x < 0: # OK +WHILE x < 0: # ERROR +``` + +### Looping + +Looping is a way to execute a set of instructions any number of times. + +There are many ways to accomplish this in Python, one of them is the `while` statement: + +```python +while num_bills * bill_thickness < sears_height: + print(day, num_bills, num_bills * bill_thickness) + day = day + 1 + num_bills = num_bills * 2 + +print('Number of days', days) +``` + +The statements below the `while` will execute as long as the expression after the `while` is `true`. + +### Indentation + +Indentation in Python is used to denote a set of statements that go together. + +From our previous example: + +```python +while num_bills * bill_thickness < sears_height: + print(day, num_bills, num_bills * bill_thickness) + day = day + 1 + num_bills = num_bills * 2 + +print('Number of days', days) +``` + +The indentation means that the following statements go together under the `while`. + +```python + print(day, num_bills, num_bills * bill_thickness) + day = day + 1 + num_bills = num_bills * 2 +``` + +Because the next statement is not indented, it means that it does not belong to the previous set. + +```python +print('Number of days', days) +``` + +The empty line is just for readability. It does not affect the execution. + +### Blocks + +A block is a set of statements grouped together. + +In our previous example, the statements within the `while` form a *block*. + +```python + print(day, num_bills, num_bills * bill_thickness) + day = day + 1 + num_bills = num_bills * 2 +``` + +Indentation within the block must be consistent. + +```python +while num_bills * bill_thickness < sears_height: + print(day, num_bills, num_bills * bill_thickness) + day = day + 1 # ERROR + num_bills = num_bills * 2 +``` + +The character colon `:` indicates the start of a block and must be present. + +```python +while num_bills * bill_thickness < sears_height: +``` + +### Indentation best practices + +* Use spaces instead of tabs. +* Use 4 spaces per level. +* Use a Python-aware editor. + +### Conditionals + +The `if` statement is used to execute a conditional: + +```python +if a > b: + # `a` is greater than `b` + print('Computer says no') +else: + # `a` is lower or equal to `b` + print('Computer says yes') +``` + +Depending on the values of `a` and `b`, the execution will jump to `print('Computer says no')` or `print('Computer says yes')`. + +You can check for multiple conditions with the `elif`. + +```python +if a > b: + # `a` is greater than `b` + print('Computer says no') +elif a == b: + # `a` is equal to `b` + print('Computer says yes') +else: + # `a` is lower to `b` + print('Computer says maybe') +``` + +### Printing + +The `print` function produces a single line of text with the values passed. + +```python +print('Hello world!') # Prints the text 'Hello world!' +``` + +You can use variables. The text printed will be the value of the variable, not the name. + +```python +x = 100 +print(x) # Prints the text '100' +``` + +If you pass more than one item to `print` they are separated by spaces. + +```python +name = 'Jake' +print('My name is', name) # Print the text 'My name is Jake' +``` + +`print()` always creates a new line at the end. + +```python +print('Hello') +print('My name is', 'Jake') +``` + +This prints: + +```code +Hello +My name is Jake +``` + +This can be avoided. + +```python +print('Hello', end=' ') +print('My name is', 'Jake') +``` + +The previous code will print: + +```code +Hello My name is Jake +``` + +### User input + +To read a line of typed user input, use the `input()` function: + +```python +name = input('Enter your name:') +print('Your name is', name) +``` + +`input` prints a prompt to the user and returns the response. + +This is useful for small programs, learning exercises or simple debugging. + +It is not widely used for real programs. + +### `pass` statement + +Sometimes you need to specify an empty block. + +The keyword `pass` is used for it. + +```python +if a > b: + pass +else: + print('Computer says false') +``` + +This is also called a "no-op" statement. It does nothing. It serves as a placeholder for statements. Possibly to be added later. + +## Exercises 1.2 + +### (a) The Bouncing Ball + +A rubber ball is dropped from a height of 100 meters and each time it hits the ground, it bounces back up to 3/5 the height it fell. +Write a program "bounce.py" that prints a table showing the height of the first 10 bounces. + +Your program should make a table that looks something like this: + +```code +1 60.0 +2 36.0 +3 21.599999999999998 +4 12.959999999999999 +5 7.775999999999999 +6 4.6655999999999995 +7 2.7993599999999996 +8 1.6796159999999998 +9 1.0077695999999998 +10 0.6046617599999998 +``` + +*Note: You can clean up the output a bit if you use the round() function. Try using it to round the output to 4 digits.* + +```code +1 60.0 +2 36.0 +3 21.6 +4 12.96 +5 7.776 +6 4.6656 +7 2.7994 +8 1.6796 +9 1.0078 +10 0.6047 +``` + +### (b) Debugging + +The following code fragment contains code from the Sears tower problem. It also has a bug in it. + +```python +# sears.py + +bill_thickness = 0.11 * 0.001 # Meters (0.11 mm) +sears_height = 442 # Height (meters) +num_bills = 1 +day = 1 + +while num_bills * bill_thickness < sears_height: + print(day, num_bills, num_bills * bill_thickness) + day = days + 1 + num_bills = num_bills * 2 + +print('Number of days', day) +print('Number of bills', num_bills) +print('Final height', num_bills * bill_thickness) +``` + +Copy and paste the code that appears above in a new program called `sears.py`. +When you run the code you will get an error message that causes the +program to crash like this: + +```code +Traceback (most recent call last): + File "sears.py", line 10, in + day = days + 1 +NameError: name 'days' is not defined +``` + +Reading error messages is an important part of Python code. If your program +crashes, the very last line of the traceback message is the actual reason why the +the program crashed. Above that, you should see a fragment of source code and then +an identifying filename and line number. + +* Which line is the error? +* What is the error? +* Fix the error +* Run the program successfully + +[Next]("03_Numbers.html") \ No newline at end of file diff --git a/Notes/01_Introduction/03_Numbers.md b/Notes/01_Introduction/03_Numbers.md new file mode 100644 index 0000000..fb99e18 --- /dev/null +++ b/Notes/01_Introduction/03_Numbers.md @@ -0,0 +1,235 @@ +# 1.3 Numbers and Booleans + +## Notes + +### Types of Numbers + +Python has 4 types of numbers: + +* Booleans +* Integers +* Floating point +* Complex (imaginary numbers) + +### Booleans + +Booleans have two values: `True`, `False`. + +```python +a = True +b = False +``` + +Numerically, they're evaluated as integers with value `1`, `0`. + +```python +c = 4 + True # 5 +d = False +if d == 0: + print('d is False') +``` + +*Don't do that, it would be odd.* + +### Integers + +Signed values of arbitrary size and base: + +```python +a = 37 +b = -299392993727716627377128481812241231 +c = 0x7fa8 # Hexadecimal +d = 0o253 # Octal +e = 0b10001111 # Binary +``` + +Common operations: + +| | | +| ---- | ---- | +| + | Add | +| - | Subtract | +| * | Multiply | +| / | Divide | +| // | Floor divide | +| % | Modulo | +| ** | Power | +| << | Bit shift left | +| >> | Bit shift right | +| & | Big-wise AND | +| | | Bit-wise OR | +| ^ | Bit-wise XOR | +| ~ | Bit-wise NOT | +| abs(x) | Absolute value | + +### Comparisons + +The following comparison / relational operators work with numbers: + +`<`, `>`, `<=` `>=`, `==`, `!=` + +You can form more complex boolean expressions using + +`and`, `or`, `not` + +Here are a few examples: + +```python +if b >= a and b <= c: + print('b is between a and c') + +if not (b < a or b > c): + print('b is still between a and c') +``` + +### Floating point (float) + +Use a decimal or exponential notation to specify a floating point value: + +```python +a = 37.45 +b = 4e5 # 4 x 10**5 or 400,000 +c = -1.345e-10 +``` + +Floats are represented as double precision using the native CPU representation [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754). + +> 17 digits or precision +> Exponent from -308 to 308 + +This is the same as the `double` type in the programming language C. + +Be aware that floating point numbers are inexact when representing decimals. + +```python +>>> a = 2.1 + 4.2 +>>> a === 6.3 +False +>>> a +6.300000000000001 +>>> +``` + +This is **not a Python issue**, but the underlying floating point hardware on the CPU. + +Common Operations: + +| | | +| ---- | ---- | +| + | Add | +| - | Subtract | +| * | Multiply | +| / | Divide | +| // | Floor divide | +| % | Modulo (remainder) | +| ** | Power | +| abs(x) | Absolute value | + +Theses are the same operators as Integers, except for the bit-wise operators. + +Additional math functions are found in the `math` module. + +```python +import math +a = math.sqrt(x) +b = math.sin(x) +c = math.cos(x) +d = math.tan(x) +e = math.log(x) +``` + +### Converting Numbers + +The type name can be used to convert values: + +```python +a = int(x) # Convert x to integer +b = float(x) # Convert x to float +``` + +Try it out. + +```python +>>> a = 3.14159 +>>> int(a) +3 +>>> b = '3.14159' # It also works with strings containing numbers +>>> float(b) +3.15159 +>>> +``` + +## Exercise 1.3 + +### (a) Dave's mortgage + +Dave has decided to take out a 30-year fixed rate mortgage of $500,000 with Guido’s Mortgage, Stock Investment, and Bitcoin trading corporation. +The interest rate is 5% and the monthly payment is $2684.11. + +Here is a program that calculates the total amount that Dave will have to pay over the life of the mortgage: + +```python +# mortgage.py + +principal = 500000.0 +rate = 0.05 +payment = 2684.11 +total_paid = 0.0 + +while principal > 0: + principal = principal * (1+rate/12) - payment + total_paid = total_paid + payment + +print('Total paid', total_paid) +``` + +Enter this program and run it. You should get an answer of `966,279.6`. + +### (b) Extra payments + +Suppose Dave pays an extra $1000/month for the first 12 months of the mortgage? + +Modify the program to incorporate this extra payment and have it print the total amount paid along with the number of months required. + +When you run the new program, it should report a total payment of `929,965.62` over 342 months. + +### (c) Making an Extra Payment Calculator + +Modify the program so that extra payment information can be more generally handled. + +Make it so that the user can set these variables: + +```python +extra_payment_start_month = 60 +extra_payment_end_month = 108 +extra_payment = 1000 +``` + +Make the program look at these variables and calculate the total paid appropriately. + +How much will Dave pay if he pays an extra $1000/month for 4 years starting in year 5 of the mortgage? + +### (d) Making a table + +Modify the program to print out a table showing the month, total paid so far, and the remaining principal. + +The output should look something like this: + +```bash +1 2684.11 499399.22 +2 5368.22 498795.94 +3 8052.33 498190.15 +4 10736.44 497581.83 +5 13420.55 496970.98 +... +308 875705.88 674.44 +309 878389.99 -2006.86 +Total paid 878389.99 +Months 309 +``` + +### (e) Bonus + +While you’re at it, fix the program to correct the for overpayment that occurs in the last month. + +[Next]("04_Strings.html") \ No newline at end of file diff --git a/Notes/01_Introduction/04_Strings.md b/Notes/01_Introduction/04_Strings.md new file mode 100644 index 0000000..0a7c8c9 --- /dev/null +++ b/Notes/01_Introduction/04_Strings.md @@ -0,0 +1,423 @@ +# 1.4 Strings + +## Notes + +### Representing Text + +String are text literals written in programs with quotes. + +```python +# Single quote +a = 'Yeah but no but yeah but...' + +# Double quote +b = "computer says no" + +# Triple quotes +c = ''' +Look into my eyes, look into my eyes, the eyes, the eyes, the eyes, +not around the eyes, +don't look around the eyes, +look into my eyes, you're under. +''' +``` + +Triple quotes capture all text enclosed in multiple lines. + +### String escape codes + +| | | +| ---- | ---- | +| `'\n'` | Line feed | +| `'\r'` | Carriage return | +| `'\t'` | Tab | +| `'\''` | Literal single quote | +| `'\"'` | Literal double quote | +| `'\\'` | Backslash | + +These codes are inspired by C. + +### String Representation + +Each character represents a raw unicode code point. + + + +```python +a = '\xf1' # a = 'ñ' +b = '\u2200' # b = '∀' +c = '\U0001D122' # c = '𝄢' +d = '\N{FORALL}' # d = '∀' +``` + +Strings work like an array for accessing its characters. You use an integer index, starting at 0. + +```python +a = 'Hello world' +b = a[0] # 'H' +c = a[4] # 'o' +d = a[-1] # 'd' (end of string) +``` + +You can also slice or select substrings with `:`. + +```python +d = a[:5] # 'Hello' +e = a[6:] # 'world' +f = a[3:8] # 'lowo' +g = a[-5:] # 'world' +``` + +### String operations + +Concatenation, length, membership and replication. + +```python +# Concatenation (+) +a = 'Hello' + 'World' # 'HelloWorld' +b = 'Say ' + a # 'Say HelloWorld' + +# Length (len) +s = 'Hello' +len(s) # 5 + +# Membership test (`in`, `not in`) +t = 'e' in s # True +f = 'x' in s # False +tt = 'hi' not in s # True + +# Replication (s * n) +rep = s * 5 # 'HelloHelloHelloHelloHello' +``` + +### String methods + +Strings have methods that perform various operations with the string data. + +Stripping any leading / trailing white space. + +```python +s = ' Hello ' +t = s.strip() # 'Hello' +``` + +Case conversion. + +```python +s = 'Hello' +l = s.lower() # 'hello' +u = s.upper() # 'HELLO' +``` + +Replacing text. + +```python +s = 'Hello world' +t = s.replace('Hello' , 'Hallo') +``` + +**More string methods:** + +| | | +| ---- | ---- | +| s.endswith(suffix) | # Check if string ends with suffix | +| s.find(t) | # First occurrence of t in s | +| s.index(t) | # First occurrence of t in s | +| s.isalpha() | # Check if characters are alphabetic | +| s.isdigit() | # Check if characters are numeric | +| s.islower() | # Check if characters are lower-case | +| s.isupper() | # Check if characters are upper-case | +| s.join(slist) | # Joins lists using s as delimiter | +| s.lower() | # Convert to lower case | +| s.replace(old,new) | # Replace text | +| s.rfind(t) | # Search for t from end of string | +| s.rindex(t) | # Search for t from end of string | +| s.split([delim]) | # Split string into list of substrings | +| s.startswith(prefix) | # Check if string starts with prefix | +| s.strip() | # Strip leading/trailing space | +| s.upper() | # Convert to upper case | + +### String Mutability + +Strings are "immutable". They are read only. + +Once created, the value can't be changed. + +```python +>>> s = 'Hello World' +>>> s[1] = 'a' +Traceback (most recent call last): +File "", line 1, in +TypeError: 'str' object does not support item assignment +>>> +``` + +**All operations and methods that manipulate string data, always create new strings.** + +### String Conversions + +Use `str()` to convert a value to a string. + +```python +>>> x = 42 +>>> str(x) +'42' +>>> +``` + +### Byte Strings + +A string of 8-bit bytes. + +```python +data = b'Hello World\r\n' +``` + +By putting a little b before our string literal we specify that it is a byte string as opposed to a text string. + +Most of the usual string operations work. + +```python +len(data) # 13 +data[0:5] # b'Hello' +data.replace(b'Hello', b'Cruel') # b'Cruel World\r\n' +``` + +Indexing is a bit different because it returns byte values. + +```python +data[0] # 72 (ASCII code for 'H') +``` + +Conversion to/from text. + +```python +text = data.decode('utf-8') # bytes -> text +data = text.encode('utf-8') # text -> bytes +``` + +### Raw Strings + +String with uninterpreted backslash. + +```python +>>> rs = r'c:\newdata\test' # Raw (uninterpreted backslash) +>>> rs +'c:\\newdata\\test' +``` + +String is the literal text, exactly as typed. +This is useful in situations where the backslash has special significance. Example: filename, regular expressions, etc. + +### f-Strings + +String with formatted expression substitution. + +```python +>>> name = 'IBM' +>>> shares = 100 +>>> price = 91.1 +>>> a = f'{name:>10s} {shares:10d} {price:10.2f}' +>>> a +' IBM 100 91.10' +>>> b = f'Cost = ${shares*price:0.2f}' +>>> b +'Cost = $9110.00' +>>> +``` + +**Note: Requires Python 3.6 or newer.** + +## Exercises 1.4 + + +In this exercise, we experiment with operations on Python's string type. +You may want to do most of this exercise at the Python interactive prompt where you can easily see the results. +Important note: + +> In exercises where you are supposed to interact with the interpreter, +> `>>>` is the interpreter prompt that you get when Python wants +> you to type a new statement. Some statements in the exercise span +> multiple lines--to get these statements to run, you may have to hit +> 'return' a few times. Just a reminder that you *DO NOT* type +> the `>>>` when working these examples. + +Start by defining a string containing a series of stock ticker symbols like this: + +```pycon +>>> symbols = 'AAPL,IBM,MSFT,YHOO,SCO' +>>> +``` + +### (a): Extracting individual characters and substrings + +Strings are arrays of characters. Try extracting a few characters: + +```pycon +>>> symbols[0] +? +>>> symbols[1] +? +>>> symbols[2] +? +>>> symbols[-1] # Last character +? +>>> symbols[-2] # Negative indices are from end of string +? +>>> +``` + +### (b) Strings as read-only objects + +In Python, strings are read-only. + +Verify this by trying to change the first character of `symbols` to a lower-case 'a'. + +```python +>>> symbols[0] = 'a' +Traceback (most recent call last): + File "", line 1, in +TypeError: 'str' object does not support item assignment +>>> +``` + +### (c) String concatenation + +Although string data is read-only, you can always reassign a variable +to a newly created string. + +Try the following statement which concatenates a new symbol "GOOG" to +the end of `symbols`: + +```pycon +>>> symbols = symbols + 'GOOG' +>>> symbols +'AAPL,IBM,MSFT,YHOO,SCOGOOG' +>>> +``` + +Oops! That's not what we wanted. Fix it so that the `symbols` variable holds the value `'HPQ,AAPL,IBM,MSFT,YHOO,SCO,GOOG'`. + +In these examples, it might look like the original string is being +modified, in an apparent violation of strings being read only. Not +so. Operations on strings create an entirely new string each +time. When the variable name `symbols` is reassigned, it points to the +newly created string. Afterwards, the old string is destroyed since +it's not being used anymore. + +### (d) Membership testing (substring testing) + +Experiment with the `in` operator to check for substrings. At the interactive prompt, try these operations: + +```pycon +>>> 'IBM' in symbols +? +>>> 'AA' in symbols +? +>>> 'CAT' in symbols +? +>>> +``` + +*Why did the check for "AA" return `True`?* + +### (e) String Methods + +At the Python interactive prompt, try experimenting with some of the string methods. + +```pycon +>>> symbols.lower() +? +>>> symbols +? +>>> +``` + +Remember, strings are always read-only. If you want to save the result of an operation, you need to place it in a variable: + +```pycon +>>> lowersyms = symbols.lower() +>>> +``` + +Try some more operations: + +```pycon +>>> symbols.find('MSFT') +? +>>> symbols[13:17] +? +>>> symbols = symbols.replace('SCO','DOA') +>>> symbols +? +>>> name = ' IBM \n' +>>> name = name.strip() # Remove surrounding whitespace +>>> name +? +>>> +``` + +### (f) f-strings + +Sometimes you want to create a string and embed the values of +variables into it. + +To do that, use an f-string. For example: + +```pycon +>>> name = 'IBM' +>>> shares = 100 +>>> price = 91.1 +>>> f'{shares} shares of {name} at ${price:0.2f}' +'100 shares of IBM at $91.10' +>>> +``` + +Modify the `mortgage.py` program from Exercise 1.3 to create its output using f-strings. +Try to make it so that output is nicely aligned. + +### Commentary + +As you start to experiment with the interpreter, you often want to know more about the operations supported by different objects. +For example, how do you find out what operations are available on a string? + +Depending on your Python environment, you might be able to see a list +of available methods via tab-completion. For example, try typing +this: + +```python +>>> s = 'hello world' +>>> s. +>>> +``` + +If hitting tab doesn't do anything, you can fall back to the +builtin-in `dir()` function. For example: + +```python +>>> s = 'hello' +>>> dir(s) +['__add__', '__class__', '__contains__', ..., 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] +>>> +``` + +`dir()` produces a list of all operations that can appear after the `(.)`. +Use the `help()` command to get more information about a specific operation: + +```python +>>> help(s.upper) +Help on built-in function upper: + +upper(...) + S.upper() -> string + + Return a copy of the string S converted to uppercase. +>>> +``` + +IDEs and alternative interactive shells often give you more help here. +For example, a popular alternative to Python's normal interactive mode +is IPython (http://ipython.org). IPython provides some nice features +such as tab-completion of method names, more integrated help and more. + +[Next]("05_Lists.html") \ No newline at end of file diff --git a/Notes/01_Introduction/05_Lists.md b/Notes/01_Introduction/05_Lists.md new file mode 100644 index 0000000..e056c9e --- /dev/null +++ b/Notes/01_Introduction/05_Lists.md @@ -0,0 +1,405 @@ +# 1.5 Lists + +## Notes + +### String Splitting + +Strings can represent fields of data. We can work with each field by splitting the string into a list. + +```pycon +>>> line = 'GOOG,100,490.10' +>>> row = line.split(',') +>>> row +['GOOG', '100', '490.10'] +>>> +``` + +A common use case for this splitting is when reading data from a file. You might read each line as text and then split the text into columns. + +### List operations + +Use square brackets to create a list: + +```python +names = [ 'Elwood', 'Jake', 'Curtis' ] +nums = [ 39, 38, 42, 65, 111] +``` + +It can hold items of any type. Add a new item using `append()`: + +```python +names.append('Murphy') # Adds at end +names.insert(2, 'Aretha') # Inserts in middle +``` + +Use `+` to concatenate lists: + +```python +s = [1, 2, 3] +t = ['a', 'b'] +s + t # [1, 2, 3, 'a', 'b'] +``` + +Lists are indexed by integers. Starting at 0. + +```python +names = [ 'Elwood', 'Jake', 'Curtis' ] + +names[0] # 'Elwood' +names[1] # 'Jake' +names[2] # 'Curtis' +``` + +Negative indices count from the end. + +```python +names[-1] # 'Curtis' +``` + +You can change on element in the list. + +```python +names[1] = 'Joliet Jake' +names # [ 'Elwood', 'Joliet Jake', 'Curtis' ] +``` + +Length of the list. + +```python +names = ['Elwood','Jake','Curtis'] +len(names) # 3 +``` + +Membership test (`in`, `not in`). + +```python +'Elwood' in names # True +'Britney' not in names # True +``` + +Replication (`s * n`). + +```python +s = [1, 2, 3] +s * 3 # [1, 2, 3, 1, 2, 3, 1, 2, 3] +``` + +### List Iteration & Search + +Iterating over the list contents. + +```python +for name in names: + # use name + # e.g. print(name) + ... +``` + +This is similar to a `foreach` statement from other programming languages. + +To find something quickly, use `index()`. + +```python +names = ['Elwood','Jake','Curtis'] +names.index('Curtis') # 2 +``` + +If the element is present more than one, it will return the index of the first occurrence. + +If the element is not found, it will raise a `ValueError` exception. + +### List Removal + +You can remove either with the element value or the index number. + +```python +# Using the value +names.remove('Curtis') + +# Using the index +del names[1] +``` + +Removing results in items moving down to fill the space vacated. There are no holes in the list. +If there are more than one occurrence of the element, `.remove()` will remove only the first occurrence. + +### List Sorting + +Lists can be sorted 'in-place'. + +```python +s = [10, 1, 7, 3] +s.sort() # [1, 3, 7, 10] + +# Reverse order +s = [10, 1, 7, 3] +s.sort(reverse=True) # [10, 7, 3, 1] + +# It works with any ordered data +s = ['foo', 'bar', 'spam'] +s.sort() # ['bar', 'foo', 'spam'] +``` + +### Lists and Math + +*Caution: Lists were not designed for math operations.* + +```pycon +>>> nums = [1, 2, 3, 4, 5] +>>> nums * 2 +[1, 2, 3, 4, 5, 1, 2, 3, 4, 5] +>>> nums + [10, 11, 12, 13, 14] +[1, 2, 3, 4, 5, 10, 11, 12, 13, 14] >>> +``` + +Specifically, lists don't represent vectors/matrices as in MATLAB, Octave, IDL, etc. +However, there are some packages to help you with that (e.g. numpy). + +## Exercises 1.5 + +In this exercise, we experiment with Python's list datatype. In the last exercise, you worked with strings containing stock symbols. + +```pycon +>>> symbols = 'HPQ,AAPL,IBM,MSFT,YHOO,DOA,GOOG' +``` + +Split it into a list of names using the `split()` operation of strings: + +```pycon +>>> symlist = symbols.split(',') +``` + +### (a) Extracting and reassigning list elements + +Try a few lookups: + +```python +>>> symlist[0] +'HPQ' +>>> symlist[1] +'AAPL' +>>> symlist[-1] +'GOOG' +>>> symlist[-2] +'DOA' +>>> +``` + +Try reassigning one value: + +```python +>>> symlist[2] = 'AIG' +>>> symlist +['HPQ', 'AAPL', 'AIG', 'MSFT', 'YHOO', 'DOA', 'GOOG'] +>>> +``` + +Take a few slices: + +```python +>>> symlist[0:3] +['HPQ', 'AAPL', 'AIG'] +>>> symlist[-2:] +['DOA', 'GOOG'] +>>> +``` + +Create an empty list and append an item to it. + +```python +>>> mysyms = [] +>>> mysyms.append('GOOG') +>>> mysyms +['GOOG'] +``` + +You can reassign a portion of a list to another list. For example: + +```python +>>> symlist[-2:] = mysyms +>>> symlist +['HPQ', 'AAPL', 'AIG', 'MSFT', 'YHOO', 'GOOG'] +>>> +``` + +When you do this, the list on the left-hand-side (`symlist`) will be resized as appropriate to make the right-hand-side (`mysyms`) fit. +For instance, in the above example, the last two items of `symlist` got replaced by the single item in the list `mysyms`. + +### (b) Looping over list items + +The `for` loop works by looping over data in a sequence such as a list. +Check this out by typing the following loop and watching what happens: + +```pycon +>>> for s in symlist: + print('s =', s) +# Look at the output +``` + +### (c) Membership tests + +Use the `in` or `not in` operator to check if `'AIG'`,`'AA'`, and `'CAT'` are in the list of symbols. + +```pycon +>>> # Is 'AIG' IN the `symlist`? +True +>>> # Is 'AA' IN the `symlist`? +False +>>> # Is 'CAT' NOT IN the `symlist`? +True +>>> +``` + +### (d) Appending, inserting, and deleting items + +Use the `append()` method to add the symbol `'RHT'` to end of `symlist`. + +```pycon +>>> # append 'RHT' +>>> symlist +['HPQ', 'AAPL', 'AIG', 'MSFT', 'YHOO', 'GOOG', 'RHT'] +>>> +``` + +Use the `insert()` method to insert the symbol `'AA'` as the second item in the list. + +```pycon +>>> # Insert 'AA' as the second item in the list +>>> symlist +['HPQ', 'AA', 'AAPL', 'AIG', 'MSFT', 'YHOO', 'GOOG', 'RHT'] +>>> +``` + +Use the `remove()` method to remove `'MSFT'` from the list. + +```pycon +>>> # Remove 'MSFT' +>>> symlist +['HPQ', 'AA', 'AAPL', 'AIG', 'YHOO', 'GOOG', 'RHT'] +>>> +``` + +Append a duplicate entry for `'YHOO'` at the end of the list. + +*Note: it is perfectly fine for a list to have duplicate values.* + +```pycon +>>> # Append 'YHOO' +>>> symlist +['HPQ', 'AA', 'AAPL', 'AIG', 'YHOO', 'GOOG', 'RHT', 'YHOO'] +>>> +``` + +Use the `index()` method to find the first position of `'YHOO'` in the list. + +```pycon +>>> # Find the first index of 'YHOO' +4 +>>> symlist[4] +'YHOO' +>>> +``` + +Count how many times `'YHOO'` is in the list: + +```pycon +>>> symlist.count('YHOO') +2 +>>> +``` + +Remove the first occurrence of `'YHOO'`. + +```pycon +>>> # Remove first occurrence 'YHOO' +>>> symlist +['HPQ', 'AA', 'AAPL', 'AIG', 'GOOG', 'RHT', 'YHOO'] +>>> +``` + +Just so you know, there is no method to find or remove all occurrences of an item. +However, we'll see an elegant way to do this in section 2. + +### (e) Sorting + +Want to sort a list? Use the `sort()` method. Try it out: + +```pycon +>>> symlist.sort() +>>> symlist +['AA', 'AAPL', 'AIG', 'GOOG', 'HPQ', 'RHT', 'YHOO'] +>>> +``` + +Want to sort in reverse? Try this: + +```pycon +>>> symlist.sort(reverse=True) +>>> symlist +['YHOO', 'RHT', 'HPQ', 'GOOG', 'AIG', 'AAPL', 'AA'] +>>> +``` + +Note: Sorting a list modifies its contents 'in-place'. That is, the elements of the list are shuffled around, but no new list is created as a result. + +### (f) Putting it all back together + +Want to take a list of strings and join them together into one string? +Use the `join()` method of strings like this (note: this looks funny at first). + +```pycon +>>> a = ','.join(symlist) +>>> a +'YHOO,RHT,HPQ,GOOG,AIG,AAPL,AA' +>>> b = ':'.join(symlist) +>>> b +'YHOO:RHT:HPQ:GOOG:AIG:AAPL:AA' +>>> c = ''.join(symlist) +>>> c +'YHOORHTHPQGOOGAIGAAPLAA' +>>> +``` + +### (g) Lists of anything + +Lists can contain any kind of object, including other lists (e.g., nested lists). +Try this out: + +```pycon +>>> nums = [101, 102, 103] +>>> items = ['spam', symlist, nums] +>>> items +['spam', ['YHOO', 'RHT', 'HPQ', 'GOOG', 'AIG', 'AAPL', 'AA'], [101, 102, 103]] +``` + +Pay close attention to the above output. `items` is a list with three elements. +The first element is a string, but the other two elements are lists. + +You can access items in the nested lists by using multiple indexing operations. + +```pycon +>>> items[0] +'spam' +>>> items[0][0] +'s' +>>> items[1] +['YHOO', 'RHT', 'HPQ', 'GOOG', 'AIG', 'AAPL', 'AA'] +>>> items[1][1] +'RHT' +>>> items[1][1][2] +'T' +>>> items[2] +[101, 102, 103] +>>> items[2][1] +102 +>>> +``` + +Even though it is technically possible to make very complicated list +structures, as a general rule, you want to keep things simple. +Usually lists hold items that are all the same kind of value. For +example, a list that consists entirely of numbers or a list of text +strings. Mixing different kinds of data together in the same list is +often a good way to make your head explode so it's best avoided. + +[Next]("06_Files.html") \ No newline at end of file diff --git a/Notes/01_Introduction/06_Files.md b/Notes/01_Introduction/06_Files.md new file mode 100644 index 0000000..7669e94 --- /dev/null +++ b/Notes/01_Introduction/06_Files.md @@ -0,0 +1,217 @@ +# 1.6 File Management + +## Notes + +### File Input and Output + +Open a file. + +```python +f = open('foo.txt', 'rt') # Open for reading (text) +g = open('bar.txt', 'wt') # Open for writing (text) +``` + +Reading data. + +```python +data = f.read() + +# Read only up to 'maxbytes' bytes +data = f.read([maxbytes]) +``` + +Writing text to a file. + +```python +g.write('some text') +``` + +Close when you are done. + +```python +f.close() +g.close() +``` + +Files should be properly closed. This is why the preferred approach is to use the `with` statement. + +```python +with open(filename, 'rt') as f: + # Use the file `f` + ... + # No need to close explicitly +...statements +``` + +This automatically closes the file when control leaves the indented code block. + +### Common Idioms for Reading File Data + +Reading an entire file all at once as a string. + +```python +with open('foo.txt', 'rt') as f: + data = f.read() + # `data` is a string with all the text in `foo.txt` +``` + +Reading a file line-by-line + +```python +with open(filename, 'rt') as f: + for line in f: + # Process the line `f` +``` + +Writing string data. + +```python +with open('outfile', 'wt') as f: + f.write('Hello World\n') + ... +``` + +Redirecting the print function. + +```python +with open('outfile', 'wt') as f: + print('Hello World', file=f) + ... +``` + +## Exercises 1.6 + +This exercise depends on a file `Data/portfolio.csv`. The file contains a list of lines with information on a portfolio of stocks. +Locate the file and look at its contents: + +### (a) File Preliminaries + +*Note: Make sure you are running Python in a location where you can access the `portfolio.csv` file. +You can find out where Python thinks it's running by doing this: + +```python +>>> import os +>>> os.getcwd() +'/Users/beazley/Desktop/practical-python' # Output vary +>>> +``` + +First, try reading the entire file all at once as a big string: + +```pycon +>>> with open('Data/portfolio.csv', 'rt') as f: + data = f.read() +>>> data +'name,shares,price\n"AA",100,32.20\n"IBM",50,91.10\n"CAT",150,83.44\n"MSFT",200,51.23\n"GE",95,40.37\n"MSFT",50,65.10\n"IBM",100,70.44\n' +>>> print(data) +name,shares,price +"AA",100,32.20 +"IBM",50,91.10 +"CAT",150,83.44 +"MSFT",200,51.23 +"GE",95,40.37 +"MSFT",50,65.10 +"IBM",100,70.44 +>>> +``` + +In the above example, it should be noted that Python has two modes of output. +In the first mode where you type `data` at the prompt, Python shows you the raw string representation including quotes and escape codes. +When you type `print(data)`, you get the actual formatted output of the string. + +Although reading a file all at once is simple, it is often not the +most appropriate way to do it—especially if the file happens to be +huge or if contains lines of text that you want to handle one at a +time. + +To read a file line-by-line, use a for-loop like this: + +```pycon +>>> with open('Data/portfolio.csv', 'rt') as f: + for line in f: + print(line, end='') +name,shares,price +"AA",100,32.20 +"IBM",50,91.10 +... +>>> +``` + +When you use this code as shown, lines are read until the end of the file is reached at which point the loop stops. + +On certain occasions, you might want to manually read or skip a *single* line of text (e.g., perhaps you want to skip the first line of column headers). + +```pycon +>>> f = open('Data/portfolio.csv', 'rt') +>>> headers = next(f) +>>> headers +'name,shares,price\n' +>>> for line in f: + print(line, end='') + +"AA",100,32.20 +"IBM",50,91.10 +... +>>> f.close() +>>> +``` + +`next()` returns the next line of text in the file. If you were to call it repeatedly, you would get successive lines. +However, just so you know, the `for` loop already uses `next()` to obtain its data. +Thus, you normally wouldn’t call it directly unless you’re trying to explicitly skip or read a single line as shown. + +Once you’re reading lines of a file, you can start to perform more processing such as splitting. +For example, try this: + +```pycon +>>> f = open('Data/portfolio.csv', 'rt') +>>> headers = next(f).split(',') +>>> headers +['name', 'shares', 'price\n'] +>>> for line in f: + row = line.split(',') + print(row) + +['"AA"', '100', '32.20\n'] +['"IBM"', '50', '91.10\n'] +... +>>> f.close() +``` + +*Note: In these examples, `f.close()` is being called explicitly because the `with` statement isn’t being used.* + +### (b) Reading a data file + +Now that you know how to read a file, let’s write a program to perform a simple calculation. + +The columns in `portfolio.csv` correspond to the stock name, number of +shares, and purchase price of a single share. Write a program called +`pcost.py` that opens this file, reads all lines, and calculates how +much it cost to purchase all of the shares in the portfolio. + +*Hint: to convert a string to an integer, use `int(s)`. To convert a string to a floating point, use `float(s)`.* + +Your program should print output such as the following: + +```bash +Total cost 44671.15 +``` + +### (c) Other kinds of 'files' + +What if you wanted to read a non-text file such as a gzip-compressed datafile? +The builtin `open()` function won’t help you here, but Python has a library module `gzip` that can read gzip compressed files. + +Try it: + +```pycon +>>> import gzip +>>> with gzip.open('Data/portfolio.csv.gz') as f: + for line in f: + print(line, end='') + +... look at the output ... +>>> +``` + +[Next]("07_Functions.html") \ No newline at end of file diff --git a/Notes/01_Introduction/07_Functions.md b/Notes/01_Introduction/07_Functions.md new file mode 100644 index 0000000..2c2a46d --- /dev/null +++ b/Notes/01_Introduction/07_Functions.md @@ -0,0 +1,257 @@ +# 1.7 Introduction to Functions + +## Notes + +### Custom Functions + +Use functions for code you want to reuse. Here is a function definition: + +```python +def sumcount(n): + ''' + Returns the sum of the first n integers + ''' + total = 0 + while n > 0: + total += n + n -= 1 + return total +``` + +Calling a function. + +```python +# Use parenthesis to call the function +a = sumcount(100) +``` + +A function is a series of statements that perform some task and return a result. +The `return` keyword is needed to explicitly specify the return value of the function. + +### Library Functions + +Python comes with a large standard library. +Library modules are accessed using `import`. + +```python +# `math` module +import math +x = math.sqrt(10) + +# `urllib.request` module +import urllib.request +u = urllib.request.urlopen('http://www.python.org/') data = u.read() +``` + +We will cover libraries and modules in more detail later. + +### Errors and exceptions + +Errors are reported as exceptions. An exception causes the program to stop. + +Try this in your python REPL. + +```pycon +>>> int('N/A') +Traceback (most recent call last): +File "", line 1, in +ValueError: invalid literal for int() with base 10: 'N/A' +>>> +``` + +For debugging purposes, the message describes what happened, where the error occurred and the traceback. + +### Catching and Handling Exceptions + +Exceptions can be caught and handled. + +To catch, use `try - except` statement. + +```python +for line in f: + fields = line.split() + try: + shares = int(fields[1]) + except ValueError: + print("Couldn't parse", line) + ... +``` + +The name `ValueError` must match the kind of error you are trying to catch. + +### Raising Exceptions + +To raise an exception, use the `raise` statement. + +```python +raise RuntimeError('What a kerfuffle') +``` + +This will cause the program to abord with an exception traceback. Unless caught by a `try-except` block. + +```bash +% python3 foo.py +Traceback (most recent call last): + File "foo.py", line 21, in + raise RuntimeError("What a kerfuffle") +RuntimeError: What a kerfuffle +``` + + +## Exercises 1.7 + +### (a) Defining a function + +You can define a simple function using the `def` statement. For example, + +```python +>>> def greeting(name): + 'Issues a greeting' + print('Hello', name) + +>>> greeting('Guido') +Hello Guido +>>> greeting('Paula') +Hello Paula +>>> +``` + +If the first statement of a function is a string, it serves as documentation. +Try typing a command such as `help(greeting)` to see it displayed. + +### (b) Turning a script into a function + +Take the code you wrote for the `pcost.py` program in the last exercise and turn it into a function `portfolio_cost(filename)`. +The function takes a filename as input, reads the portfolio data in that file, and returns the total cost of the portfolio. + +To use your function, change your program so that it looks something like this: + +```python +def portfolio_cost(filename): + ... + # Your code here + ... + +cost = portfolio_cost('Data/portfolio.csv') +print('Total cost:', cost) +``` + +When you run your program, you should see the same output as before. +After you’ve run your program, you can also call your function interactively by typing this: + +```bash +bash $ python3 -i pcost.py +``` + +This will allow you to call your function from the interactive mode. + +```python +>>> portfolio_cost('Data/portfolio.csv') +44671.15 +>>> +``` + +Being able to experiment with your code interactively is useful for testing and debugging. + +What happens if you try your function on a file with some missing fields? + +```pycon +>>> portfolio_cost('Data/missing.csv') +Traceback (most recent call last): + File "", line 1, in + File "pcost.py", line 11, in portfolio_cost + nshares = int(fields[1]) +ValueError: invalid literal for int() with base 10: '' +>>> +``` + +At this point, you’re faced with a decision. To make the program work +you can either sanitize the original input file by eliminating bad +lines or you can modify your code to handle the bad lines in some +manner. + +Modify the `pcost.py` program to catch the exception, print a warning message, +and continue processing the rest of the file. + +### (c) Using a library function + +Python comes with a large standard library of useful functions. +One library that might be useful here is the `csv` module. You should use it whenever you have to work with CSV data files. +Here is an example of how it works: + +```pycon +>>> import csv +>>> f = open('Data/portfolio.csv') +>>> rows = csv.reader(f) +>>> headers = next(rows) +>>> headers +['name', 'shares', 'price'] +>>> for row in rows: + print(row) + +['AA', '100', '32.20'] +['IBM', '50', '91.10'] +['CAT', '150', '83.44'] +['MSFT', '200', '51.23'] +['GE', '95', '40.37'] +['MSFT', '50', '65.10'] +['IBM', '100', '70.44'] +>>> f.close() +>>> +``` + +One nice thing about the `csv` module is that it deals with a variety of low-level details such as quoting and proper comma splitting. +In the above output, you’ll notice that it has stripped the double-quotes away from the names in the first column. + +Modify your `pcost.py` program so that it uses the `csv` module for parsing and try running earlier examples. + +### (d) Reading from the command line + +In the `pcost.py` program, the name of the input file has been hardwired into the code: + +```python +# pcost.py + +def portfolio_cost(filename): + ... + # Your code here + ... + +cost = portfolio_cost('Data/portfolio.csv') +print('Total cost:', cost) +``` + +That’s fine for learning and testing, but in a real program you probably wouldn’t do that. + +Instead, you might pass the name of the file in as an argument to a script. Try changing the bottom part of the program as follows: + +```python +# pcost.py +import sys + +def portfolio_cost(filename): + ... + # Your code here + ... + +if len(sys.argv) == 2: + filename = sys.argv[1] +else: + filename = 'portfolio.csv' + +cost = portfolio_cost(filename) +print('Total cost:', cost) +``` + +`sys.argv` is a list that contains passed arguments on the command line (if any). + +To run your program, you’ll need to run Python from the +terminal. + +For example, from bash on Unix: + +```bash +bash % python3 pcost.py Data/portfolio.csv +Total cost: 44671.15 +bash % +```