前面已经讲过,Python是一种面向对象的编程语言. 面向对象编程语言中最重要的特征是允许程序员创建类建立数据模型来解决问题.

我们之前利用抽象数据类型提供的逻辑来描述数据对象 (它的状态) 和功能 (它的方法). 通过构建类来实现抽象数据类型, 一个程序员可以发挥抽象处理的优势,同时提供详细的现实信息来解决问题.当我们想实现一个抽象数据类型的时候,我们将构建一个新的类.

本文地址:http://www.cnblogs.com/archimedes/p/python-datastruct-algorithm-class.html,转载请注明源地址。

一个分数类

下面来看一个非常普通的例子,用来展示实现抽象数据类型的一个用户自定义类:Fraction(分数). 我们已经知道 Python 给我们提供了大量的类. 有很多可以适当地帮我们构建分数类型的数据对象.

一个分数比如3/5包含两部分. 分子,可以为任何整数. 分母, 可以为除了0以外的任何整数.

Fraction 类的方法应该能够让 Fraction 对象可以像其他的数值那样进行计算. 我们需要可以进行分数之间的 加, 减, 乘, 和 除 运算. 更进一步, 所有的方法应该返回最简分数.

在 Python中, 我们定义一个类的名字还有一些方法,类似于定义一个函数,例如,

  1. class Fraction:
  2.  
  3. #the methods go here

提供了给我们定义方法的框架.第一个方法是所有类都要提供的构造器. 构造函数定义了类创建的方式. 要创建分数对象, 我们需要提供两部分的数据:分子和分母. 在 Python中, 构造函数使用 __init__ (两条下划线包围 init) ,如下所示:

Listing 2

  1. class Fraction:
  2.  
  3. def __init__(self,top,bottom):
  4.  
  5. self.num = top
  6. self.den = bottom

注意到参数列表含有三个参数: (selftopbottom). self 是一个引用对象自身的特殊的参数. 它通常作为第一个参数; 但是, 它从不在调用的时候传值. 之前已经讲过,分数包含两部分(分子和分母). 记号 self.num 在构造函数中被定义为 fraction 对象具有一个叫num 的内部数据对象. 同理, self.den 也是类似的目的.

要实现 Fraction 类, 我们需要调用构造函数. 接着通过类名传递参数 (注意到我们从不直接调用__init__). 例如:

  1. myfraction = Fraction(3,5)

创建一个分数对象 myfraction 代表分数3/5 .

接着要做的事情就是给抽象数据类型实现方法. 首先, 意识到当我们要输出一个 Fraction 对象.

  1. >>> myf = Fraction(3,5)
  2. >>> print(myf)
  3. <__main__.Fraction instance at 0x409b1acc>

fraction 对象, myf, 并不知道怎样响应输出操作.  print 函数需要对象转换为可输出的字符串格式,这样才能输出. 唯一的选择 myf 必须显示变量实际的地址引用(自身的地址). 这不是我们想要的.

有两种解决问题的办法. 一种是定义一种称为 show 的方法,可以将Fraction 对象作为一个字符串的形式打印. 我们可以实现如 Listing 3所示.假如我们按照前面讲的创建 Fraction 对象, 我们可以让它输出自身, 换句话说, 打印自身按照适当的格式. 不幸的是, 这通常不起作用. 为了使输出工作正常, 我们必须告诉 Fraction 类怎样将自身转换为字符串的格式.

Listing 3

  1. def show(self):
  2. print(self.num,"/",self.den)
  3. >>> myf = Fraction(3,5)
  4. >>> myf.show()
  5. 3 / 5
  6. >>> print(myf)
  7. <__main__.Fraction instance at 0x40bce9ac>

在 Python, 所有的类都提供但不是都适用的标准的方法. 其中之一, __str__,就是一个将对象转换为字符串的方法. 这个方法默认的实现是用来以字符串格式返回类实例的地址. 我们必须为这个方法提供一个“更好的”实现. 我们说这个新的方法重载前面的, 或者说重新定义了方法的行为.

