Java程序辅导

C C++ Java Python Processing编程在线培训 程序编写 软件开发 视频讲解

客服在线QQ:2653320439 微信:ittutor Email:itutor@qq.com
wx: cjtutor
QQ: 2653320439
WEEK
FIVE
OO (Part 2), Exceptions, and Parsing
Lots of you are still slaving over hot computers. Keep up the good work — only one week to go!
There is a fair bit of material this week because we went a bit easy on you in the notes department last time :)
1 Object Oriented Python (continued...)
Last week, we created a Circle class (we’ve removed the colour attribute here):
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def set_radius(self, r):
self.radius = r
def area(self):
return math.pi*self.radius**2
This week we want to demonstrate some reasons for using object oriented programming. You might find these
techniques useful in answering some of the challenging problems you’ve been set this week.
2 Information hiding and abstraction
The first motivation for using OO is information hiding. In our earlier discussion about circles, we concluded
that their size could be represented by either the radius or diameter. We might want to hide our decision behind
a bunch of methods that do the work of returning the Circle properties. These methods are called getters. The
internal representation is now completely hidden outside of the class:
import math
class Circle:
def __init__(self, radius):
self._radius = radius
def radius(self):
return self._radius
def diameter(self):
return self._radius*2
def set_radius(self, r):
self._radius = r
def area(self):
return math.pi*self.radius()**2
c© National Computer Science School 2005-2009 1
NCSS Challenge (Advanced) WEEK FIVE
Notice that the original radius attribute has now been replaced with _radius. The underscore is a common
convention to indicate the attribute is private, i.e. it should not be directly accessed or modified outside of the
class. The correct way of retrieving the radius is now:
>>> c = Circle(5)
>>> c.radius()
5
>>> c.diameter()
10
This approach has the advantage that stored properties are accessed in exactly the same way as calculated
properties. From outside the object, You can’t even tell which one was stored and which one is calculated.
Hint
Try this experiment: store the diameter internally in Circle rather than the radius, and redefine the constructur,
and the diameter and radius methods to use the diameter. Any code which uses Circle will not need to be
changed.
We can now say that we have abstracted (or generalised) away from the internal representation of a circle. All
users of Circle see is the interface.
3 Operators for Objects
One advanced Python OO trick is that user defined classes can behave in the same way as builtin types, like lists
and strings with most operators. For example, the operators, like + and *, and the type casting functions, like
str and int, can be defined for them in a process called operator overloading.
Operator overloading involves defining specially named methods for each operator. For example, the __add__
method is needed for classes that support +.
Here’s a Point class that represents points in 2D space:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __str__(self):
return ’(%s, %s)’ % (self.x, self.y)
Point instances can be used with the + operator and anywhere a string is expected (e.g. in print statements):
>>> p1, p2 = Point(1, 3), Point(4, 2)
>>> res = p1 + p2
>>> res
<__main__.Point instance at 0x74c10>
>>> print res
(5, 5)
>>>
You can spy on the special methods using the dir function on an object in Python:
>>> dir(int)
[’__abs__’, ’__add__’, ’__and__’, ’__class__’, ’__cmp__’, ... ]
>>> x = 3
>>> x.__add__(5)
8
c© National Computer Science School 2005-2009 2
NCSS Challenge (Advanced) WEEK FIVE
4 Error Handling
Unexpected things happen to programs more often than any programmer likes to think about. Good programmers
spend more of their day trying to “be prepared” than even your most zealous Scout master because expecting the
unexpected is challenging — for even the most methodical programmer.
Not only is error handling hard, but it is cumbersome because it tends to clutter code with mountains of checking
and handling statements. Even the most aesthetically-pleasing tract of code can be buried in a mound of error
handling goo.
Fundamentally, error handling is about covering all of the dumb things (i.e. unexpected or erroneous events) that
could happen to your program and writing code to deal with them. Which leads to the difficulty of deciding what
to do when something does go wrong. In many cases it is not clear what the program should do when a file is
missing, a network connection fails, or the program runs out of memory. In fact, because it is so mean and ugly,
many programmers avoid writing error-handling code entirely!
Since so many things can go wrong at some level or another, simple, flexible error-handling is imperative for
clean, correct, readable code. Exceptions are an attempt to manage error handling in a simple yet flexible way
which minimises code clutter.
5 Exceptions
By now you have seen an exception — because they are so easy to create. You have seen them occur when trying
to open a file that doesn’t exist, trying to give functions illegal arguments (or number of arguments) and so on.
As a quick refresher example, a KeyboardInterrupt exception is created when you stop a running Python
program using Ctrl-C:
>>> while True:
... print ’hello’
...
hello
hello
hello
hello
hello
..
This program will continue to run until you stop it because the conditional expression is always True resulting
in an infinite loop. The only way to stop it is with Ctrl-C which creates the following exception:
Traceback (most recent call last):
File ‘‘’’, line 2, in -toplevel-
print ’hello’
KeyboardInterrupt
However, a keyboard interrupt may not actually be an error, it could be an expected or anticipated event.
Exceptions are really about general event-handling rather than just catching errors. Errors are just unexpected,
nonsensical user actions or error events passed on from the operating system. For instance, a user attempting to
open a filename which does not exist or the entire system running out of memory are both error events. Other
kinds of events might include the operating system telling a program that an alarm timer has gone off. All of
these types of events can be handled using the exception mechanism.
Another kind of error event is an internal program error (often called a runtime error), such as when a program
attempts to use an undefined variable. You don’t want these kinds of errors because they indicate that your
program is broken in some way. It is also possible to handle these exceptions in Python, but it is very dodgy
programming to do so.
c© National Computer Science School 2005-2009 3
NCSS Challenge (Advanced) WEEK FIVE
6 try statements
Python supports exception handling with the try control structure. A try statement surrounds the block of code
where we are expecting an exception to occur. The try statement consists of the try keyword followed by a
colon and then the block of code which is being protected. After this comes one or more except clauses which
describe what action to take when a particular exception has occurred. These except clauses consist of an line
containing the except keyword which may be followed by an optional exception matching expression and then
a colon. This is followed by the block of code to handle the exception.
This example shows how to handle the KeyboardInterrupt exception above. In fact, this block of code will
handle any exception which occurs within the try body.
try:
while 1:
print ’hello’
except:
print ’infinite loop terminated’
In this example, the except clause does not contain an exception matching expression which means it will handle
any type of exception, including KeyboardInterrupt. When this program is run and Ctrl-C is pressed to
interrupt the infinite loop, the KeyboardInterrupt exception causes the execution to terminate immediately.
The control then passes to the first except clause that matches the exception being thrown. The block of code
associated with the matching except clause is then executed and the exception has thus been handled. This is
called catching the exception.
7 Catching specific exceptions
However, we almost always want to be more discriminating about which exceptions we handle, in which case
we should actually specify the exception to catch:
try:
while 1:
pass
except KeyboardInterrupt:
print ’infinite loop terminated’
We can also catch different exceptions using one try statement with multiple except clauses. In this example
there are three except clauses which handle three different types of exceptions: an IOError exception caused by
attempting to open a file which does not exist, a ZeroDivisionError exception caused by dividing a number
by zero and the KeyboardInterrupt we have seen above.
def test(case):
try:
if case == 0:
# try to open a file which does not exist
open(’missing.txt’)
elif case == 1:
# do division by zero (which is illegal)
1/0
except ZeroDivisionError:
print ’division by zero!!’
except IOError:
print ’file opening error!!’
test(0)
test(1)
The output of this program shows all three except clauses in action:
c© National Computer Science School 2005-2009 4
NCSS Challenge (Advanced) WEEK FIVE
file opening error!!
division by zero!!
Notice that we can use the try statement within any level of a program. Here it is used within a function. As long
as the raised exception does is one of the three listed, the function caller will not even know that an exception has
occurred and been handled.
8 Exception data
When builtin exceptions have been thrown but not caught, the exception information is printed to
sys.stderr. The last line of this output is the exception itself. Sometimes this information is simple,
eg. KeyboardInterrupt or ZeroDivisionError: integer division or modulo by zero, with no
reference to the exact circumstance in which the event occurred.
Others refer directly to the particular error that occurred: IOError: [Errno 2] No such file or
directory: ’missing.txt’ indicating which particular file access error occurred (and on what file). This
is because IOError exceptions carry more information than just the name of the exception. This information
can be accessed by specifying a variable after the exception to be matched:
try:
open(’missing.txt’)
except IOError, e:
print e.args
print e.errno
print e.filename
print e.strerror
The output of this example shows the information available about an IOError. This information is passed from
the operating system to python when the open function fails. The interpreter raises an exception and includes the
information from the operating system.
(2, ’No such file or directory’)
2
missing.txt
No such file or directory
9 Raising exceptions
As well as handling builtin exceptions, it is also possible to raise and catch your own exceptions. Exceptions
must be either objects or classes. For now, we’re going to use the builtin exceptions:
An exception is raised using the raise statement. When an exception has no extra data, raising an exception is
as simple as using the raise keyword followed by the name of the exception:
>>> raise ValueError
Traceback (most recent call last):
File "", line 1, in 
ValueError
The interpreter does the traceback for user raised exceptions in the same way. If the exception needs to contain
extra information it can be provided after the name of the exception in the raise statement:
c© National Computer Science School 2005-2009 5
NCSS Challenge (Advanced) WEEK FIVE
def square_area(side):
if side <= 0:
raise ValueError, "sides must be positive"
return side*side
try:
square_area(-1)
except ValueError, e:
print e
10 When to use exceptions
Just before we complete exceptions there are a couple of points that should be made.
• Exceptions don’t dramatically solve the problem of error handling and the amount of code that must
be devoted to error handling. Try statements are still relatively bulky, particularly with multiple except
clauses. There is still the problem of where to handle the error and how to handle it.
• General exception handlers can conceal actual program errors, so exceptions should be made to be as
specific as possible. One of the nice features of Java and to a lesser extent exceptions in general, is that
they force you to think about the different types of exceptions you need to handle. Use specific except
clauses.
This example works without whinging because the exception that should have been thrown about blahblah not
being defined previously has been swallowed up by over-zealous use of exceptions:
try:
print blahblah # haha won’t catch this syntax error!
except:
pass
11 Grammars and BNF
Parsing is the process of identifying the underlying structure or syntax of a sequence of tokens that belong to a
language. If we were talking about a language like English, the tokens would be words, and the syntax is the
English grammar.
The syntax or grammar defines which tokens and groups of tokens can legally appear together in a language. For
example, in English it is not grammatically correct to put two determiners next to each other:
The the dog ate.
Note: grammatical doesn’t necessarily mean the tokens make sense. Here’s Noam Chomsky’s famous example:
Colourless green ideas sleep furiously.
Perfectly grammatical nonsense!
Formally, a grammar is a series of rules which determine how tokens, called terminal symbols in grammars, can
be combined. For example, here is a very simple grammar of English:
Sentence ::=  
NounPhrase ::=  |  
VerbPhrase ::=  |  
ProperNoun ::= Bob | Tom
Det ::= a | the
Noun ::= fish | cat | dog
Verb ::= ate | walked
c© National Computer Science School 2005-2009 6
NCSS Challenge (Advanced) WEEK FIVE
The format for this grammar is called Backus-Naur Form (BNF) after the two inventors John Backus (the designer
of FORTRAN) and Peter Naur (a contributor to ALGOL 60). Each rule (called a production) has a left-hand and
right-hand side. The left-hand side is the name of the rule (what the rule produces). The right-hand side is a
recipe for producing the left hand side.
For example, the first rule is a recipe for creating sentences:
Sentence ::=  
A Sentence consists of a NounPhrase followed by a VerbPhrase. Our English grammar defines three possible
ways of making a Noun – all of which are terminals: fish, cat, and dog:
Noun ::= fish | cat | dog
Angle brackets are used on the right-hand side is used to distinguish rule name (called non-terminals) from
terminals like dog and Tom.
This grammar matches the sentences:
the cat ate a fish
Tom walked the dog
Bob walked
But it does not allow these sentences:
the the cat ate
Tom the dog walked
Bob
12 Recursive Grammars
Things get a bit trickier when we add rules that refer to themselves (possibly via other rules):
NounPhrase ::=   |   
PrepPhrase ::=  
Preposition := with | on
This introduces recursion into the creation of sentences because a NounPhrase may now be made up of a
PrepPhrase, which in turn must have another NounPhrase inside it.
For example, now these sentences are also valid:
the cat ate the fish with the dog
the cat on the dog ate the fish
the cat ate the fish with a dog with the cat on a fish ...
I find this quite amazing: a set of finite rules can create language of infinite extent using recursion in the rules.
13 Parsing (redux)
The grammar rules above can quite easily be turned into a set of functions that parse with the grammar. Each
grammar rule gets its own function which either checks its right-hand side by calling more functions. In the case
of terminal symbols, they’re checked directly against the tokens. If a terminal matches the next token, the token
is removed from the input – this work is done by pop below.
c© National Computer Science School 2005-2009 7
NCSS Challenge (Advanced) WEEK FIVE
def pop(tokens, legal):
if tokens and tokens[0] in legal:
tokens.pop(0)
return True
return False
def S(tokens): return NP(tokens) and VP(tokens)
def NP(tokens):
return ProperNoun(tokens) or (Det(tokens) and Noun(tokens))
def VP(tokens): return Verb(tokens)
def ProperNoun(tokens): return pop(tokens, [’Bob’, ’Tom’])
def Det(tokens): return pop(tokens, [’a’, ’the’])
def Noun(tokens): return pop(tokens, [’fish’, ’cat’, ’dog’])
def Verb(tokens): return pop(tokens, [’ate’, ’walked’])
The Sentence function, S, can now be called to check whether a set of tokens is a legal Sentence:
>>> S(’the dog ate a cat’.split())
True
>>> S(’the the dog ate a cat’.split())
False
This type of parser is called a recursive descent parser because the grammar is formed by (if necessary)
recursively calling functions to consume rules and tokens.
c© National Computer Science School 2005-2009 8