SlideShare a Scribd company logo
<High School>
Computer Science:
Introduction to Python
Unit #2
(Covering CPython 3)
Spring 2017
License: CC BY-SA 4.0 (except images), by Jay Coskey 1
Unit #2
Software Engineering
2
Software Engineering
3
• Docstrings
• Lambdas, map, and filter
• Classes & Python special functions
• Caching implemented using class-level data
• Logging with the package "logging"
• Unit testing with the package "unittest"
• Debugging
Docstrings
• Q: Where do the help strings associated with these functions come from?
• A: From docstrings! These are comments inside triple quotes. Recall this example:
def get_pi_estimate(sample_count):
"""Use a Monte Carlo technique to approximate the area of the unit circle"""
• Docstrings can be attached to individual functions, classes, or modules.
• As shown above, docstrings for functions appear just under the first line of the definition.
• The first line of the docstring becomes the short description of the function.
• There are conventions for what goes into a docstring, but we won't worry about that now.
ipython: For a short description of any of these:
>>> ? function_name
For a longer description:
>>> help(function_name)
License: CC BY-SA 4.0 (except images), by Jay Coskey
4
Lambdas (a.k.a. "function expressions") (1)
• We've been defining functions like this
def square(x):
return x**2 # After the colon is a block (often indented) of statements
• We can instead define "lambda functions" (or just "lambdas") like this:
square = lambda x: x**2 # No "def" needed. After the colon is an expression; no return needed.
square(5) # Value is 25
• Lambdas can also be functions of multiple arguments:
area = lambda x, y: x * y
area(5, 4) # Value is 20
• As with all functions, lambdas can be stored in variables or passed as function arguments:
def replace(f, items):
return [f(x) for x in items]
square = lambda x: x**2
items = [1,2,3,4,5]
print(replace(square, items)) # Output: [1, 4, 9, 16, 25]
License: CC BY-SA 4.0 (except images), by Jay Coskey
5
Lambdas (a.k.a. "function expressions") (2)
• Lambdas are functions defined as expressions. They can be called "function expressions".
• They're also sometimes called "function literals".
• The term "lambda" comes from the word of Alonzo Church (1903-1995), who in the 1930s
used the Greek letter lambda (λ) to represent such functions in his writings on the lambda
calculus (within the field of mathematical logic), which in some ways is still used today.
• The Lisp programming language, which we mentioned on the first day of this course, was
basically a translation into software of Church's lambda calculus. So the lambda calculus
was basically the first written practical programming language.
License: CC BY-SA 4.0 (except images), by Jay Coskey 6
map and filter
• The function map applies a function to each item in a list.
So map(f, items) acts like the list comprehension [f(x) for x in items]
For example, print(map(square, [1,2,3,4,5]))
prints out [1,4,9,16,25].
print(map(lambda s: s[::-1], ['Hello', 'world']))
prints out ['olleH', dlrow']
• The function filter results only those items in a list for which the filter function returns True.
So filter(p, items) acts like [x for x in items if p(x)]
For example, print(filter(lambda x: x % 2 == 0, [1,2,3,4,5]))
prints out [2,4].
print(filter(lambda s: len(s) > 3, ['Hello','to','the','world']))
prints out ['Hello', 'world']
License: CC BY-SA 4.0 (except images), by Jay Coskey 7
p items
Classes
8
Software design: Components
• Suppose you're writing a program for a small game.
• It has game logic.
• It has player interaction.
• Probably some miscellaneous functionality.
• We can think of these as
• A Game Logic component,
• A Player Interaction component, and
• A Utility component.
• It's helpful to write the program in different sections,
where each section written corresponds to one of the
components being modeled.
License: CC BY-SA 4.0 (except images), by Jay Coskey
9
Game
Game Logic
Player
Interaction
Utility
Software design: Classes
• Object-oriented ("OO") software development became popular in the 1980s with the
programming language C++ (*). In short, the OO approach:
• Breaks a large system down into smaller components
• Takes the bits of software that modeled each of those components
• Assembles those bits of software into a program dealing with the original system.
• In OO software, the bits of software used are called "objects".
• Objects generally consist of data and the functions that operate on that data.
• The definitions of those objects are called classes.
• The objects themselves are sometimes called class instances.
License: CC BY-SA 4.0 (except images), by Jay Coskey
10
(*) But OO software goes back at least to 1965, when the Simula language first appeared.
Game System
Game Logic
Player
Interaction
Utility
Game Program
Game class Player class Util class
(a game that
people play)
(software that
people run)
Class example: BankAccount (use, not definition)
• Here is an example of how we might use a class called BankAccount.
• Note: We haven't defined the class yet. We'll do that soon.
if __name__ == '__main__':
ba = BankAccount(100.00)  This line creates and initializes the object.
ba.print_balance()  This line calls a function belonging to that object.
Output:
Balance=$100.00
License: CC BY-SA 4.0 (except images), by Jay Coskey
11
Note: It looks like a BankAccountobject stores one
piece of information — the account balance,
which is printed by print_balance.
"Let's play the
BankAccount game!"
said no one ever.
class BankAccount:
def __init__(self, balance=0):  This initializes the object, with a default balance of zero.
self.balance = balance  Here "balance" is the argument, while "self.balance" belongs to the object.
def print_balance(self):
print('Balance=${0:.2f}'.format(self.balance))
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
assert(amount <= self.balance) # Why is this line here?
self.balance -= amount
if __name__ == '__main__':
checking = BankAccount(100)  checking is an object of class BankAccount, or an instantiation of it.
checking.print_balance()  Does this remind you of string's format, lower, & upper functions?
savings = BankAccount()  savings is initialized without a balance, so it's set to the default of $0.
savings.deposit(250)
savings.print_balance()
Output:
Balance=$100.00
Balance=$250.00
Class example: BankAccount definition
License: CC BY-SA 4.0 (except images), by Jay Coskey
12
Data
Functions
The term "self" refers to
the current object.
Class example: Greeter (use, not definition)
if __name__ == '__main__':
greeter = Greeter('Hello', 'world')  This line creates and initializes the object.
greeter.greet()  This line calls a function belonging to that
Output:
Hello, world!
License: CC BY-SA 4.0 (except images), by Jay Coskey
13
Note: This shows how the Greeter class is used.
It looks like a Greeter object has two pieces of
information:
(1) the greeting, and (2) the thing being greeted.
We'll see shortly how its defined.
class Greeter:
def __init__(self, greeting='Hello', name ='world'):  This initializes the object with default args.
self.greeting = greeting  Here "greeting" is the argument, while "self.greeting" belongs to the object.
self.name = name  Same here: Value of the function argument is put into the object attribute
def greet(self):
print(self.greeting + ', ' + self.name + '!')
if __name__ == '__main__':
hw = Greeter('Hello', 'world')  hw is called a "Greeter object", or an instantiation of class Greeter.
sp = Greeter('Hola', 'amigos')
hw.greet()
sp.greet()
print('{0:s}, {1:s}!'.format(hw.greeting, sp.name))  You can access attributes directly
Output:
Hello, world! # hw.greet()
Hola, amigos! # sp.greet()
Hello, amigos! # print statement
Class example: Greeter (2)
License: CC BY-SA 4.0 (except images), by Jay Coskey
14
Data
Function
Helpful(?) example: Biological taxonomies
• Consider this abridged taxonomy of the animal kingdom
License: CC BY-SA 4.0 (except images), by Jay Coskey
15
DogCat Cow
Mammals
Animals
Deer Goat Pig
rover porkybessiescampers buck rambrandt
Object/Instance
attributes:
individual_name
height
weight
owner
Life
Class attributes:
species_name
is_domesticated
is_endangered
How are these taxons different than the ones below
(Cat, Cow, etc.)? No one says:
"I saw a mammal while driving today!"
These taxons represent categories rather than types
of individual animals. In other words, they aren't
ready to be instantiated in our minds as animals.
Class attributes vs. instance attributes (1)
• In HW #2, we saw that the contant data value pi (i.e., π) is math.pi.
• There are also oodles of math functions in the same module, such as:
• exp(x), sqrt(x), sin(x), cos(x), tan(x), log2(x), log10(x), factorial(x), gcd(a, b), etc.
• None of these require that you create an object of type math.
• Instead, these are attributes that belong to the class itself, rather than to
instances of the class. Let's call them class-level attributes, because they
belong to the class itself, not to individual instances of the class.
• The class Greeter that we defined earlier has these instance-level attributes:
• greeting and name (both data attributes of type string)
• greet (a function attribute)
License: CC BY-SA 4.0 (except images), by Jay Coskey
16
Class attributes vs. instance attributes (2)
•Examples:
License: CC BY-SA 4.0 (except images), by Jay Coskey
17
Attribute type Class-level
(i.e., one shared by all objects)
Instance-level
(i.e., one per object)
Data math.pi balance
greeting
name
Function math.cos print_balance
greet
(sometimes called methods)
Class example: Hello
(different than Greeting)
class Hello:
greeting = 'Hello'
def __init__(self, name='world'):
self.name = name
def greet(self):
print(Hello.greeting + ', ' + self.name + '!')
If __name__ == '__main__':
hw = Hello('world')  hw is a "Hello object", or an instantiation of class Hello.
sp = Hello('amigos')
hw.greet()
sp.greet()
print('Greeting={0:s}'.format(Hello.greeting))  The class-level greeting is Hello.
print('Spanish name={0:s}'.format(sp.name))  This particular instance-level name is "amigos".
Output:
Hello, world!
Hello, amigos!
Greeting=Hello
Spanish name=amigos
License: CC BY-SA 4.0 (except images), by Jay Coskey
18
Instance
data
Class
data
• "greeting" belongs to the class itself, not each object.
• Everybody gets the same greeting.
• "name" is class instance data.
• Every object greets someone different.
function
Q: Can class data point to a specific object? A: Yes
class Human:
tallest = None # Equalling None is like not having a value
def get_tallest():
return Human.tallest
def __init__(self, name, height):
self.name = name
self.height = height
if Human.tallest == None or height > Human.tallest.height:
Human.tallest = self
def greet(self):
print('Hi, {0:s}'.format(self.name))
if __name__ == '__main__':
amy = Human('Amy', 5.5)
bob = Human('Bob', 6.0)
cindy = Human('Cindy', 7.5)
print('Tallest: {0:s}'.format(Human.get_tallest().name))
Output:
Tallest: Cindy
License: CC BY-SA 4.0 (except images), by Jay Coskey
19
Class example: fibonacci revisited
Recall that we saw this recursive function fibonacci earlier. We can make it a function attribute of a class.
class Fibonacci:
def __init__(self):
pass
def fibonacci(self, n):
assert( type(n) == int and n >= 0 )
if n == 0 or n == 1:
return 1
else:
return fibonacci(n – 2) + fibonacci(n – 1)
if __name__ == '__main__':
f = Fibonacci( )
for n in range(0, 32):
print('Fibonacci #{0:d} = {1:d}'.format(n, f.fibonacci (n)))
Output:
Fibonacci #0 = 1
Fibonacci #1 = 1
Fibonacci #2 = 2
Fibonacci #3 = 3
etc.
License: CC BY-SA 4.0 (except images), by Jay Coskey
20
No instance data, so nothing
to initialize, so __init__ isn't needed. This is an
"instance function".
That just mean that each object created
gets its own copy.
So what?
Is there any benefit to putting this function in a class?
Not yet.
But wait, there's more!....
Class example: Cached fibonacci function
We can save return values as we compute them, and reuse them. This saves on computation time, and speeds up our program.
class CFibonacci:
computed_values = dict()
def __init__(self):
pass
def fibonacci(self, n):
assert( type(n) == int and n >= 0 )
if n in CFibonacci.computed_values.keys():
return CFibonacci.computed_values[n]
elif n == 0 or n == 1:
return 1
else:
val = self.fibonacci(n – 2) + self.fibonacci(n – 1)
CFibonacci.computed_values[n] = val
return val
if __name__ == '__main__':
cf = CFibonacci()
for n in range(0, 5000):
print(cf.fibonacci(n))
Output:
Same as before, but much faster.
License: CC BY-SA 4.0 (except images), by Jay Coskey
21
Class-level data
(no use of 'self')
Instance-level function
(self is used)
An instance-level
function can use
class data
This range is far more than the
previous version could have handled,
and it's done in a fraction of a second.
Python operators and special functions (a partial list)
(See Python 3 docs at https://docs.python.org/3.6/library/operator.html)
Operation Special object-level function (a.k.a. "dunder" function)
abs(), +, -
*, /, //
**, %, @, divmod
__abs__, __pos__, __add__, __concat__, __neg__, __sub__,
__mul__, __truediv__, __floordiv__,
__pow__, __mod__, __matmul__, __divmod__
+, -, *, /, **, etc. __radd__, __rsub__, __rmul__, __rtruediv__, __rpow__, etc.
+=, -=, *=, /=, etc. __iadd__, __isub__, __imul__, __itruediv__, etc.
<, <=, ==, !=, >, >= __lt__, __le__, __eq__, __ne__, __gt__, __ge__ (__cmp__ is not in 3.5)
See functools.total_ordering
and, or, not Logical: __not__ (Also, not_). No special functions for the first two: see PEP 335.
&, |, ^, ~ Bitwise: __and__, __or__, __xor__, __invert__ (Also and_, or_)
<<, >> __lshift__, __rlshift, __rshift__, __rrshift__
bool(obj)
len(obj)
If __bool__ is not defined, but __len__ is, then an object is true if len != 0.
If neither __bool__ nor __len__ is defined, then all instances are considered true.
self[key] __getitem__, __setitem__, __delitem__ (Both indexing and slicing)
for word in words __contains__, __iter__, __next__
is_, is_not Test object type
22
License: CC BY-SA 4.0 (except images), by Jay Coskey
Python operators and special functions (2)
Operation Special function (a.k.a. "dunder" function)
abs(), +, -
*, /, //
**, %, @, divmod
__abs__, __pos__, __add__, __concat__, __neg__, __sub__,
__mul__, __truediv__, __floordiv__,
__pow__, __mod__, __matmul__, __divmod__
Etc. Etc.
23
License: CC BY-SA 4.0 (except images), by Jay Coskey
• Suppose we define our own class, Vector2d:
class Vector2d:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
new_x = self.x + other.x
new_y = self.y + other.y
return Vector2d(new_x, new_y)
With Vector2d defined like this, whenever Python sees:
• a Vector2d called a
• in the expression a + b
it converts that expression into a.__add__(b).
Otherwise, when it sees:
• a Vector2d called b
• in the expression a + b
it converts that expression into b.__radd__(a).
Lastly, when it sees:
• a Vector2d called a
• in the expression a += b
it converts that expression into a.__iadd__(b).
• Which of the operators we just saw would be convenient to define for this new class?
r => "right-hand side"
i => "increment"
IndividualCheckingAcct
Class hierarchies (not a focus of this course)
• Just like the tree biological taxonomy forms a hierarchy, you can also form a
tree of classes modeling some sort of hierarchy.
• For example, you can define a class called Mammal, and then define another
class called Dog, and tell Python that class Dog inherits all the attributes in
class Mammal.
• We won't be focusing on class hierarchies or class inheritance in this course.
• Sorry, rover!
License: CC BY-SA 4.0 (except images), by Jay Coskey
24
Dog
Mammals IndividualBankAcct CommercialBankAcct CheckingAcct SavingsAcct
BankAcct
class IndividualCheckingAcct( IndividualBankAcct, CheckingAcct ):
Logging
License: CC BY-SA 4.0 (except images), by Jay Coskey 25
Logging (1)
• Print statements can be useful for debugging, but there is room for improvement. There could be a way to:
• save the output to files, since a text window isn't always available (e.g., window-based apps)
• associate each message with a level of importance (DEBUG, INFO, WARNING, ERROR, CRITICAL)
• log only those messages that are important enough within the current "context", i.e., that meet a given "threshold"
• always log the time that the message was logged (and perhaps the host, for a server).
• Enter the logging package.
import logging
logging.basicConfig(level=logging.WARNING) # Only log msgs @ level WARNING or higher
logger = logging.getLogger(__name__)
logger.info('Starting to read from database') # Not logged, since INFO < WARNING
logger.error('Could not read from database') # Logged, since ERROR > WARNING
Output:
ERROR:__main__:Could not read from database
1. Within the software industry, a standard has emerged for web servers: See Common_Log_Format on Wikipedia.
2. If your logged messages are aggregated across timezones, then you should use Coordinated Universal Time (UTC).
3. Protip: Never use the filename logging.py: this confuses the logging infrastructure.
License: CC BY-SA 4.0 (except images), by Jay Coskey
26
Logging (2)
• We just saw that the logging system can be configured with the statement that sets the logging "threshold".
logging.basicConfig(level=logging.WARNING)
• Here are some of the many other things that can be done with a basicConfig statement.
• Specify a filename, with the filename argument (By default, output is printed on the "console".)
• Specify the output format, with the format argument (This can include the user name, host name, etc.)
• Specify the date/time format, with the datefmt argument (default format: 2010-09-06 22:38:15,292)
In case it wasn't clear from the previous slide:
• The statement logging.debug(msg) logs the given message at level DEBUG.
• The statement logging.info(msg) logs the given message at level INFO.
• The statement logging.warning(msg) logs the given message at level WARNING.
• The statement logging.error(msg) logs the given message at level ERROR.
• The statement logging.critical(msg) logs the given message at level CRITICAL.
Note: The logging framework is designed to be thread-safe by default.
Note: Logging systems have been around for ages, including syslog (since the '80s) and Java's log4j (since 2001).
License: CC BY-SA 4.0 (except images), by Jay Coskey
27
Unit testing
License: CC BY-SA 4.0 (except images), by Jay Coskey 28
Unit testing: Introduction
• Q: How do you know if your code is working right?
• A: You don't.
• Q: What types of testing can increase your
confidence that it's working right?
• A: There are many. Unit testing is an important one.
• Unit testing—Apply tests to small units of code.
• Integration testing—Check that the different pieces of
code are working together as they're supposed to.
• Regression testing—Check to see that the bugs that have
been seen in the past haven't returned.
• Acceptance testing—Check that the customer's
requirements for sign-off are being met.
• And many, many more.
License: CC BY-SA 4.0 (except images), by Jay Coskey
29
Unit testing: TestCase using assertEqual (1)
• Suppose your program has as many lines of testing code as there are of "production" code.
Keeping them separate helps simplify the code. (See "Separation of concerns" on Wikipedia.)
import unittest
class MyClassTest(unittest.TestCase):
def test_increment(self):
x = 0
x += 1
self.assertEqual(x, 1)
def test_decrement(self):
x = 0
x -= 1
self.assertEqual(x, -1)
if __name__ == '__main__':
unittest.main()
License: CC BY-SA 4.0 (except images), by Jay Coskey 30
This main function discovers and runs all the tests it finds.
Details on how this works can be found at
https://docs.python.org/3/library/unittest.html#unittest.main
Important: Each test name starts with 'test_'
Output:
..
----------------------------------------
Ran 2 tests in 0.000s
OK
Unit testing: TestCase using assertEqual (2)
• Here is a similar unit testing example that uses the BankAccount class we saw earlier.
import unittest
class BankAccountTest(unittest.TestCase):
<...functions that we saw earlier...>
def test_deposit(self):
TEST_AMOUNT = 100
acct = BankAccount(0)
acct.deposit(TEST_AMOUNT)
self.assertEqual(acct.get_balance(), TEST_AMOUNT)
<...more test functions...>
if __name__ == '__main__':
unittest.main()
License: CC BY-SA 4.0 (except images), by Jay Coskey 31
Oops: class BankAccount doesn't have a
get_balance function. We'll need to add it.
Unit testing: expectedFailure
• Unittest has support for many, many types of assertions, including "expected failures".
import unittest
class BankAccountTest(unittest.TestCase):
<...functions we saw earlier...>
@unittest.expectedFailure
def test_withdraw(self):
TEST_AMOUNT = 100
acct = BankAccount(0)
acct.withdraw(TEST_AMOUNT) # Ack! Insufficient funds!
self.assertEqual(acct.get_balance(), -TEST_AMOUNT)
<...more test functions...>
if __name__ == '__main__':
unittest.main()
License: CC BY-SA 4.0 (except images), by Jay Coskey 32
This demonstrates the use of Python
decorators, which we haven't seen before.
With this, the assertion failure within this
function actually causes the test to succeed.
Unit testing: setUp and tearDown
• Lastly, the unittest package has support for organized groups of tests, or "test suites".
• Suppose you're testing a weather model that requires a lot of effort to set up.
• Then you'd want to:
• Set up a weather model before running a the test suite.
• Run each test that uses this weather model.
• Dispose of ("tear down") the weather model.
License: CC BY-SA 4.0 (except images), by Jay Coskey 33
import unittest
class MyClassTest(unittest.TestCase):
def setUp(self):
create_weather_model()
def tearDown(self):
delete_weather_model()
def weather_test_1(self):
pass # TODO: Add test code here
def weather_test_2(self):
pass # TODO: Add test code here
• setUp is run first.
• Next, tests #1 and #2 are run.
• tearDown is run last
Debugging
“As soon as we started programming, we found to our surprise that it wasn’t as easy to get
programs right as we had thought. We had to discover debugging. I can remember the exact
instant when I realized that a large part of my life from then on was going to be spent in finding
mistakes in my own programs.”
—Maurice Wilkes (1913-2010), 1949
"Program testing can be used to show the presence of bugs, but never to show their absence!"
—Edsger W. Dijkstra (1930-2002), 1970
“Everyone knows that debugging is twice as hard as writing a program in the first place. So if
you're as cleverly as you can be when you write it, how will you ever debug it?”
— Brian W. Kernighan (b. 1942), 1974
License: CC BY-SA 4.0 (except images), by Jay Coskey 34
Debugging: An overview
• When your program doesn't work the way you expect (or hope), it can be difficult to tell what's doing wrong.
• The first 18 chapters of Think Python v2 each have a section on Debugging, plus an Appendix on debugging.
• The debugging mindset. Some bugs are easy. Some require that you become a detective.
• Top 10 types of bugs. There are some common mistakes that are made that you can watch out for.
• Bug reports. There are standard ways to report bugs, in order to help teams debug effectively.
• Prevent recurrence. Sever bugs warrant a process or activity to prevent recurrence.
• Custom debugging code. Debugging is something that's often worth investing in.
• Design for Testability and Automation. Make it easier and cheaper to find bugs in the future.
License: CC BY-SA 4.0 (except images), by Jay Coskey 35
Debugging: The debugging mindset
Bugs are an inevitable byproduct of the optimal pace of development.
• It might be possible to avoid almost all bugs by proceeding very cautiously, or even by
mathematically providing the correctness of the algorithms involved, but development
would be too slow for commercial purposes.
Some bugs are "shallow", and just require you to fix the error reported by the
Python interpreter, or correct a typo.
Some other bugs are more subtle, and require that you become a detective, and
• imagine what are the most likely things that could have gone wrong
• question your assumptions about how your program works
• get help from others on your team, or other teams, or from the internet.
License: CC BY-SA 4.0 (except images), by Jay Coskey 36
Debugging: Top 10 types of bugs
Keeping possibilities like this in mind can help you find the bug more quickly.
• Indentation. Mixed spaces and tabs that confused the Python interpreter?
• Missing import. Tried a new function but didn't import the required module?
• No return value. Trying to use the return value of a function that doesn't return one.
• Off-by-one error. Did you get the right the begin and end loop bounds? (See § 8.11 of TP)
• Varying constants. Was a "constant" defined differently in two different places?
• Missing dependencies. Did you neglect to set up the weather model before using it?
• Version incompatibility. You're calling version 3.6 of a function, but you need to call v3.8.
• Recursion depth exceeded. Did a recursive function fail to stop calling itself?
• Didn't remove temporary code. Forgot to remove something temporary?
• Undertested error-handling code.
• Suppose there are checks to handle invalid user input.
• Such code is often not tested as well as the "mainstream" part of the program.
License: CC BY-SA 4.0 (except images), by Jay Coskey 37
Debugging: Bug reports
Reporting a bug to others (for resolution, analysis, or archiving)? Some info to include:
• Reproduction ("repro") steps.
• Can the bug be reproduced?
• If so, how? (See "Platform".) If it's too difficult to reproduce, is there a "minimal test
case" that can trigger the same type of bug? Maybe a short program?
• Description.
• What (program) behavior are you expecting? A shared expectation, or disagreement?
• What behavior was actually observed? How do expectation and observation differ?
• Do the details of the bug change from run to run (e.g., timing issues)?
• Platform.
• What operating system was being used when the bug was found?
• What version of Python? "python --version" or "import sys; print(sys.version)"
• What program was run? How was it run? With what arguments?
• If it was found through a web browser, which browser type and version?
License: CC BY-SA 4.0 (except images), by Jay Coskey 38
Debugging: Prevent recurrence
• "Low-hanging fruit".
• Suppose a function you use was updated from taking two arguments to taking three.
• When one call of a function uncovers the error, go ahead and fix all the calls.
• The systematic "Root Cause Resolution" (RCR) approach.
• If the bug was serious enough, you can take steps to prevent a recurrence.
• Gather info on the details. The bigger the program/system, the more effort this takes.
• Understand people's actions, but make it about the system, not the people. ("Ego free")
• List what steps could be taken to avoid recurrence.
• Follow through and see that those steps are carried out.
License: CC BY-SA 4.0 (except images), by Jay Coskey 39
Debugging: Custom debugging code
• Companies often invest a great deal of effort in custom software to help them fix bugs in
their own code.
• Below is a function that prints out the important attributes of a given object. It's a fancier
version of the print_attributes function from Section 17.10 of ThinkPython, 2nd ed.
def dump_attributes(obj):
assert(hasattr(obj, '__class__'))
print('Dump of instance of {0:s}'.format(repr(obj.__class__)))
for attr_name in dir(obj):
if hasattr(obj, attr_name) and '__' not in attr_name:
attr_val = str(getattr(obj, attr_name))
attr_val = '<method>' if 'method' in attr_val else attr_val
print(' obj.{0:s} = {1:s}'.format(attr_name, attr_val))
• Code like this that prints out the details of object is sometimes called "pretty printing".
License: CC BY-SA 4.0 (except images), by Jay Coskey 40
Debugging: Design for Testability and Automation
• Testability = Controllability + Visibility
• The Lunar Lander program we saw earlier in the course had options to set the initial
values of fuel, altitude, and velocity. This made it easy to test the effect of specific burn
rates on specific states of the Lunar Lander.
• The BankAccount class we saw earlier started without a get_balance function, which we
had to add in order to perform a specific test we had in mind.
• When breaking up functionality into classes and functions, choose wisely.
• Classes that are too "large" allow for too much "hidden" interaction within the class.
• Classes that are too "fine-grained" require excessive "bookkeeping".
• Automation of testing.
• Testing is expensive. It makes sense to invest effort into automating tests over time.
• unittest is one example of support for improving test automation over time.
License: CC BY-SA 4.0 (except images), by Jay Coskey 41

More Related Content

What's hot

Feel of Kotlin (Berlin JUG 16 Apr 2015)
Feel of Kotlin (Berlin JUG 16 Apr 2015)Feel of Kotlin (Berlin JUG 16 Apr 2015)
Feel of Kotlin (Berlin JUG 16 Apr 2015)
intelliyole
 

What's hot (20)

Coding in Kotlin with Arrow NIDC 2018
Coding in Kotlin with Arrow NIDC 2018Coding in Kotlin with Arrow NIDC 2018
Coding in Kotlin with Arrow NIDC 2018
 
Feel of Kotlin (Berlin JUG 16 Apr 2015)
Feel of Kotlin (Berlin JUG 16 Apr 2015)Feel of Kotlin (Berlin JUG 16 Apr 2015)
Feel of Kotlin (Berlin JUG 16 Apr 2015)
 
Introduction to Objective - C
Introduction to Objective - CIntroduction to Objective - C
Introduction to Objective - C
 
Introduction to Python and TensorFlow
Introduction to Python and TensorFlowIntroduction to Python and TensorFlow
Introduction to Python and TensorFlow
 
Hack Like It's 2013 (The Workshop)
Hack Like It's 2013 (The Workshop)Hack Like It's 2013 (The Workshop)
Hack Like It's 2013 (The Workshop)
 
Pythonpresent
PythonpresentPythonpresent
Pythonpresent
 
Basics of Python programming (part 2)
Basics of Python programming (part 2)Basics of Python programming (part 2)
Basics of Python programming (part 2)
 
Declarative Thinking, Declarative Practice
Declarative Thinking, Declarative PracticeDeclarative Thinking, Declarative Practice
Declarative Thinking, Declarative Practice
 
Introduction to TensorFlow 2.0
Introduction to TensorFlow 2.0Introduction to TensorFlow 2.0
Introduction to TensorFlow 2.0
 
Creating Objects in Python
Creating Objects in PythonCreating Objects in Python
Creating Objects in Python
 
Learn python in 20 minutes
Learn python in 20 minutesLearn python in 20 minutes
Learn python in 20 minutes
 
Learn 90% of Python in 90 Minutes
Learn 90% of Python in 90 MinutesLearn 90% of Python in 90 Minutes
Learn 90% of Python in 90 Minutes
 
Dynamic Python
Dynamic PythonDynamic Python
Dynamic Python
 
Python in 30 minutes!
Python in 30 minutes!Python in 30 minutes!
Python in 30 minutes!
 
Scala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To KnowScala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To Know
 
Python: Migrating from Procedural to Object-Oriented Programming
Python: Migrating from Procedural to Object-Oriented ProgrammingPython: Migrating from Procedural to Object-Oriented Programming
Python: Migrating from Procedural to Object-Oriented Programming
 
07. Java Array, Set and Maps
07.  Java Array, Set and Maps07.  Java Array, Set and Maps
07. Java Array, Set and Maps
 
Peyton jones-2009-fun with-type_functions-slide
Peyton jones-2009-fun with-type_functions-slidePeyton jones-2009-fun with-type_functions-slide
Peyton jones-2009-fun with-type_functions-slide
 
Porting to Python 3
Porting to Python 3Porting to Python 3
Porting to Python 3
 
Introduction to Functional Programming
Introduction to Functional ProgrammingIntroduction to Functional Programming
Introduction to Functional Programming
 

Similar to Intro to Python (High School) Unit #2

PYTHON -Chapter 2 - Functions, Exception, Modules and Files -MAULIK BOR...
PYTHON -Chapter 2 - Functions,   Exception, Modules  and    Files -MAULIK BOR...PYTHON -Chapter 2 - Functions,   Exception, Modules  and    Files -MAULIK BOR...
PYTHON -Chapter 2 - Functions, Exception, Modules and Files -MAULIK BOR...
Maulik Borsaniya
 
Python Interview Questions | Python Interview Questions And Answers | Python ...
Python Interview Questions | Python Interview Questions And Answers | Python ...Python Interview Questions | Python Interview Questions And Answers | Python ...
Python Interview Questions | Python Interview Questions And Answers | Python ...
Simplilearn
 
Write better python code with these 10 tricks | by yong cui, ph.d. | aug, 202...
Write better python code with these 10 tricks | by yong cui, ph.d. | aug, 202...Write better python code with these 10 tricks | by yong cui, ph.d. | aug, 202...
Write better python code with these 10 tricks | by yong cui, ph.d. | aug, 202...
amit kuraria
 

Similar to Intro to Python (High School) Unit #2 (20)

lecture4.ppt
lecture4.pptlecture4.ppt
lecture4.ppt
 
PYTHON -Chapter 2 - Functions, Exception, Modules and Files -MAULIK BOR...
PYTHON -Chapter 2 - Functions,   Exception, Modules  and    Files -MAULIK BOR...PYTHON -Chapter 2 - Functions,   Exception, Modules  and    Files -MAULIK BOR...
PYTHON -Chapter 2 - Functions, Exception, Modules and Files -MAULIK BOR...
 
Class 26: Objectifying Objects
Class 26: Objectifying ObjectsClass 26: Objectifying Objects
Class 26: Objectifying Objects
 
C++ Boot Camp Part 2
C++ Boot Camp Part 2C++ Boot Camp Part 2
C++ Boot Camp Part 2
 
These questions will be a bit advanced level 2
These questions will be a bit advanced level 2These questions will be a bit advanced level 2
These questions will be a bit advanced level 2
 
An Introduction : Python
An Introduction : PythonAn Introduction : Python
An Introduction : Python
 
Lecture 4. mte 407
Lecture 4. mte 407Lecture 4. mte 407
Lecture 4. mte 407
 
Python advance
Python advancePython advance
Python advance
 
Oop concepts in python
Oop concepts in pythonOop concepts in python
Oop concepts in python
 
Module IV_updated(old).pdf
Module IV_updated(old).pdfModule IV_updated(old).pdf
Module IV_updated(old).pdf
 
C++ Interview Question And Answer
C++ Interview Question And AnswerC++ Interview Question And Answer
C++ Interview Question And Answer
 
C++ questions And Answer
C++ questions And AnswerC++ questions And Answer
C++ questions And Answer
 
Introduction to Python for Plone developers
Introduction to Python for Plone developersIntroduction to Python for Plone developers
Introduction to Python for Plone developers
 
ch 2. Python module
ch 2. Python module ch 2. Python module
ch 2. Python module
 
Python Interview Questions | Python Interview Questions And Answers | Python ...
Python Interview Questions | Python Interview Questions And Answers | Python ...Python Interview Questions | Python Interview Questions And Answers | Python ...
Python Interview Questions | Python Interview Questions And Answers | Python ...
 
COMP111-Week-1_138439.pptx
COMP111-Week-1_138439.pptxCOMP111-Week-1_138439.pptx
COMP111-Week-1_138439.pptx
 
Chap 3 Python Object Oriented Programming - Copy.ppt
Chap 3 Python Object Oriented Programming - Copy.pptChap 3 Python Object Oriented Programming - Copy.ppt
Chap 3 Python Object Oriented Programming - Copy.ppt
 
Write better python code with these 10 tricks | by yong cui, ph.d. | aug, 202...
Write better python code with these 10 tricks | by yong cui, ph.d. | aug, 202...Write better python code with these 10 tricks | by yong cui, ph.d. | aug, 202...
Write better python code with these 10 tricks | by yong cui, ph.d. | aug, 202...
 
C++ classes
C++ classesC++ classes
C++ classes
 
Getting started with Clojure
Getting started with ClojureGetting started with Clojure
Getting started with Clojure
 

More from Jay Coskey

More from Jay Coskey (6)

Graph Database Query Languages
Graph Database Query LanguagesGraph Database Query Languages
Graph Database Query Languages
 
A Cosmic Hunt In The Berber Sky: An Introduction to Phylogenetic Inference
A Cosmic Hunt In The Berber Sky: An Introduction to Phylogenetic InferenceA Cosmic Hunt In The Berber Sky: An Introduction to Phylogenetic Inference
A Cosmic Hunt In The Berber Sky: An Introduction to Phylogenetic Inference
 
Software Modeling of Contracts in Games and Finance, Part 1: 2018-01-10
Software Modeling of Contracts in Games and Finance, Part 1: 2018-01-10Software Modeling of Contracts in Games and Finance, Part 1: 2018-01-10
Software Modeling of Contracts in Games and Finance, Part 1: 2018-01-10
 
Intro to Python (High School) Unit #3
Intro to Python (High School) Unit #3Intro to Python (High School) Unit #3
Intro to Python (High School) Unit #3
 
Intro to Python (High School) Unit #1
Intro to Python (High School) Unit #1Intro to Python (High School) Unit #1
Intro to Python (High School) Unit #1
 
Zippers: Derivatives of Regular Types
Zippers: Derivatives of Regular TypesZippers: Derivatives of Regular Types
Zippers: Derivatives of Regular Types
 

Recently uploaded

Recently uploaded (20)

Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
 
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
 
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
 
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
 
AI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in Michelangelo
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
Breaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdfBreaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdf
 
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoamOpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
 
Designing for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web ServicesDesigning for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web Services
 

Intro to Python (High School) Unit #2

  • 1. <High School> Computer Science: Introduction to Python Unit #2 (Covering CPython 3) Spring 2017 License: CC BY-SA 4.0 (except images), by Jay Coskey 1
  • 3. Software Engineering 3 • Docstrings • Lambdas, map, and filter • Classes & Python special functions • Caching implemented using class-level data • Logging with the package "logging" • Unit testing with the package "unittest" • Debugging
  • 4. Docstrings • Q: Where do the help strings associated with these functions come from? • A: From docstrings! These are comments inside triple quotes. Recall this example: def get_pi_estimate(sample_count): """Use a Monte Carlo technique to approximate the area of the unit circle""" • Docstrings can be attached to individual functions, classes, or modules. • As shown above, docstrings for functions appear just under the first line of the definition. • The first line of the docstring becomes the short description of the function. • There are conventions for what goes into a docstring, but we won't worry about that now. ipython: For a short description of any of these: >>> ? function_name For a longer description: >>> help(function_name) License: CC BY-SA 4.0 (except images), by Jay Coskey 4
  • 5. Lambdas (a.k.a. "function expressions") (1) • We've been defining functions like this def square(x): return x**2 # After the colon is a block (often indented) of statements • We can instead define "lambda functions" (or just "lambdas") like this: square = lambda x: x**2 # No "def" needed. After the colon is an expression; no return needed. square(5) # Value is 25 • Lambdas can also be functions of multiple arguments: area = lambda x, y: x * y area(5, 4) # Value is 20 • As with all functions, lambdas can be stored in variables or passed as function arguments: def replace(f, items): return [f(x) for x in items] square = lambda x: x**2 items = [1,2,3,4,5] print(replace(square, items)) # Output: [1, 4, 9, 16, 25] License: CC BY-SA 4.0 (except images), by Jay Coskey 5
  • 6. Lambdas (a.k.a. "function expressions") (2) • Lambdas are functions defined as expressions. They can be called "function expressions". • They're also sometimes called "function literals". • The term "lambda" comes from the word of Alonzo Church (1903-1995), who in the 1930s used the Greek letter lambda (λ) to represent such functions in his writings on the lambda calculus (within the field of mathematical logic), which in some ways is still used today. • The Lisp programming language, which we mentioned on the first day of this course, was basically a translation into software of Church's lambda calculus. So the lambda calculus was basically the first written practical programming language. License: CC BY-SA 4.0 (except images), by Jay Coskey 6
  • 7. map and filter • The function map applies a function to each item in a list. So map(f, items) acts like the list comprehension [f(x) for x in items] For example, print(map(square, [1,2,3,4,5])) prints out [1,4,9,16,25]. print(map(lambda s: s[::-1], ['Hello', 'world'])) prints out ['olleH', dlrow'] • The function filter results only those items in a list for which the filter function returns True. So filter(p, items) acts like [x for x in items if p(x)] For example, print(filter(lambda x: x % 2 == 0, [1,2,3,4,5])) prints out [2,4]. print(filter(lambda s: len(s) > 3, ['Hello','to','the','world'])) prints out ['Hello', 'world'] License: CC BY-SA 4.0 (except images), by Jay Coskey 7 p items
  • 9. Software design: Components • Suppose you're writing a program for a small game. • It has game logic. • It has player interaction. • Probably some miscellaneous functionality. • We can think of these as • A Game Logic component, • A Player Interaction component, and • A Utility component. • It's helpful to write the program in different sections, where each section written corresponds to one of the components being modeled. License: CC BY-SA 4.0 (except images), by Jay Coskey 9 Game Game Logic Player Interaction Utility
  • 10. Software design: Classes • Object-oriented ("OO") software development became popular in the 1980s with the programming language C++ (*). In short, the OO approach: • Breaks a large system down into smaller components • Takes the bits of software that modeled each of those components • Assembles those bits of software into a program dealing with the original system. • In OO software, the bits of software used are called "objects". • Objects generally consist of data and the functions that operate on that data. • The definitions of those objects are called classes. • The objects themselves are sometimes called class instances. License: CC BY-SA 4.0 (except images), by Jay Coskey 10 (*) But OO software goes back at least to 1965, when the Simula language first appeared. Game System Game Logic Player Interaction Utility Game Program Game class Player class Util class (a game that people play) (software that people run)
  • 11. Class example: BankAccount (use, not definition) • Here is an example of how we might use a class called BankAccount. • Note: We haven't defined the class yet. We'll do that soon. if __name__ == '__main__': ba = BankAccount(100.00)  This line creates and initializes the object. ba.print_balance()  This line calls a function belonging to that object. Output: Balance=$100.00 License: CC BY-SA 4.0 (except images), by Jay Coskey 11 Note: It looks like a BankAccountobject stores one piece of information — the account balance, which is printed by print_balance. "Let's play the BankAccount game!" said no one ever.
  • 12. class BankAccount: def __init__(self, balance=0):  This initializes the object, with a default balance of zero. self.balance = balance  Here "balance" is the argument, while "self.balance" belongs to the object. def print_balance(self): print('Balance=${0:.2f}'.format(self.balance)) def deposit(self, amount): self.balance += amount def withdraw(self, amount): assert(amount <= self.balance) # Why is this line here? self.balance -= amount if __name__ == '__main__': checking = BankAccount(100)  checking is an object of class BankAccount, or an instantiation of it. checking.print_balance()  Does this remind you of string's format, lower, & upper functions? savings = BankAccount()  savings is initialized without a balance, so it's set to the default of $0. savings.deposit(250) savings.print_balance() Output: Balance=$100.00 Balance=$250.00 Class example: BankAccount definition License: CC BY-SA 4.0 (except images), by Jay Coskey 12 Data Functions The term "self" refers to the current object.
  • 13. Class example: Greeter (use, not definition) if __name__ == '__main__': greeter = Greeter('Hello', 'world')  This line creates and initializes the object. greeter.greet()  This line calls a function belonging to that Output: Hello, world! License: CC BY-SA 4.0 (except images), by Jay Coskey 13 Note: This shows how the Greeter class is used. It looks like a Greeter object has two pieces of information: (1) the greeting, and (2) the thing being greeted. We'll see shortly how its defined.
  • 14. class Greeter: def __init__(self, greeting='Hello', name ='world'):  This initializes the object with default args. self.greeting = greeting  Here "greeting" is the argument, while "self.greeting" belongs to the object. self.name = name  Same here: Value of the function argument is put into the object attribute def greet(self): print(self.greeting + ', ' + self.name + '!') if __name__ == '__main__': hw = Greeter('Hello', 'world')  hw is called a "Greeter object", or an instantiation of class Greeter. sp = Greeter('Hola', 'amigos') hw.greet() sp.greet() print('{0:s}, {1:s}!'.format(hw.greeting, sp.name))  You can access attributes directly Output: Hello, world! # hw.greet() Hola, amigos! # sp.greet() Hello, amigos! # print statement Class example: Greeter (2) License: CC BY-SA 4.0 (except images), by Jay Coskey 14 Data Function
  • 15. Helpful(?) example: Biological taxonomies • Consider this abridged taxonomy of the animal kingdom License: CC BY-SA 4.0 (except images), by Jay Coskey 15 DogCat Cow Mammals Animals Deer Goat Pig rover porkybessiescampers buck rambrandt Object/Instance attributes: individual_name height weight owner Life Class attributes: species_name is_domesticated is_endangered How are these taxons different than the ones below (Cat, Cow, etc.)? No one says: "I saw a mammal while driving today!" These taxons represent categories rather than types of individual animals. In other words, they aren't ready to be instantiated in our minds as animals.
  • 16. Class attributes vs. instance attributes (1) • In HW #2, we saw that the contant data value pi (i.e., π) is math.pi. • There are also oodles of math functions in the same module, such as: • exp(x), sqrt(x), sin(x), cos(x), tan(x), log2(x), log10(x), factorial(x), gcd(a, b), etc. • None of these require that you create an object of type math. • Instead, these are attributes that belong to the class itself, rather than to instances of the class. Let's call them class-level attributes, because they belong to the class itself, not to individual instances of the class. • The class Greeter that we defined earlier has these instance-level attributes: • greeting and name (both data attributes of type string) • greet (a function attribute) License: CC BY-SA 4.0 (except images), by Jay Coskey 16
  • 17. Class attributes vs. instance attributes (2) •Examples: License: CC BY-SA 4.0 (except images), by Jay Coskey 17 Attribute type Class-level (i.e., one shared by all objects) Instance-level (i.e., one per object) Data math.pi balance greeting name Function math.cos print_balance greet (sometimes called methods)
  • 18. Class example: Hello (different than Greeting) class Hello: greeting = 'Hello' def __init__(self, name='world'): self.name = name def greet(self): print(Hello.greeting + ', ' + self.name + '!') If __name__ == '__main__': hw = Hello('world')  hw is a "Hello object", or an instantiation of class Hello. sp = Hello('amigos') hw.greet() sp.greet() print('Greeting={0:s}'.format(Hello.greeting))  The class-level greeting is Hello. print('Spanish name={0:s}'.format(sp.name))  This particular instance-level name is "amigos". Output: Hello, world! Hello, amigos! Greeting=Hello Spanish name=amigos License: CC BY-SA 4.0 (except images), by Jay Coskey 18 Instance data Class data • "greeting" belongs to the class itself, not each object. • Everybody gets the same greeting. • "name" is class instance data. • Every object greets someone different. function
  • 19. Q: Can class data point to a specific object? A: Yes class Human: tallest = None # Equalling None is like not having a value def get_tallest(): return Human.tallest def __init__(self, name, height): self.name = name self.height = height if Human.tallest == None or height > Human.tallest.height: Human.tallest = self def greet(self): print('Hi, {0:s}'.format(self.name)) if __name__ == '__main__': amy = Human('Amy', 5.5) bob = Human('Bob', 6.0) cindy = Human('Cindy', 7.5) print('Tallest: {0:s}'.format(Human.get_tallest().name)) Output: Tallest: Cindy License: CC BY-SA 4.0 (except images), by Jay Coskey 19
  • 20. Class example: fibonacci revisited Recall that we saw this recursive function fibonacci earlier. We can make it a function attribute of a class. class Fibonacci: def __init__(self): pass def fibonacci(self, n): assert( type(n) == int and n >= 0 ) if n == 0 or n == 1: return 1 else: return fibonacci(n – 2) + fibonacci(n – 1) if __name__ == '__main__': f = Fibonacci( ) for n in range(0, 32): print('Fibonacci #{0:d} = {1:d}'.format(n, f.fibonacci (n))) Output: Fibonacci #0 = 1 Fibonacci #1 = 1 Fibonacci #2 = 2 Fibonacci #3 = 3 etc. License: CC BY-SA 4.0 (except images), by Jay Coskey 20 No instance data, so nothing to initialize, so __init__ isn't needed. This is an "instance function". That just mean that each object created gets its own copy. So what? Is there any benefit to putting this function in a class? Not yet. But wait, there's more!....
  • 21. Class example: Cached fibonacci function We can save return values as we compute them, and reuse them. This saves on computation time, and speeds up our program. class CFibonacci: computed_values = dict() def __init__(self): pass def fibonacci(self, n): assert( type(n) == int and n >= 0 ) if n in CFibonacci.computed_values.keys(): return CFibonacci.computed_values[n] elif n == 0 or n == 1: return 1 else: val = self.fibonacci(n – 2) + self.fibonacci(n – 1) CFibonacci.computed_values[n] = val return val if __name__ == '__main__': cf = CFibonacci() for n in range(0, 5000): print(cf.fibonacci(n)) Output: Same as before, but much faster. License: CC BY-SA 4.0 (except images), by Jay Coskey 21 Class-level data (no use of 'self') Instance-level function (self is used) An instance-level function can use class data This range is far more than the previous version could have handled, and it's done in a fraction of a second.
  • 22. Python operators and special functions (a partial list) (See Python 3 docs at https://docs.python.org/3.6/library/operator.html) Operation Special object-level function (a.k.a. "dunder" function) abs(), +, - *, /, // **, %, @, divmod __abs__, __pos__, __add__, __concat__, __neg__, __sub__, __mul__, __truediv__, __floordiv__, __pow__, __mod__, __matmul__, __divmod__ +, -, *, /, **, etc. __radd__, __rsub__, __rmul__, __rtruediv__, __rpow__, etc. +=, -=, *=, /=, etc. __iadd__, __isub__, __imul__, __itruediv__, etc. <, <=, ==, !=, >, >= __lt__, __le__, __eq__, __ne__, __gt__, __ge__ (__cmp__ is not in 3.5) See functools.total_ordering and, or, not Logical: __not__ (Also, not_). No special functions for the first two: see PEP 335. &, |, ^, ~ Bitwise: __and__, __or__, __xor__, __invert__ (Also and_, or_) <<, >> __lshift__, __rlshift, __rshift__, __rrshift__ bool(obj) len(obj) If __bool__ is not defined, but __len__ is, then an object is true if len != 0. If neither __bool__ nor __len__ is defined, then all instances are considered true. self[key] __getitem__, __setitem__, __delitem__ (Both indexing and slicing) for word in words __contains__, __iter__, __next__ is_, is_not Test object type 22 License: CC BY-SA 4.0 (except images), by Jay Coskey
  • 23. Python operators and special functions (2) Operation Special function (a.k.a. "dunder" function) abs(), +, - *, /, // **, %, @, divmod __abs__, __pos__, __add__, __concat__, __neg__, __sub__, __mul__, __truediv__, __floordiv__, __pow__, __mod__, __matmul__, __divmod__ Etc. Etc. 23 License: CC BY-SA 4.0 (except images), by Jay Coskey • Suppose we define our own class, Vector2d: class Vector2d: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): new_x = self.x + other.x new_y = self.y + other.y return Vector2d(new_x, new_y) With Vector2d defined like this, whenever Python sees: • a Vector2d called a • in the expression a + b it converts that expression into a.__add__(b). Otherwise, when it sees: • a Vector2d called b • in the expression a + b it converts that expression into b.__radd__(a). Lastly, when it sees: • a Vector2d called a • in the expression a += b it converts that expression into a.__iadd__(b). • Which of the operators we just saw would be convenient to define for this new class? r => "right-hand side" i => "increment"
  • 24. IndividualCheckingAcct Class hierarchies (not a focus of this course) • Just like the tree biological taxonomy forms a hierarchy, you can also form a tree of classes modeling some sort of hierarchy. • For example, you can define a class called Mammal, and then define another class called Dog, and tell Python that class Dog inherits all the attributes in class Mammal. • We won't be focusing on class hierarchies or class inheritance in this course. • Sorry, rover! License: CC BY-SA 4.0 (except images), by Jay Coskey 24 Dog Mammals IndividualBankAcct CommercialBankAcct CheckingAcct SavingsAcct BankAcct class IndividualCheckingAcct( IndividualBankAcct, CheckingAcct ):
  • 25. Logging License: CC BY-SA 4.0 (except images), by Jay Coskey 25
  • 26. Logging (1) • Print statements can be useful for debugging, but there is room for improvement. There could be a way to: • save the output to files, since a text window isn't always available (e.g., window-based apps) • associate each message with a level of importance (DEBUG, INFO, WARNING, ERROR, CRITICAL) • log only those messages that are important enough within the current "context", i.e., that meet a given "threshold" • always log the time that the message was logged (and perhaps the host, for a server). • Enter the logging package. import logging logging.basicConfig(level=logging.WARNING) # Only log msgs @ level WARNING or higher logger = logging.getLogger(__name__) logger.info('Starting to read from database') # Not logged, since INFO < WARNING logger.error('Could not read from database') # Logged, since ERROR > WARNING Output: ERROR:__main__:Could not read from database 1. Within the software industry, a standard has emerged for web servers: See Common_Log_Format on Wikipedia. 2. If your logged messages are aggregated across timezones, then you should use Coordinated Universal Time (UTC). 3. Protip: Never use the filename logging.py: this confuses the logging infrastructure. License: CC BY-SA 4.0 (except images), by Jay Coskey 26
  • 27. Logging (2) • We just saw that the logging system can be configured with the statement that sets the logging "threshold". logging.basicConfig(level=logging.WARNING) • Here are some of the many other things that can be done with a basicConfig statement. • Specify a filename, with the filename argument (By default, output is printed on the "console".) • Specify the output format, with the format argument (This can include the user name, host name, etc.) • Specify the date/time format, with the datefmt argument (default format: 2010-09-06 22:38:15,292) In case it wasn't clear from the previous slide: • The statement logging.debug(msg) logs the given message at level DEBUG. • The statement logging.info(msg) logs the given message at level INFO. • The statement logging.warning(msg) logs the given message at level WARNING. • The statement logging.error(msg) logs the given message at level ERROR. • The statement logging.critical(msg) logs the given message at level CRITICAL. Note: The logging framework is designed to be thread-safe by default. Note: Logging systems have been around for ages, including syslog (since the '80s) and Java's log4j (since 2001). License: CC BY-SA 4.0 (except images), by Jay Coskey 27
  • 28. Unit testing License: CC BY-SA 4.0 (except images), by Jay Coskey 28
  • 29. Unit testing: Introduction • Q: How do you know if your code is working right? • A: You don't. • Q: What types of testing can increase your confidence that it's working right? • A: There are many. Unit testing is an important one. • Unit testing—Apply tests to small units of code. • Integration testing—Check that the different pieces of code are working together as they're supposed to. • Regression testing—Check to see that the bugs that have been seen in the past haven't returned. • Acceptance testing—Check that the customer's requirements for sign-off are being met. • And many, many more. License: CC BY-SA 4.0 (except images), by Jay Coskey 29
  • 30. Unit testing: TestCase using assertEqual (1) • Suppose your program has as many lines of testing code as there are of "production" code. Keeping them separate helps simplify the code. (See "Separation of concerns" on Wikipedia.) import unittest class MyClassTest(unittest.TestCase): def test_increment(self): x = 0 x += 1 self.assertEqual(x, 1) def test_decrement(self): x = 0 x -= 1 self.assertEqual(x, -1) if __name__ == '__main__': unittest.main() License: CC BY-SA 4.0 (except images), by Jay Coskey 30 This main function discovers and runs all the tests it finds. Details on how this works can be found at https://docs.python.org/3/library/unittest.html#unittest.main Important: Each test name starts with 'test_' Output: .. ---------------------------------------- Ran 2 tests in 0.000s OK
  • 31. Unit testing: TestCase using assertEqual (2) • Here is a similar unit testing example that uses the BankAccount class we saw earlier. import unittest class BankAccountTest(unittest.TestCase): <...functions that we saw earlier...> def test_deposit(self): TEST_AMOUNT = 100 acct = BankAccount(0) acct.deposit(TEST_AMOUNT) self.assertEqual(acct.get_balance(), TEST_AMOUNT) <...more test functions...> if __name__ == '__main__': unittest.main() License: CC BY-SA 4.0 (except images), by Jay Coskey 31 Oops: class BankAccount doesn't have a get_balance function. We'll need to add it.
  • 32. Unit testing: expectedFailure • Unittest has support for many, many types of assertions, including "expected failures". import unittest class BankAccountTest(unittest.TestCase): <...functions we saw earlier...> @unittest.expectedFailure def test_withdraw(self): TEST_AMOUNT = 100 acct = BankAccount(0) acct.withdraw(TEST_AMOUNT) # Ack! Insufficient funds! self.assertEqual(acct.get_balance(), -TEST_AMOUNT) <...more test functions...> if __name__ == '__main__': unittest.main() License: CC BY-SA 4.0 (except images), by Jay Coskey 32 This demonstrates the use of Python decorators, which we haven't seen before. With this, the assertion failure within this function actually causes the test to succeed.
  • 33. Unit testing: setUp and tearDown • Lastly, the unittest package has support for organized groups of tests, or "test suites". • Suppose you're testing a weather model that requires a lot of effort to set up. • Then you'd want to: • Set up a weather model before running a the test suite. • Run each test that uses this weather model. • Dispose of ("tear down") the weather model. License: CC BY-SA 4.0 (except images), by Jay Coskey 33 import unittest class MyClassTest(unittest.TestCase): def setUp(self): create_weather_model() def tearDown(self): delete_weather_model() def weather_test_1(self): pass # TODO: Add test code here def weather_test_2(self): pass # TODO: Add test code here • setUp is run first. • Next, tests #1 and #2 are run. • tearDown is run last
  • 34. Debugging “As soon as we started programming, we found to our surprise that it wasn’t as easy to get programs right as we had thought. We had to discover debugging. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.” —Maurice Wilkes (1913-2010), 1949 "Program testing can be used to show the presence of bugs, but never to show their absence!" —Edsger W. Dijkstra (1930-2002), 1970 “Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as cleverly as you can be when you write it, how will you ever debug it?” — Brian W. Kernighan (b. 1942), 1974 License: CC BY-SA 4.0 (except images), by Jay Coskey 34
  • 35. Debugging: An overview • When your program doesn't work the way you expect (or hope), it can be difficult to tell what's doing wrong. • The first 18 chapters of Think Python v2 each have a section on Debugging, plus an Appendix on debugging. • The debugging mindset. Some bugs are easy. Some require that you become a detective. • Top 10 types of bugs. There are some common mistakes that are made that you can watch out for. • Bug reports. There are standard ways to report bugs, in order to help teams debug effectively. • Prevent recurrence. Sever bugs warrant a process or activity to prevent recurrence. • Custom debugging code. Debugging is something that's often worth investing in. • Design for Testability and Automation. Make it easier and cheaper to find bugs in the future. License: CC BY-SA 4.0 (except images), by Jay Coskey 35
  • 36. Debugging: The debugging mindset Bugs are an inevitable byproduct of the optimal pace of development. • It might be possible to avoid almost all bugs by proceeding very cautiously, or even by mathematically providing the correctness of the algorithms involved, but development would be too slow for commercial purposes. Some bugs are "shallow", and just require you to fix the error reported by the Python interpreter, or correct a typo. Some other bugs are more subtle, and require that you become a detective, and • imagine what are the most likely things that could have gone wrong • question your assumptions about how your program works • get help from others on your team, or other teams, or from the internet. License: CC BY-SA 4.0 (except images), by Jay Coskey 36
  • 37. Debugging: Top 10 types of bugs Keeping possibilities like this in mind can help you find the bug more quickly. • Indentation. Mixed spaces and tabs that confused the Python interpreter? • Missing import. Tried a new function but didn't import the required module? • No return value. Trying to use the return value of a function that doesn't return one. • Off-by-one error. Did you get the right the begin and end loop bounds? (See § 8.11 of TP) • Varying constants. Was a "constant" defined differently in two different places? • Missing dependencies. Did you neglect to set up the weather model before using it? • Version incompatibility. You're calling version 3.6 of a function, but you need to call v3.8. • Recursion depth exceeded. Did a recursive function fail to stop calling itself? • Didn't remove temporary code. Forgot to remove something temporary? • Undertested error-handling code. • Suppose there are checks to handle invalid user input. • Such code is often not tested as well as the "mainstream" part of the program. License: CC BY-SA 4.0 (except images), by Jay Coskey 37
  • 38. Debugging: Bug reports Reporting a bug to others (for resolution, analysis, or archiving)? Some info to include: • Reproduction ("repro") steps. • Can the bug be reproduced? • If so, how? (See "Platform".) If it's too difficult to reproduce, is there a "minimal test case" that can trigger the same type of bug? Maybe a short program? • Description. • What (program) behavior are you expecting? A shared expectation, or disagreement? • What behavior was actually observed? How do expectation and observation differ? • Do the details of the bug change from run to run (e.g., timing issues)? • Platform. • What operating system was being used when the bug was found? • What version of Python? "python --version" or "import sys; print(sys.version)" • What program was run? How was it run? With what arguments? • If it was found through a web browser, which browser type and version? License: CC BY-SA 4.0 (except images), by Jay Coskey 38
  • 39. Debugging: Prevent recurrence • "Low-hanging fruit". • Suppose a function you use was updated from taking two arguments to taking three. • When one call of a function uncovers the error, go ahead and fix all the calls. • The systematic "Root Cause Resolution" (RCR) approach. • If the bug was serious enough, you can take steps to prevent a recurrence. • Gather info on the details. The bigger the program/system, the more effort this takes. • Understand people's actions, but make it about the system, not the people. ("Ego free") • List what steps could be taken to avoid recurrence. • Follow through and see that those steps are carried out. License: CC BY-SA 4.0 (except images), by Jay Coskey 39
  • 40. Debugging: Custom debugging code • Companies often invest a great deal of effort in custom software to help them fix bugs in their own code. • Below is a function that prints out the important attributes of a given object. It's a fancier version of the print_attributes function from Section 17.10 of ThinkPython, 2nd ed. def dump_attributes(obj): assert(hasattr(obj, '__class__')) print('Dump of instance of {0:s}'.format(repr(obj.__class__))) for attr_name in dir(obj): if hasattr(obj, attr_name) and '__' not in attr_name: attr_val = str(getattr(obj, attr_name)) attr_val = '<method>' if 'method' in attr_val else attr_val print(' obj.{0:s} = {1:s}'.format(attr_name, attr_val)) • Code like this that prints out the details of object is sometimes called "pretty printing". License: CC BY-SA 4.0 (except images), by Jay Coskey 40
  • 41. Debugging: Design for Testability and Automation • Testability = Controllability + Visibility • The Lunar Lander program we saw earlier in the course had options to set the initial values of fuel, altitude, and velocity. This made it easy to test the effect of specific burn rates on specific states of the Lunar Lander. • The BankAccount class we saw earlier started without a get_balance function, which we had to add in order to perform a specific test we had in mind. • When breaking up functionality into classes and functions, choose wisely. • Classes that are too "large" allow for too much "hidden" interaction within the class. • Classes that are too "fine-grained" require excessive "bookkeeping". • Automation of testing. • Testing is expensive. It makes sense to invest effort into automating tests over time. • unittest is one example of support for improving test automation over time. License: CC BY-SA 4.0 (except images), by Jay Coskey 41

Editor's Notes

  1. Note: Python has decorators called @classmethod and @staticmethod. Those functions that are called class-level in the document correspond to @staticmethod. Functions decorated with @classmethod aren't covered in this course.