MIME-Version: 1.0 Content-Location: file:///C:/90861A50/Week10.htm Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset="us-ascii"
Week =
10
Object-oriented Programming and Tables
Indiana University
Computer Science A202 /=
A598
and Informatics I211
This
week’s success strategy
<=
![if !supportLists]>n&nb=
sp;
Learn to read computer material like a scien=
tist
<=
![if !supportLists]>q <=
/span>ok to read casually the first time to get a general
impression
<=
![if !supportLists]>q <=
/span>but you must go back and reread it until you
understand it in detail
<=
![if !supportLists]>q <=
/span>reread until you feel you understand why
everything is the way it is
<=
![if !supportLists]>q <=
/span>if you don't understand the why of it, you don't
understand the underlying concept, and you won't be able to apply the knowl=
edge
when you are programming
This =
week
<=
![if !supportLists]>n Review
of a solution to assignment 8
<=
![if !supportLists]>n A
couple of dictionary examples from last weeks notes
<=
![if !supportLists]>n Keyword
default value strangeness
<=
![if !supportLists]>n Introduction
to object-oriented programming (OOP)
<=
![if !supportLists]>q <=
/span>essence of OOP
<=
![if !supportLists]>q <=
/span>classes
<=
![if !supportLists]>q <=
/span>methods
<=
![if !supportLists]>q <=
/span>instance variables
<=
![if !supportLists]>q <=
/span>instance creation
<=
![if !supportLists]>n Table
class
makeDictionary function
<=
![if !supportLists]>n =
span>Define the function makeDictionary, which t=
akes
a sequence of (key, value) pairs and returns the corresponding dictionary
>>> items =3D dict.items()
>>> items
[('Apr', 30), ('Mar', 31), ('Feb', 29)]
>>> makeDictionary(items)
{'Apr': 30, 'Mar': 31, 'Feb': 29}
<=
![if !supportLists]>n =
span>Answer
def makeDictionary(items):
'''Return=
a
dictionary with those associations
indicated=
by
(key, value) pairs in items list.'''
dict =3D =
{}
for key, =
value
in items:
if
key in dict:
=
raise Exception(key + ' already in dictionary')
dict[key] =3D value
return di=
ct
invertDictionary function
<=
![if !supportLists]>n Write
a function that takes a dictionary and returns a dictionary obtained by
reversing the key/value roles in its associations
>>> dict
{'Apr': 30, 'Mar': 31, 'Feb': 29}
>>> invertDictionary(dict)
{29: 'Feb', 30: 'Apr', 31: 'Mar'}
<=
![if !supportLists]>n Solution
def invertDictionary(dict):
invertedD=
ict =3D
{}
for key in
dict:
if
dict[key] in invertedDict:
raise
Exception('dictionary is not 1-to-1')
invertedDict[ dict[key] ] =3D key
return
invertedDict
Keywo=
rd
default value strangeness
<=
![if !supportLists]>n =
span>This seems very strange. What's going on?
>>> def strange(data=3D[]):
data.append('x')=
return data
>>> strange()
['x']
>>> strange()
['x', 'x']
<=
![if !supportLists]>n =
span>Keyword default value expressions, in this case an
empty list, are only evaluated once, when the function definition is evalua=
ted
<=
![if !supportLists]>q <=
/span>in this example there is just one default value for data, whi=
ch
both calls modify
<=
![if !supportLists]>n =
span>Moral: never
mutate a keyword default value !
<=
![if !supportLists]>q <=
/span>if necessary, make a copy of the value that can be mutated without
strangeness
The
essence of object-oriented programming
<=
![if !supportLists]>n An
object is a computation value that can contain both state (da=
ta)
and behavior (code)
<=
![if !supportLists]>n Object-oriented
programming (OOP) greatly facilitates code reuse
<=
![if !supportLists]>q <=
/span>code can be used in many more circumstances if the
data it manipulates contains knowledge of how it behaves <=
![if !supportLists]>q <=
/span>much of the world (both real and artificial) consi=
sts
of objects containing state and behavior, which OOP can model more directly=
Stori=
ng
object state and behavior <=
![if !supportLists]>n =
span>Variables, called fields, that are part of =
an
object store its state <=
![if !supportLists]>q <=
/span>the syntax <object expression>.<field name> may be
used to access and assign field values <=
![if !supportLists]>q <=
/span>as usual for Python variables, fields are created via assignment <=
![if !supportLists]>n =
span>Every object is associated with a class that
was used to create it <=
![if !supportLists]>q <=
/span>an object is said to be an instance of the class that created=
it <=
![if !supportLists]>q <=
/span>think of a class as a factory or blueprint for creating objects <=
![if !supportLists]>q <=
/span>every class is also a type, to which all of its instances belong <=
![if !supportLists]>n =
span>The behavior of an object is provide by special
functions, called methods <=
![if !supportLists]>q <=
/span>methods are stored in variables associated with its class <=
![if !supportLists]>q <=
/span>methods are called with the syntax Object
attributes <=
![if !supportLists]>n&nb=
sp;
The fields and methods of an object are known
collectively as its attributes <=
![if !supportLists]>n Each
instance (object) has its own set of fields <=
![if !supportLists]>n All
instances of the same class share the same set of methods Messa=
ge
passing <=
![if !supportLists]>n The
message passing metaphor is used to describe method calls <=
![if !supportLists]>q <=
/span>the value of the <object expression> is obje=
ct
to which the message is sent, called the target of the call <=
![if !supportLists]>q <=
/span>the <method name> and the <parameter>
values (arguments of the call) are the message <=
![if !supportLists]>n In
every Python method call, the first formal parameter of the method,
traditionally named self, is assigned the target of the call <=
![if !supportLists]>q <=
/span>hence every Python method must have at least one
formal parameter <=
![if !supportLists]>q <=
/span>remaining formal parameters are bound to the call
arguments as usual <=
![if !supportLists]>q <=
/span>in Java, the keyword this has the same mean=
ing
as self Class
syntax <=
![if !supportLists]>n =
span>Class statement syntax (simplified) class <class name> : def <method name> ( self [, <parameter>, ̷=
0;,
<parameter> ] ) : <method body> +… <=
![if !supportLists]>n =
span>Example: a class with a single field, value,
and associated getter and setter methods >>> class Value: def setValue(self,
value): =
self.value =3D value def
getValue(self): =
return self.value >>> v =3D Value() # create an instance of the Value class >>> v.setValue(3) >>> v.getValue() 3 Insta=
nce
creation <=
![if !supportLists]>n =
span>Instance creation expression syntax <=
![if !supportLists]>q
<class name> (=
<argument
exp>, …, <argument exp> ) <=
![if !supportLists]>n =
span>Semantics <=
![if !supportLists]>q <=
/span>a new instance of the named class is created and returned <=
![if !supportLists]>q <=
/span>if the class has a constructor method named __init__, =
it
is called with the new instance (as self) and the argument expression
values <=
![if !supportLists]>q <=
/span>an error is raised if there are argument expressions and there is not
method named __init__ accepting the given number of arguments <=
![if !supportLists]>q <=
/span>the purpose of the constructor is usually just to create the new
instance's fields <=
![if !supportLists]>n =
span>Example <=
![if !supportLists]>q <=
/span>add to the Value class just defined def __init__(self, value) : self.valu=
e =3D
value <=
![if !supportLists]>q <=
/span>then an instance can be created with an initial value v =3D Value(3) OOP
example: class RandomPick <=
![if !supportLists]>n =
span>Class RandomPick documentation <=
![if !supportLists]>q <=
/span>RandomPick(items) returns an instance of the RandomPick
class that allows random selection of elements from the items sequence <=
![if !supportLists]>q <=
/span>.pick() returns an element pic=
ked
at random (without replacement) from the items remaining in the pick collec=
tion <=
![if !supportLists]>q <=
/span>.isEmpty() returns a boolean
indicating if all items in the collection have been picked <=
![if !supportLists]>n =
span>Usage example >>> rp =3D RandomPick('abc') >>> rp.pick() 'c' >>> rp.pick() 'a' >>> rp.pick() 'b' >>> rp.isEmpty() True Class=
RandomPick
implementation import random class RandomPick: def
__init__(self, items):
self.items =3D list(items) # copies lists def pick(=
self): #
randomly pick self.items index i =3D
random.randrange(len(self.items))
return self.items.pop(i) def
isEmpty(self):
return self.items =3D=3D [] Tables
(not in text) <=
![if !supportLists]>n Tables
are the basis of spreadsheets, databases, and much graphical and other nume=
ric
computation, among other common uses <=
![if !supportLists]>n Tables
are most naturally represented in Python as a list of lists (or perhaps usi=
ng
tuples instead of lists) <=
![if !supportLists]>q <=
/span>rows are represented by sublists <=
![if !supportLists]>q <=
/span>columns are obtained by selecting the elements wit=
h a
given index from all the rows <=
![if !supportLists]>q <=
/span>all rows are assumed to be of the same length <=
![if !supportLists]>n&nb=
sp;
We abstract table operations by defining cla=
ss Table assert
and documentation convention (not in text) <=
![if !supportLists]>n Assert
statement <=
![if !supportLists]>q <=
/span>syntax: assert <expression> <=
![if !supportLists]>q <=
/span>semantics: throws an exception if <test
expression> is not true <=
![if !supportLists]>q <=
/span>valuable for <=
![if !supportLists]>n &nb=
sp; automatic testing <=
![if !supportLists]>n &nb=
sp; catching errors running programs <=
![if !supportLists]>n &nb=
sp; making programs easier to understand <=
![if !supportLists]>q <=
/span>example: assert 1 + 2 =3D=3D 3 <=
![if !supportLists]>n Documentation
convention: from now on, assume all mutation methods and functions return <=
b>None
unless noted otherwise Class=
Table,
documentation <=
![if !supportLists]>n =
span>Table(data<=
/span>) constructs a new
Table instance with rows being the elements of the given list of lists <=
![if !supportLists]>q <=
/span>assume each row is the same length <=
![if !supportLists]>q <=
/span>data could be a sequence of sequences with associated immutability <=
![if !supportLists]>n
.getData() returns the table data structure <=
![if !supportLists]>n =
span>.ref(rowIn=
dex, colum=
nIndex) returns the value=
of
the element at the indicated row and column <=
![if !supportLists]>n =
span>.set(rowIn=
dex, colum=
nIndex, valu=
e) assigns value<=
b> to
the element at the indicated row and column <=
![if !supportLists]>q <=
/span>rows must be lists <=
![if !supportLists]>n =
span>.row(rowIn=
dex) returns the indic=
ated
row <=
![if !supportLists]>n =
span>.column(colum=
nIndex) returns a list wh=
ose
elements are those in the indicated column Modul=
e Table,
test code <=
![if !supportLists]>n
We define a table
module (in a file named table.py) <=
![if !supportLists]>n
It is a good idea=
to
write the test code first def main(): t =3D
Table([['a', 1, True], ['b', 2, False]]) assert
t.getData() =3D=3D [['a', 1, True], ['b', 2, False]] assert Fa=
lse =3D=3D
t.ref(1, 2) t.set(1, =
2,
True) assert Tr=
ue =3D=3D
t.ref(1, 2) assert [1=
, 2]
=3D=3D t.column(1) assert ['=
b' ,
2, True] =3D=3D t.row(1) if __name__ =3D=3D '__main__': main() Class=
Table,
definition class Table: def
__init__(self, data):
self.numCols =3D len(data[0])
self.table =3D data def getData(=
self):
return self.table def ref(self, rowIndex, colIndex):<=
o:p>
return self.table[rowIndex][colIndex] def set(s=
elf,
rowIndex, colIndex, value):
self.table[rowIndex][colIndex] =3D value def row(s=
elf,
rowIndex):
return self.table[rowIndex] def
column(self, columnIndex): col
=3D [] for
row in self.table: =
col.append(row[columnIndex])
return col Addin=
g zip
to your code <=
![if !supportLists]>n The
built-in function zip takes one or more sequences and returns a list=
of
tuples, with each tuple containing the values of sequence elements with the
same index <=
![if !supportLists]>q <=
/span>if some sequences are longer than others, values p=
ast
the length of the shortest sequence are ignored <=
![if !supportLists]>q <=
/span>like a zipper that locks together teeth in the same
position on each side >>> zip('abc', 'def') [('a', 'd'), ('b', 'e'), ('c', 'f')] >>> zip('abc', [1, 2, 3], (True, False, True)) [('a', 1, True), ('b', 2, False), ('c', 3, True)] Table
exercise 1 <=
![if !supportLists]>n Add
the following methods <=
![if !supportLists]>q <=
/span>.dimensions() retur=
ns
number of table rows and columns <=
![if !supportLists]>q <=
/span>.addRow(row=
span>) adds row =
b>to
end of table <=
![if !supportLists]>n &nb=
sp; assume row length equals the number of columns in the current
table <=
![if !supportLists]>q <=
/span>.addColumn(colum=
n) adds column=
to
right side of table <=
![if !supportLists]>n &nb=
sp; assume column length equals number of rows in the current tab=
le <=
![if !supportLists]>n
hint: =
use a for loop iterating over a list created by zip <=
![if !supportLists]>n Additional
tests for these methods assert t.getData() =3D=3D [['a', 1, True], ['b', 2, True]] t.addRow(['c', 3, False]) t.addColumn(['the', 'last', 'column']) assert t.getData() =3D=3D [['a', 1, True, 'the'], =
&nb=
sp;
['b', 2, True, 'last'], =
&nb=
sp;
['c', 3, False, 'column']] Table
exercise 1, solution def dimensions(self):
return (len(self.table), self.numCols) def
addRow(self, row):
self.table.append(row) def
addColumn(self, column): for
row, value in zip(self.table, column): =
row.append(value)
self.numCols +=3D 1 Table=
exercise 2 <=
![if !supportLists]>n Add
the following methods <=
![if !supportLists]>q <=
/span>.deleteRow(rowIn=
dex) deletes the indic=
ated
row from the table <=
![if !supportLists]>q <=
/span>.deleteColumn(columnIndex) deletes the indic=
ated
column from the table <=
![if !supportLists]>q <=
/span>.printTable(print=
ColWidth) prints the table =
with
column width being the absolute value of printColWidth, left justifi=
ed
if it is negative, and otherwise right justified <=
![if !supportLists]>n Additional
tests for these methods assert t.getData() =3D=3D [['a', 1, True, 'the'], =
&nb=
sp;
['b', 2, True, 'last'], =
&nb=
sp;
['c', 3, False, 'column']] t.deleteRow(0) t.deleteColumn(0) assert t.getData() =3D=3D [[2, True, 'last'], =
&nb=
sp;
[3, False, 'column']] Table
exercise 2 solution <=
span
style=3D'font-size:7.0pt;font-family:"Courier New";mso-bidi-font-family:"Co=
urier New";
mso-bidi-language:#AC45'>def deleteRow(self, rowIndex):
<object expression>.<method name>(<parameter&g=
t;…)
def
deleteColumn(self, columnIndex):
self.numCols -=3D 1
for
row in self.table:
=
def
printTable(self, columnWidth):
for
row in self.table:
=
line =3D ''
=
for value in row:
=
if columnWidth < 0:
=
s =3D str(value).ljust(-columnWidth)
=
else:
=
s =3D str(value).rjust(columnWidth)
=
line +=3D s
=
print line