Sep 29 - more about functions
last Tuesday's homework
... generally looked good.
The assignment for next Tuesday, on chapter 6,
is posted. (We'll be doing both chapter 7 and 8
the following week, so if you want to get a headstart
on that, read ahead.
What I was looking for from the questions
on object-oriented programming concepts
when I said "use examples from your code".
Here is some python code using the Zelle's graphics module.
red_dot = Circle(Point(50, 40), 4)
red_dot.setFill("red")
Then we have :
Circle is a class (i.e. a template)
red_dot is an instance of class Circle
setFill is a method of class Circle (like a function)
"red" is an argument passed to setFill (an input variable)
Anything else you'd like to talk about ?
functions
Continuing our discussion of functions ...
- What is similar and different between "functions" and "methods"?
- What is similar and different about "local variables" and "object data"?
doctests
How do you know that a function is doing what you expect? Tests.
In my Tuesday notes at the end (which we didn't get to),
I describe what a "doctest" is - a very cool Python feature.
Here is python's docs about them :
So uppose you're writing a program that has something
to do with palindromes. One thing you'd need is
to have strings turned around backwards. So what
would a function look like to do that? Turns out
(google "python reverse") that you can reverse
lists easily. And we've seen before that you can
turn strings into lists and vice-versa. So you
play around at the command line and figure this out:
>>> word = "hello"
>>> chars = list(word)
>>> chars
['h', 'e', 'l', 'l', 'o']
>>> chars.reverse()
['o', 'l', 'l', 'e', 'h']
>>> import string
>>> string.join(chars, '')
'olleh'
This is a small specific piece of the larger
problem that you can tuck into a function.
Together with its docs and a test it would look like this:
def reverse_string(word):
""" Reverse a string.
input: a string, e.g. "hello"
output: the string backwards, e.g. "olleh"
>>> reverse_string("Can you hear me?")
'?em raeh uoy naC'
"""
import string
chars = list(word)
chars.reverse()
return string.join(chars, '')
if __name__ == "__main__": # This runs the doctests;
import doctest # just put these 3 lines at the
doctest.testmod() # end if you do this sort of testing.
Put this in "test.py", run it with "python test.py -v".
Also put the wrong thing in the test, and run it with "python test.py".
program development
Ideas :
- top-down (look at big picture first)
- bottom-up programming (look at details first, decide on data representation)
- swap back and forth between the last two
- test-driven development :
- create a function's API, docstring, tests *first*, as design spec.
- put in a "dummy" incorrect return value
- test it and see it fail tests
- write implementation, see it fail tests
Often as you get more experience,
you not to implement every function right away -
instead you set up their interfaces and
see how they fit together to solve the whole problem.
Here's an example to complete in class :
"""
factorial.py
Looking at functions, doctests, and test-driven deveopment.
Jim M | Oct 2013
"""
def factorial(n):
""" Return the factorial of n, i.e. n!, which is n * (n-1) * ... 2 * 1
>>> factorial(2)
2
>>> factorial(5)
120
"""
return 0 # This is the wrong result. Tests will fail.
def main():
print "-- factorial --"
n = input("What is n? ")
print "{}! is {}.".format(n, factorial(n))
if __name__ == "__main__":
import doctest
doctest.testmod()
main()
In class modify this so that factorial() passes its tests.
A slightly larger example : the cipher program we wrote two weeks ago.
- Here's something like our old code : cipher.py
- What parts should go into functions?
- For each function, you typically need to consider both
- the interface (its name, how you use it, inputs & outputs), and
- implementation (how to make it work)
- what are useful tests of what it does
- ... take a few minutes and think of what functions you'd define.
With this model of writing code, you can play around
with pieces from the command prompt without running
the main() function every time, like this :
$ python
>>> from cipher_functions import *
>>> encode_char('a', 3)
>>> encode_word('hello', 18)
When it's imported this way, the package __name__ is not "__main__",
and so the doctests and main() are not run.
However, after you make changes to the source file
(cipher_functions.py in this case) you should quit
the interactive shell and import it again.
(ipython has some tricks to get around this; google "ipython run" and "ipython relaod" to learn more.)
book example
- inputs?
- outputs?
- side effects?
- tests?
- docs?
default values in function arguments
def do_something(size, color="white"):
# code goes here
print "size, color = %i, %s" % (size, color)
do_something(3, "blue") # this is OK
do_something(4) # also OK; color defaults to "white"
do_something() # *not* OK; 0 args gives error
Args with defaults may only go on the *end* of the argument list ... otherwise the later ones (required) imply that the earlier ones are also required.
Note that this is a python-specific syntax.
multiple return values
def get_stuff():
return (1, "hey")
# And now use it
(a, b) = get_stuff()
# "Tuples" in python
other bells and whistles
Less used, and can be safely ignored on a first pass through python.
But for those who like the tricky corners...
def two_or_more_args(a, b, *c):
print "a = '" + str(a) + "'"
print "b = '" + str(b) + "'"
print "c = '" + str(c) + "'"
# and now call it.
two_or_more_args(3, "hey") # a=3, b="hey", c=[]
two_or_more_args(3, "hey", 4, "there") # a=3, b="hey", c=[4, "there"]
depending on time
... start "conditionals" (chapters 7 and 8):
value = input("What is the number? (1 to 10) ")
if value < 1 or value > 10 :
print "Oops: your number was too big or too small."
print "Your number is %i" % number
Discuss flowcharts.
Discuss comparison operators.
Coming: boolean variables.