Functions are Objects, Too
Consider this interactive session.
>>> def square(x): return x*x ... >>> square(5) 25 >>> cows = square >>> cows(5) 25
A function name is just a variable name. This name points at the code in memory that runs the function.
>>> type(cows) <class 'function'>
There is a type for functions.
A Mini Case Study
Let's write a Python function that prints an HTML5 table of values for a function, where the values are specified in a numerical list.
Here is the function to do this job, with a stub so Python will run.
def table(value_list, f): """value_list is a list of numbers f is a function whose domain contains those numbers return a string that is a table of values for f in HTML5 format.""" pass
Begin by putting in the top and bottom for the table.
def table(value_list, f): """value_list is a list of numbers f is a function whose domain contains those numbers return a string that is a table of values for f in HTML5 format.""" pass out = "<table>\n" out += "<tr><th>x</th><th>f(x)</th></tr>\n" # what should happen here? # for every value in the list, generate a row. out += "</table>\n" return out
When we write a function, we strive for atomicity of purpose. Your function should accomplish a single task. If it's longer than a screenful of code, it's time to think about breaking things up into manageable pieces.
So we will write a function that will generate rows.
Table rows look like this.
<tr><td>item1</td><td>item2</td></tr>
Just gank that and stick it in our table row function.
def table(value_list, f): """value_list is a list of numbers f is a function whose domain contains those numbers return a string that is a table of values for f in HTML5 format.""" pass out = "<table>\n" out += "<tr><th>x</th><th>f(x)</th></tr>\n" # what should happen here? # for every value in the list, generate a row. out += "</table>\n" return out def table_row(value, f): """make a table row for the table of values""" return "<tr><td>item1</td><td>item2</td></tr>"
Now let's use a format string to forma our row.
def table_row(value, f): """make a table row for the table of values""" return "<tr><td>value</td><td>f(value)</td></tr>"
Test it with this statement.
print(table_row(5, square))
unix> python table.py <tr><td>5</td><td>25</td></tr>
Now how do we make our way through the list? [1, 2, 3, 4, 5, 6] <function square at 0x7fddad62a200> (base) MAC:Mon Sep 14:09:56:week3> !vi vi table.py (base) MAC:Mon Sep 14:09:56:week3> !p python table.py <tr><td>5</td><td>25</td></tr> None (base) MAC:Mon Sep 14:09:56:week3> !vi vi table.py (base) MAC:Mon Sep 14:09:56:week3> !p python table.py <tr><td>5</td><td>25</td></tr> <table> <tr><th>x</th><th>f(x)</th></tr> <tr><td>1</td><td>1</td></tr> <tr><td>2</td><td>4</td></tr> <tr><td>3</td><td>9</td></tr> <tr><td>4</td><td>16</td></tr> <tr><td>5</td><td>25</td></tr> <tr><td>6</td><td>36</td></tr> </table> (base) MAC:Mon Sep 14:09:56:week3> !vi vi table.py (base) MAC:Mon Sep 14:09:58:week3> python Python 3.7.4 (default, Aug 13 2019, 15:17:50) [Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin Type "help", "copyright", "credits" or "license" for more information. >>> def square(x): return x*x ... >>> str(square( ... )) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: square() missing 1 required positional argument: 'x' >>> str(square) '<function square at 0x7faddfe737a0>' >>> (base) MAC:Mon Sep 14:10:00:week3> ls table.py (base) MAC:Mon Sep 14:10:00:week3> vi table.py (base) MAC:Mon Sep 14:10:00:week3> python table.py <tr><td>5</td><td>25</td></tr> <table> <tr><th>x</th><th>f(x)</th></tr> <tr><td>1</td><td>1</td></tr> <tr><td>2</td><td>4</td></tr> <tr><td>3</td><td>9</td></tr> <tr><td>4</td><td>16</td></tr> <tr><td>5</td><td>25</td></tr> <tr><td>6</td><td>36</td></tr> </table> <table> <tr><th>x</th><th>f(x)</th></tr> <tr><td>0.1</td><td>0.010000000000000002</td></tr> <tr><td>0.2</td><td>0.04000000000000001</td></tr> <tr><td>0.3</td><td>0.09</td></tr> <tr><td>0.4</td><td>0.16000000000000003</td></tr> <tr><td>0.5</td><td>0.25</td></tr> <tr><td>0.6</td><td>0.36</td></tr> </table> (base) MAC:Mon Sep 14:10:00:week3> ls table.py (base) MAC:Mon Sep 14:10:02:week3> vi pancakes.py (base) MAC:Mon Sep 14:10:03:week3> python pancakes.py {'x': 5} {'x': 5} 125 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f9fd8dc42d0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'pancakes.py', '__cached__': None, 'square': <function square at 0x7f9fd8e04320>, 'cube': <function cube at 0x7f9fd8e04440>, 'x': 5} (base) MAC:Mon Sep 14:10:04:week3>
x | f(x) |
---|---|
1 | 1 |
2 | 4 |
3 | 9 |
4 | 16 |
5 | 25 |
6 | 36 |
7 | 49 |
8 | 64 |
9 | 81 |
The Heap and the Stack
There are two areas of memory with a program: the heap and the stack.
The heap is a data warehouse. All objects are stored on the heap. Python garbage collects the heap. Big things like elephants, War and Peace and the King James Bible can go on the heap.
The heap is divided into bytes. Each byte has an address. That is address that a variable stores.
Variables and function calls live on the stack.
stack frame: a container holds a function's local variables If another function is called, a bookmark keeps track of where it left off. (activation record)