要实现这个,我们简单地定义一个名叫 __str__ 的方法并给出实现 如Listing 4. 这个定义除了使用特殊参数 self以外不需要其他的信息. 注意函数中的不同的实现办法.

Listing 4

  1. def __str__(self):
  2. return str(self.num)+"/"+str(self.den)
  3. >>> myf = Fraction(3,5)
  4. >>> print(myf)
  5. 3/5
  6. >>> print("I ate", myf, "of the pizza")
  7. I ate 3/5 of the pizza
  8. >>> myf.__str__()
  9. '3/5'
  10. >>> str(myf)
  11. '3/5'
  12. >>>

我们可以为我们的新 Fraction 类覆盖很多其他的方法. 其中一些最重要的是一些基础的算术运算操作. 我们可以创建两种 Fraction 对象,同时使用“+” 符号将它们相加 . 这时, 如果我们使两分数相加, 我们得到:

  1. >>> f1 = Fraction(1,4)
  2. >>> f2 = Fraction(1,2)
  3. >>> f1+f2
  4. Traceback (most recent call last):
  5. File "<pyshell#173>", line 1, in -toplevel-
  6. f1+f2
  7. TypeError: unsupported operand type(s) for +:
  8. 'instance' and 'instance'

如果你仔细观察错误信息, 你将发现问题是: “+” 操作符不能理解Fraction 操作.

我们可以通过给 Fraction 类提供重载的加法函数来实现. 在 Python, 这种方法称为 __add__ 同时需要两个参数. 第一个参数, self,  第二个参数是另一个操作数. 例如,

  1. f1.__add__(f2)

当 Fraction  f1 加 Fraction f2. 可以写成标准的形式:f1+f2.

两个分数必须有相同的分母才能直接相加. 使它们分母相同最简单的方法是通分: ,具体实现如 Listing 5. 加法函数返回了一个新的 Fraction 对象.

Listing 5

  1. def __add__(self,otherfraction):
  2.  
  3. newnum = self.num * otherfraction.den + self.den*otherfraction.num
  4. newden = self.den * otherfraction.den
  5.  
  6. return Fraction(newnum,newden)
  1. >>> f1=Fraction(1,4)
  2. >>> f2=Fraction(1,2)
  3. >>> f3=f1+f2
  4. >>> print(f3)
  5. 6/8
  6. >>>

上面的加法函数看起来实现了我们期望的, 但是还可以更完美. 注意到 6/8 是正确的结果,但是却不是以 “最简项” 的形式展示的. 最好的表达式为3/4. 为了使我们的结果为最简项的形式, 我们需要一个辅助函数才化简分数. 这个函数可以求出最大公约数, 或者称为 GCD. 可以通过分子和分母的最大公约数来达到化简分数的目的.

计算最大公约数最著名的算法要数 Euclid算法,原理我就不详细指明了,很简单。实现如下:

  1. >>> def gcd(m, n):
  2. while m % n != 0:
  3. oldm = m
  4. oldn = n
  5. m = oldn
  6. n = oldm % oldn
  7. return n
  8.  
  9. >>> print gcd(20, 10)
  10. 10

这样我们就可以化简任何的分数了,代码如下: (Listing).

