简介
本节介绍类语句以及创建新对象的概念。
本节介绍类语句以及创建新对象的概念。
一种编程技术,其中代码被组织为一组“对象”。
一个“对象”由以下部分组成:
在本课程中,你已经在使用一些面向对象的概念了。
例如,操作列表。
>>> nums = [1, 2, 3]
>>> nums.append(4) ## 方法
>>> nums.insert(1,10) ## 方法
>>> nums
[1, 10, 2, 3, 4] ## 数据
>>>
nums 是列表的一个“实例”。
方法(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
简而言之,一个类是一组对所谓的“实例”执行各种操作的函数。
实例是你在程序中实际操作的“对象”。
它们通过将类当作函数调用来创建。
>>> a = Player(2, 3)
>>> b = Player(10, 20)
>>>
a 和 b 是 Player 的实例。
强调:类语句只是定义(它本身不做任何事情)。类似于函数定义。
每个实例都有自己的局部数据。
>>> a.x
2
>>> b.x
10
此数据由 __init__() 初始化。
class Player:
def __init__(self, x, y):
## 存储在 `self` 上的任何值都是实例数据
self.x = x
self.y = y
self.health = 100
对于所存储的属性的总数或类型没有限制。
实例方法是应用于对象实例的函数。
class Player:
...
## `move` 是一个方法
def move(self, dx, dy):
self.x += dx
self.y += dy
对象本身总是作为第一个参数传递。
>>> a.move(1, 2)
## 将 `a` 匹配到 `self`
## 将 `1` 匹配到 `dx`
## 将 `2` 匹配到 `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) ## 错误。调用全局 `move` 函数
self.move(-amt, 0) ## 正确。调用上面的方法 `move`。
如果你想对实例进行操作,你总是要显式地引用它(例如,self)。
从这组练习开始,我们将对前面章节中的现有代码进行一系列更改。你必须有一个能正常运行的练习 3.18 版本才能开始。如果你没有,请到 Solutions/3_18 目录中找到解决方案代码并使用。复制它也可以。
在第 2 节和第 3 节中,我们处理了用元组和字典表示的数据。例如,一只股票的持仓可以用这样的元组表示:
s = ('GOOG',100,490.10)
或者用这样的字典表示:
s = { 'name' : 'GOOG',
'shares' : 100,
'price' : 490.10
}
你甚至可以编写用于操作此类数据的函数。例如:
def cost(s):
return s['shares'] * s['price']
然而,随着你的程序变得庞大,你可能希望创建一种更好的组织感。因此,另一种表示数据的方法是定义一个类。创建一个名为 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}')
...查看输出...
>>>
这里要强调的一点是,类 Stock 的作用就像是一个用于创建对象实例的工厂。基本上,你将它当作函数调用,它就会为你创建一个新对象。同样必须强调的是,每个对象都是不同的——它们各自拥有与已创建的其他对象分开的自己的数据。
由类定义的对象在某种程度上类似于字典——只是语法略有不同。例如,现在你不是写 s['name'] 或 s['price'],而是写 s.name 和 s.price。
使用类时,你可以将函数附加到对象上。这些被称为方法,是对存储在对象内部的数据进行操作的函数。为你的 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
>>>
尝试按以下步骤从字典列表创建一个 Stock 实例列表。然后计算总成本:
>>> import fileparse
>>> with open('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
>>>
修改 report.py 程序中的 read_portfolio() 函数,使其像练习 4.3 中那样将投资组合读入 Stock 实例列表。完成此操作后,修复 report.py 和 pcost.py 中的所有代码,使其与 Stock 实例而非字典一起工作。
提示:你无需对代码进行重大更改。主要是将字典访问(如 s['shares'])改为 s.shares。
你应该能够像以前一样运行你的函数:
>>> import pcost
>>> pcost.portfolio_cost('portfolio.csv')
44671.15
>>> import report
>>> report.portfolio_report('portfolio.csv', '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
>>>
恭喜你!你已经完成了实验课。你可以在 LabEx 中练习更多实验来提升你的技能。