Contents | Previous (3.6 Design discussion) | Next (4.2 Inheritance)
This section introduces the class statement and the idea of creating new objects.
A Programming technique where code is organized as a collection of objects.
An object consists of:
- Data. Attributes
- Behavior. Methods which are functions applied to the object.
You have already been using some OO during this course.
For example, manipulating a list.
>>>nums= [1, 2, 3]
>>>nums.append(4) # Method>>>nums.insert(1,10) # Method>>>nums
[1, 10, 2, 3, 4] # Data>>>nums is an instance of a list.
Methods (append() and insert()) are attached to the instance (nums).
Use the class statement to define a new object.
classPlayer:
def__init__(self, x, y):
self.x=xself.y=yself.health=100defmove(self, dx, dy):
self.dx+=dxself.dy+=dydefdamage(self, pts):
self.health-=ptsIn a nutshell, a class is a set of functions that carry out various operations on so-called instances.
Instances are the actual objects that you manipulate in your program.
They are created by calling the class as a function.
>>>a=Player(2, 3)
>>>b=Player(10, 20)
>>>a and b are instances of Player.
Emphasize: The class statement is just the definition (it does nothing by itself). Similar to a function definition.
Each instance has its own local data.
>>>a.x2>>>b.x10This data is initialized by the __init__().
classPlayer:
def__init__(self, x, y):
# Any value stored on `self` is instance dataself.x=xself.y=yself.health=100There are no restrictions on the total number or type of attributes stored.
Instance methods are functions applied to instances of an object.
classPlayer:
...
# `move` is a methoddefmove(self, dx, dy):
self.x+=dxself.y+=dyThe object itself is always passed as first argument.
>>>a.move(1, 2)
# matches `a` to `self`# matches `1` to `dx`# matches `2` to `dy`defmove(self, dx, dy):By convention, the instance is called self. However, the actual name
used is unimportant. The object is always passed as the first
argument. It is merely Python programming style to call this argument
self.
Classes do not define a scope of names.
classPlayer:
...
defmove(self, dx, dy):
self.x+=dxself.y+=dydefleft(self, amt):
move(-amt, 0) # NO. Calls a global `move` functionself.move(-amt, 0) # YES. Calls method `move` from above.If you want to operate on an instance, you always refer to it explicitly (e.g., self).
Starting with this set of exercises, we start to make a series of
changes to existing code from previous sctions. It is critical that
you have a working version of Exercise 3.18 to start. If you don't
have that, please work from the solution code found in the
Solutions/3_18 directory. It's fine to copy it.
In section 2 and 3, we worked with data represented as tuples and dictionaries. For example, a holding of stock could be represented as a tuple like this:
s= ('GOOG',100,490.10)or as a dictionary like this:
s= { 'name' : 'GOOG',
'shares' : 100,
'price' : 490.10
}You can even write functions for manipulating such data. For example:
defcost(s):
returns['shares'] *s['price']However, as your program gets large, you might want to create a better
sense of organization. Thus, another approach for representing data
would be to define a class. Create a file called stock.py and
define a class Stock that represents a single holding of stock.
Have the instances of Stock have name, shares, and price
attributes. For example:
>>>importstock>>>a=stock.Stock('GOOG',100,490.10)
>>>a.name'GOOG'>>>a.shares100>>>a.price490.1>>>Create a few more Stock objects and manipulate them. For example:
>>>b=stock.Stock('AAPL', 50, 122.34)
>>>c=stock.Stock('IBM', 75, 91.75)
>>>b.shares*b.price6117.0>>>c.shares*c.price6881.25>>>stocks= [a, b, c]
>>>stocks
[<stock.Stockobjectat0x37d0b0>, <stock.Stockobjectat0x37d110>, <stock.Stockobjectat0x37d050>]
>>>forsinstocks:
print(f'{s.name:>10s}{s.shares:>10d}{s.price:>10.2f}')
... lookattheoutput ...
>>>One thing to emphasize here is that the class Stock acts like a
factory for creating instances of objects. Basically, you call
it as a function and it creates a new object for you. Also, it must
be emphasized that each object is distinct---they each have their
own data that is separate from other objects that have been created.
An object defined by a class is somewhat similar to a dictionary--just
with somewhat different syntax. For example, instead of writing
s['name'] or s['price'], you now write s.name and s.price.
With classes, you can attach functions to your objects. These are
known as methods and are functions that operate on the data
stored inside an object. Add a cost() and sell() method to your
Stock object. They should work like this:
>>>importstock>>>s=stock.Stock('GOOG', 100, 490.10)
>>>s.cost()
49010.0>>>s.shares100>>>s.sell(25)
>>>s.shares75>>>s.cost()
36757.5>>>Try these steps to make a list of Stock instances from a list of dictionaries. Then compute the total cost:
>>>importfileparse>>>withopen('Data/portfolio.csv') aslines:
... portdicts=fileparse.parse_csv(lines, select=['name','shares','price'], types=[str,int,float])
...
>>>portfolio= [ stock.Stock(d['name'], d['shares'], d['price']) fordinportdicts]
>>>portfolio
[<stock.Stockobjectat0x10c9e2128>, <stock.Stockobjectat0x10c9e2048>, <stock.Stockobjectat0x10c9e2080>,
<stock.Stockobjectat0x10c9e25f8>, <stock.Stockobjectat0x10c9e2630>, <stock.Stockobjectat0x10ca6f748>,
<stock.Stockobjectat0x10ca6f7b8>]
>>>sum([s.cost() forsinportfolio])
44671.15>>>Modify the read_portfolio() function in the report.py program so
that it reads a portfolio into a list of Stock instances as just
shown in Exercise 4.3. Once you have done that, fix all of the code
in report.py and pcost.py so that it works with Stock instances
instead of dictionaries.
Hint: You should not have to make major changes to the code. You will mainly
be changing dictionary access such as s['shares'] into s.shares.
You should be able to run your functions the same as before:
>>>importpcost>>>pcost.portfolio_cost('Data/portfolio.csv')
44671.15>>>importreport>>>report.portfolio_report('Data/portfolio.csv', 'Data/prices.csv')
NameSharesPriceChange----------------------------------------AA1009.22-22.98IBM50106.2815.18CAT15035.46-47.98MSFT20020.89-30.34GE9513.48-26.89MSFT5020.89-44.21IBM100106.2835.84>>>Contents | Previous (3.6 Design discussion) | Next (4.2 Inheritance)