目錄 | 上一節 (3.6 設計討論) | 下一節 (4.2 繼承)
4.1 類
本節介紹 class 語句以及建立新物件的方式。
物件導向程式設計(OOP)
物件導向程式設計是一種將程式碼組織成物件集合的程式設計技術。
一個物件包括:
- 資料。屬性
- 行為。方法——應用於物件的函式。
在本課程中,你已經使用了物件導向程式設計技術。
例如,操作列表。
>>> nums = [1, 2, 3]
>>> nums.append(4) # Method
>>> nums.insert(1,10) # Method
>>> nums
[1, 10, 2, 3, 4] # Data
>>>
nums
是列表的例項(instance)。
方法(append()
和 insert()
)被繫結到例項(nums
)上。
class
語句
使用 class
語句定義一個新的物件。
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.health = 100
def move(self, dx, dy):
self.x += dx
self.y += dy
def damage(self, pts):
self.health -= pts
簡而言之,類是一組函式,對所謂的 例項(instance) 執行各種操作。
例項
例項是你在程式中操作的實際物件。
通過像呼叫函式一樣呼叫類來建立例項。
>>> a = Player(2, 3)
>>> b = Player(10, 20)
>>>
a
和 b
都是 Player
類的例項。
強調:class 語句僅僅是一個定義(它本身不執行任何操作)。類似於函式定義。
例項資料
每個例項都擁有自己的區域性資料。
>>> a.x
2
>>> b.x
10
資料通過 _init__()
方法進行初始化。
class Player:
def __init__(self, x, y):
# Any value stored on `self` is instance data
self.x = x
self.y = y
self.health = 100
對屬性的總數或者型別沒有限制。
例項方法
應用於物件例項的函式稱為例項方法。
class Player:
...
# `move` is a method
def move(self, dx, dy):
self.x += dx
self.y += dy
物件本身始終作為第一個引數傳遞。
>>> a.move(1, 2)
# matches `a` to `self`
# matches `1` to `dx`
# matches `2` to `dy`
def move(self, dx, dy):
按照慣例,例項稱為 self
。但是,使用的實際名字不重要。物件始終作為第一個引數傳遞。將這個引數稱為 self
只是 Python 的程式設計風格。
類作用域
類未定義名稱的作用域。
class Player:
...
def move(self, dx, dy):
self.x += dx
self.y += dy
def left(self, amt):
move(-amt, 0) # NO. Calls a global `move` function
self.move(-amt, 0) # YES. Calls method `move` from above.
如果想要對例項進行操作,那麼你始終需要顯式地引用它(如: self
)。
練習
從本組練習開始,我們將對前面章節的現有程式碼進行一系列更改。從練習 3.18 版本的程式碼開始非常重要。如果你還沒有這些程式碼,請到 Solutions/3_18
目錄下檢視,然後複製它。
練習 4.1:把物件當做資料結構
在第 2 和第 3 節中,我們使用了了以元組和字典表示的資料。例如,持有的股票可以用像下面這樣的元組表示:
s = ('GOOG',100,490.10)
或者使用像下面這樣的字典表示:
s = { 'name' : 'GOOG',
'shares' : 100,
'price' : 490.10
}
你甚至可以編寫用於操作此類資料的函式。例如:
def cost(s):
return s['shares'] * s['price']
但是,隨著程式規模的不斷擴大,你可能希望建立更好的程式碼組織意識(sense)。因此,可以定義一個類表示資料。請建立一個名為 stock.py
的檔案,並定義一個名為 Stock
的類,用以表示持有的單支股票。Stock
類具有 name
, shares
,和 price
屬性。示例:
>>> import stock
>>> a = stock.Stock('GOOG',100,490.10)
>>> a.name
'GOOG'
>>> a.shares
100
>>> a.price
490.1
>>>
建立更多的 Stock
物件並對其進行操作。示例:
>>> b = stock.Stock('AAPL', 50, 122.34)
>>> c = stock.Stock('IBM', 75, 91.75)
>>> b.shares * b.price
6117.0
>>> c.shares * c.price
6881.25
>>> stocks = [a, b, c]
>>> stocks
[<stock.Stock object at 0x37d0b0>, <stock.Stock object at 0x37d110>, <stock.Stock object at 0x37d050>]
>>> for s in stocks:
print(f'{s.name:>10s} {s.shares:>10d} {s.price:>10.2f}')
... look at the output ...
>>>
需要強調的一點是,在這裡, Stock
類充當建立例項物件的工廠。基本上,你可以像呼叫函式一樣呼叫類為你建立新物件。另外,必須強調的是,每一個物件都是不同的——它們擁有各自的資料,這些資料與以建立的其它物件是分開的。
某種程度上,通過類定義的物件與字典類似——只是使用頗為不同的語法。例如,使用的是 s.name
和 s.price
,而不是 s['name']
和 s['price']
。
練習 4.2:新增方法
擁有物件後,你可以新增方法到物件上。眾所皆知,方法就是對儲存在物件內部的資料進行操作的函式。請給 Stock
物件新增 cost()
和 sell()
方法。它們應該像下面這樣工作:
>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> s.cost()
49010.0
>>> s.shares
100
>>> s.sell(25)
>>> s.shares
75
>>> s.cost()
36757.5
>>>
練習 4.3:建立例項列表
嘗試執行以下步驟,從列表字典中建立 Stock 的例項列表。然後計算總費用:
>>> import fileparse
>>> with open('Data/portfolio.csv') as lines:
... portdicts = fileparse.parse_csv(lines, select=['name','shares','price'], types=[str,int,float])
...
>>> portfolio = [ stock.Stock(d['name'], d['shares'], d['price']) for d in portdicts]
>>> portfolio
[<stock.Stock object at 0x10c9e2128>, <stock.Stock object at 0x10c9e2048>, <stock.Stock object at 0x10c9e2080>,
<stock.Stock object at 0x10c9e25f8>, <stock.Stock object at 0x10c9e2630>, <stock.Stock object at 0x10ca6f748>,
<stock.Stock object at 0x10ca6f7b8>]
>>> sum([s.cost() for s in portfolio])
44671.15
>>>
練習 4.4:使用類
請修改 report.py
程式裡面的 read_portfolio()
函式,以便如練習 4.3 所示那樣,讀取股票投資組合到 Stock
的例項列表裡面。修改完後,修復(fix)report.py
和 pcost.py
裡面所有的程式碼,以便使用 Stock
的例項進行工作,而不是使用字典。
提示:你不必對程式碼進行大量更改,主要是將字典訪問,如 s['shares']
更改為 s.shares
。
修改完後應該能夠像之前一樣執行函式:
>>> import pcost
>>> pcost.portfolio_cost('Data/portfolio.csv')
44671.15
>>> import report
>>> report.portfolio_report('Data/portfolio.csv', 'Data/prices.csv')
Name Shares Price Change
---------- ---------- ---------- ----------
AA 100 9.22 -22.98
IBM 50 106.28 15.18
CAT 150 35.46 -47.98
MSFT 200 20.89 -30.34
GE 95 13.48 -26.89
MSFT 50 20.89 -44.21
IBM 100 106.28 35.84
>>>