Listing 6

  1. def __add__(self,otherfraction):
  2. newnum = self.num*otherfraction.den + self.den*otherfraction.num
  3. newden = self.den * otherfraction.den
  4. common = gcd(newnum,newden)
  5. return Fraction(newnum//common,newden//common)
  1. >>> f1=Fraction(1,4)
  2. >>> f2=Fraction(1,2)
  3. >>> f3=f1+f2
  4. >>> print(f3)
  5. 3/4

我们的 Fraction 对象现在有两个非常重要的方法,如上图所示. 一些需要新增进我们的实例类 Fraction 的方法是:允许两个分数进行比较. 假如我们有两个 Fraction 对象, f1 和f2f1==f2 将得到True 假如他们指向同一个对象. 即使分子分母都相同,但是不满足条件依然将不相等. 这被称为 shallow equality (如下图).

我们可以创建 deep equality (如上图)–通过值相等来判断, 不同于引用–通过覆盖 __eq__ 方法.  __eq__ 是另一个存在于所有类中标准方法. __eq__ 方法比较两个对象当值相等的时候返回 True ,否则返回 False.

在 Fraction 类中, 我们实现了 __eq__ 方法通过常规比较方法来比较分数 (see Listing 7). 值得注意的是还有其他的方法可以覆盖. 例如,  __le__ 方法提供了小于等于功能.

Listing 7

  1. def __eq__(self, other):
  2. firstnum = self.num * other.den
  3. secondnum = other.num * self.den
  4.  
  5. return firstnum == secondnum

完整的 Fraction 类的代码如下所示:

  1. def gcd(m,n):
  2. while m%n != 0:
  3. oldm = m
  4. oldn = n
  5.  
  6. m = oldn
  7. n = oldm%oldn
  8. return n
  9.  
  10. class Fraction:
  11. def __init__(self,top,bottom):
  12. self.num = top
  13. self.den = bottom
  14.  
  15. def __str__(self):
  16. return str(self.num)+"/"+str(self.den)
  17.  
  18. def show(self):
  19. print(self.num,"/",self.den)
  20.  
  21. def __add__(self,otherfraction):
  22. newnum = self.num*otherfraction.den + \
  23. self.den*otherfraction.num
  24. newden = self.den * otherfraction.den
  25. common = gcd(newnum,newden)
  26. return Fraction(newnum//common,newden//common)
  27.  
  28. def __eq__(self, other):
  29. firstnum = self.num * other.den
  30. secondnum = other.num * self.den
  31. return firstnum == secondnum
  32.  
  33. x = Fraction(1,2)
  34. y = Fraction(2,3)
  35. print(x+y)
  36. print(x == y)

运行结果:

  1. 7/6
    False

您还可能感兴趣:

Python基础(10)--数字

Python基础(9)--正则表达式

Python基础(8)--文件

Python基础(7)--函数

Python基础(6)--条件、循环

Python基础(5)--字典

Python基础(4)--字符串

Python基础(3)--列表和元组

Python基础(2)--对象类型

Python基础(1)--Python编程习惯与特点

Python数据结构与算法--面向对象的更多相关文章

  1. Python数据结构与算法--List和Dictionaries

    Lists 当实现 list 的数据结构的时候Python 的设计者有很多的选择. 每一个选择都有可能影响着 list 操作执行的快慢. 当然他们也试图优化一些不常见的操作. 但是当权衡的时候,它们还 ...

  2. Python数据结构与算法--算法分析

    在计算机科学中,算法分析(Analysis of algorithm)是分析执行一个给定算法需要消耗的计算资源数量(例如计算时间,存储器使用等)的过程.算法的效率或复杂度在理论上表示为一个函数.其定义 ...

  3. python数据结构与算法

    最近忙着准备各种笔试的东西,主要看什么数据结构啊,算法啦,balahbalah啊,以前一直就没看过这些,就挑了本简单的<啊哈算法>入门,不过里面的数据结构和算法都是用C语言写的,而自己对p ...

  4. Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例

    本文实例讲述了Python数据结构与算法之图的最短路径(Dijkstra算法).分享给大家供大家参考,具体如下: # coding:utf-8 # Dijkstra算法--通过边实现松弛 # 指定一个 ...

  5. Python数据结构与算法之图的广度优先与深度优先搜索算法示例

    本文实例讲述了Python数据结构与算法之图的广度优先与深度优先搜索算法.分享给大家供大家参考,具体如下: 根据维基百科的伪代码实现: 广度优先BFS: 使用队列,集合 标记初始结点已被发现,放入队列 ...

  6. python数据结构与算法之问题求解实例

    关于问题求解,书中有一个实际的案例. 上图是一个交叉路口的模型,现在问题是,怎么安排红绿灯才可以保证相应的行驶路线互不交错. 第一步,就是把问题弄清楚. 怎么能让每一条行驶路线不冲突呢? 其实,就是给 ...

  7. python数据结构与算法之问题求解

    懂得计算机的童鞋应该都知道,一条计算机程序由数据结构跟算法两大部分组成.所以,其实不管你使用哪种计算机语言编写程序,最终这两部分才是一个程序设计的核心.所以,一个不懂得数据结构与算法的程序员不是一个好 ...

  8. Python 数据结构和算法

    阅读目录 什么是算法 算法效率衡量 算法分析 常见时间复杂度 Python内置类型性能分析 数据结构 顺序表 链表 栈 队列 双端队列 排序与搜索 冒泡排序 选择排序 插入排序 希尔排序 快速排序 归 ...

  9. Python数据结构与算法(几种排序)

    数据结构与算法(Python) 冒泡排序 冒泡排序(英语:Bubble Sort)是一种简单的排序算法.它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.遍历数列的工作是 ...

随机推荐

  1. SQL SERVER UNION和UNION ALL

    union与union allunion 缺省在合并结果集后消除重复项,union all 指定在合并结果集后保留重复项, 打个比喻吧 比如A表的数据是 A{ 1,4,5,9}       B{2,3 ...

  2. HIVE: Transform应用实例

    数据文件内容 steven:100;steven:90;steven:99^567^22 ray:90;ray:98^456^30 Tom:81^222^33 期望最终放到数据库的数据格式如下: st ...

  3. Linux下U盘变成只读

    今天用Ubuntu给同学拷贝数据的时候,突然其中一个文件夹U盘就不能复制和删除了.再windows7下可以删除除修改的那个文件夹之外的数据,但修改的那个文件夹死活删除不掉,只读属性也去不掉.再Ubun ...

  4. 让文档和Demo生成更加简单和强大 - SmartDoc 0.1.1 说明

    新特性 smartDoc 0.1.1版正式发布,其中加入了更多方便生成文档的功能,主要特性如下: * 加入@demo配置项,看可以动态抓取html和js的内容作为@example,同时支持扩展@dem ...

  5. 基于jQuery点击加载动画按钮特效

    分享一款基于jQuery点击加载动画按钮特效.这是一款基于jQuery+CSS3实现的鼠标点击按钮加载动画特效代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div ...

  6. 【转】在Eclipse里查看Java字节码

    要理解 Java 字节码,比较推荐的方法是自己尝试编写源码对照字节码学习.其中阅读 Java 字节码的工具必不可少.虽然javap可以以可读的形式展示出.class 文件中字节码,但每次改动源码都需调 ...

  7. 【Spring】利用AOP来做系统性能监控

    需求: 假设已经有了一些类,现在想统计每个方法调用花了多长时间,该怎么做? 思路: 我第一个想法就是去每个方法执行前后记录一下当前的时间戳,然后相减统计到日志. OK,没问题,那么这样做合理吗? 首先 ...

  8. 初学Flask(1)

    今天在学习Flask,边看官方文档一边动手运行例子,以注释的形式写了一些笔记,分享给大家. Flask官方文档,快速入门: ex1: #coding:utf-8 ################### ...

  9. python——第二天

    类和实例: 创建实例是通过类名+()实现 但是!可以自由地给每个实例变量绑定新的属性(特指以前在类定义中没有的属性) __init__方法用来给类定义必要的几个属性,第一个参数永远是self type ...

  10. CentOS6.5菜鸟之旅:安装ATI显卡驱动

    一.前言 自从安装了CentOS,我的显卡就没消停过,一直在彪高温而且噪音特别大,于是决定上网搜索解决办法.下面记录下来以供日后查阅. 二.安装fglrx driver(ATI/AMD 显卡的linu ...