Intro to CS & Python, 2 lectures.
Think Python, 2 chapters.
- 8/12 Intro to CS & Python – Dr. Ana Bell, Dr. Eric Grimson
- 1/22 Structures
- 2/30 AI – Dr. Patrick Winston
- 0/4 (0/11, 0/11, 0/5, 0/8) Math for CS
- 1/11 Puzzled
- ? EECS, Remedial 8/19 Think Python – Dr. Allen Downey
Testing, Debugging, Exceptions, and Assertions; Intro to CS and Python lecture #7
In order to make testing easier, have as many functions as possible.
Check that your program runs (no syntax/semantic errors).
Know test cases; know what outputs you expect for given inputs.
Classes of tests:
1. Unit testing
→ validate each piece of the program
→ test each function separately
2. Regression testing
→ add tests for bugs as you find them
→ catch reintroduced errors that were previously fixed
→ back to 1.
3. Integration testing
→ does the overall program work?
→ resist the urge to rush this.
→ back to 1 and 2.
1. Intuition. Some programs will have natural boundaries. If the program is designed that one parameter is always bigger than the other, it won’t work if that’s violated. Document these and move on.
2. Random testing. The probability that the code works increases with more tests. Black box and glass box testing are preferred.
3. Black box testing. Explore paths through specification.
4. Glass box testing. Explore paths through code.
Black box testing of a square root program:
Boundary → 0, -1
Perfect square → 25, 400
Less than 1 → .05, .5
Irrational square root → 2
Large → 999,999,999,999
Small → 0.000 000 000 001
Glass box testing uses code to guide test case design.
Path-complete if testing evaluates every potential path.
→ branches → exercise all parts of a conditional
→ for loops
→ → loop not entered
→ → loop executed once
→ → loop executed more than once
→ while loops
→ → same as for loops.
→ → look at all ways to exit the loop.
Notes on glass box testing:
• path-complete testing can still miss bugs
• you should still test boundary cases
Use print() to debug. Put it in loops, use it at approximate half-way points (bisection).
Study the program code. Don’t ask “what is wrong?”. Ask, “How did I get the unexpected result?”. Is it part of a family of errors?
Use the scientific method.
1. Study available data. (Gather more data.)
2. Form hypothesis.
3. Repeatable experiments.
4. Pick simplest input to test with.
Types of errors (exceptions):
1. trying to access beyond the limits of a list
→ test = [1, 2, 3]
2. trying to convert (cast) an inappropriate type
3. referencing a non-existent variable
4. mixing data types without appropriate coercion
→ ‘3’ / 4
5. forgetting to close parentheses, quotes, etc.
→ a = len([1,2,3]
6. logic errors. Think before writing new code. Draw pictures, take a break. Explain the code to someone else or a rubber ducky.
7. AttributeError. Attribute reference fails.
8. ValueError. Operand type okay, but value is illegal.
9. IOError. IO system reports malfunction (e.g. file not found).
Do unit testing. Write a function, test the function, debug the function. Then do integration testing (testing the functions’ interplay). This systematic methodology will cut down on debugging time.
If you change the code, back it up first. Do this in a systematic way. The alternative is to possibly lose track of if what bugs are caused by the original coding or changes introduced.
→ print(“Bug in user input.”)
You can use multiple except statements like if statements.
→ except ValueError:
→ print(“Could not convert to a number.”)
→ except ZeroDivisionError:
→ print(“Cannot divide by zero.”)
→ print(“Something went very wrong.”)
else block. A body of code executed after associated try body completes with no exceptions.
finally block. A body of code always executed after try, else, and except clauses, even if they raised errors or executed a break, continue, or return. Useful for clean-up code that should be run no matter what else happened (e.g. close a file).
What you can do with exceptions:
1. Fail silently. Substitute default values or just continue. Bad idea because user gets no warning.
2. Return an “error” value. What value do you choose? Complicates code having to check for a special value.
3. Stop execution, signal error condition. In Python, raise an exception.
→ raise Exception(“descriptive string”)
The Python assert (assertion) can head potential errors off at the pass.
→ def avg(grades):
→ assert not len(grades) == 0, ‘no grades data’
→ return sum(grades) / len(grades)
This raises an AssertionError for empty grade lists, otherwise runs fine.
The goal is to spot bugs as soon as they are introduced and make it clear what happened. Use assertions as a supplement to testing. Raise exceptions if users supply bad data input. Use assertions to:
• check types of arguments or values
• check that invariants on data structures are met
• check constraints on return values
• check for violations of constraints on procedures (e.g. no duplicates in a list)
Object Oriented Programming; Intro to CS and Python lecture #8
1. How is it represented? (Internal representation should be private.)
2. How do you manipulate it?
Implementing an object → defining a class. Using an object → using an instance of the class.
Creating a class: (1) define its name, (2) define its attributes.
Using a class: (1) create an instance of the class, (2) doing operations on that instance.
Defining an object in Python is similar to def (defining a function), indented lines following constitute the class’ definition.
• Coordinate is a subclass of object.
• Object is a superclass of Coordinate.
• Coordinate inherits all object’s attributes. (See next lecture.)
Attributes are data and procedures belonging to a class.
Data attributes are the types of data making a class.
• Coordinate is made of two numbers.
Methods (procedural attributes) are functions that only work with this class. Methods are how to interact with the object.
• Two coordinates have a distance between them, so x2+y2=d2 could be used to determine that distance.
Defining how to create an instance of a class uses the __init__() function. Yes, it uses two double underscores. Self is a parameter defining an instance. You can use any name, but convention is self, so stick to that.
class Coordinate(object): def __init__(self, x, y): self.x = x self.y = y
Once you’ve given how to create an instance of a class, proceed to creating methods for the class.
class Coordinate(object): def __init__(self, x, y): self.x = x self.y = y def distance (self, other): x_diff_sq = (self.x-other.x)**2 y_diff_sq = (self.y-other.y)**2 returnt (x_diff_sq + y_diff_sq)**0.5
Other than self and dot notation, methods behave just like functions; they take parameters, do operations, and return objects. Note how the distance method would work:
c = Coordinate(3,4) zero = Coordinate(0,0) print(c.distance(zero))
print(c.distance(zero)) equates to print(Coordinate.distance(c, zero)).
You’ll also want to define what print does for an object. Otherwise Python returns your defined class and the instance’s memory location.
def __str__(self): return "<" + str(self.x) + ", " + str(self.y) + ">"
You can override many common operators to work with your class.
__add__(self, other) → self + other
__sub__(self, other) → self – other
__eq__(self, other) → self == other
__len__(self) → len(self)
You can use isinstance() to check if an object is a type of object: print(isinstance(c, Coordinate)).
Think Python CH 8
Printing a string backwards.
>>> def backwardString(s): ... n = "" ... i = 1 ... while len(n) < len(s): ... n = n + s[len(s)-i] ... i += 1 ... return n ... >>> print(backwardString("Marcus Aurelius")) suileruA sucraM
Printing a string backwards, one character per line.
>>> def backwardAcrostic(s): ... i = 1 ... while i <= len(s): ... print(s[len(s)-i]) ... i += 1 ... >>> backwardAcrostic("ABC") C B A