使用类创建新对象

Beginner

This tutorial is from open-source community. Access the source code

简介

本节介绍类语句以及创建新对象的概念。

这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 中级 级别的实验,完成率为 73%。获得了学习者 100% 的好评率。

面向对象(OO)编程

一种编程技术,其中代码被组织为一组“对象”。

一个“对象”由以下部分组成:

  • 数据。属性
  • 行为。方法,即应用于该对象的函数。

在本课程中,你已经在使用一些面向对象的概念了。

例如,操作列表。

>>> 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)
>>>

abPlayer 的实例。

强调:类语句只是定义(它本身不做任何事情)。类似于函数定义。

实例数据

每个实例都有自己的局部数据。

>>> 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 目录中找到解决方案代码并使用。复制它也可以。

练习 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']

然而,随着你的程序变得庞大,你可能希望创建一种更好的组织感。因此,另一种表示数据的方法是定义一个类。创建一个名为 stock.py 的文件,并定义一个表示单只股票持仓的类 Stock。让 Stock 的实例具有 namesharesprice 属性。例如:

>>> 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.names.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('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 实例列表。完成此操作后,修复 report.pypcost.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 中练习更多实验来提升你的技能。