目录 | 上一节 (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)
>>>

ab 都是 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.names.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.pypcost.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
>>>

目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承)

注:完整翻译见 https://github.com/codists/practical-python-zh

翻译:《实用的Python编程》04_01_Class的更多相关文章

  1. 翻译:《实用的Python编程》InstructorNotes

    实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...

  2. 翻译:《实用的Python编程》README

    欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...

  3. 翻译:《实用的Python编程》05_02_Classes_encapsulation

    目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...

  4. 翻译:《实用的Python编程》04_02_Inheritance

    目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...

  5. 翻译:《实用的Python编程》01_02_Hello_world

    目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...

  6. 翻译:《实用的Python编程》03_03_Error_checking

    目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...

  7. 翻译:《实用的Python编程》03_04_Modules

    目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...

  8. 翻译:《实用的Python编程》03_05_Main_module

    目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...

  9. 翻译:《实用的Python编程》05_00_Overview

    目录 | 上一节 (4 类和对象) | 下一节 (6 生成器) 5. Python 对象的内部工作原理 本节介绍 Python 对象的内部工作原理.来自其它语言的程序员通常会发现 Python 的类概 ...

随机推荐

  1. AtCoder Beginner Contest 173 C - H and V (二进制枚举)

    题意:有一张图,.表示白色,#表示黑色,每次可以将多行和多列涂成红色(也可不涂),问有多少种方案,使得剩下黑点的个数为\(k\). 题解:又学到了新的算法qwq,因为这题的数据范围很小,所以可以用二进 ...

  2. 10.PowerShell DSC之细节

    mof文件到各Node放在哪里了? 在C:\Windows\System32\Configurtion文件夹下: 你可能会注意到mof的文件名称和Pull Server上的不一致,并且多出了几个.不用 ...

  3. 02、Jmeter正则表达式提取器

    转载自:http://blog.csdn.net/quiet_girl/article/details/50724313 在使用Jmeter过程中,会经常使用到正则表达式提取器提取器,虽然并不直接涉及 ...

  4. kafka——集群安裝部署(自带zookeeper)

    kafka系列文章 第一章 linux单机安装kafka 第二章 kafka--集群安裝部署(自带zookeeper) 一.kafka简介 kafka官网:http://kafka.apache.or ...

  5. 关于TCP状态TIME_WAIT的理解

    1.TIME_WAIT的作用: TIME_WAIT状态存在的理由:1)可靠地实现TCP全双工连接的终止 在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器 ...

  6. 一道思维题 &&递归改循环

    思路: 比如5 2 12345--> 1245 从3开始,这时候5变成了1.剩下4512,对应1234.只需要找到现在n-1,k中的数对应原来的编号的映射. 比如1-->3 是1+2 mo ...

  7. leetcode10 正则表达式匹配 dp

    刚好最近在学编译原理 有点想按照语法分析写..不过用不着 因为知道正则表达式对一个串可能有多种匹配方法,所以要准备好回溯. 有最优子结构,一段s由一段p生成,于是dp. 常规思路是从前开始逐个字符匹配 ...

  8. Java中的变量之成员变量、本地变量与类变量

    Java中的变量: 1.成员变量(实例变量,属性) 2.本地变量(局部变量) 3.类变量(静态属性) 一.成员变量(实例变量,属性) 1.1-成员变量:(在类中定义,    访问修饰符   修饰符   ...

  9. ARM汇编---程序获取符号的物理地址

    在移植u-boot的过程看到过u-boot在重定向时的实现,当时不知道怎么就觉得很好理解就把这个知识点没怎么深入的理解,最近在看华为的鸿蒙OS在Cortex-A平台上的实现过程时再次遇到一时间看不太懂 ...

  10. Pycharm无法import caffe

    这里是首先建立在读者可以在终端导入而无法在Pycharm中导入的情况下的: 参考链接(问题的最后一个回答) 选用了虚拟环境的python作为解释器, 但由于caffe的特殊性, 依然没有导入, 原因就 ...