文章目录
更加抽象
前几章节介绍了python主要的内建类型,以及内建函数和标准库的用法,还有定义函数的方法.现在看来,还差一点,创建自己的对象,这正是本章要介绍的内容.
7.1 对象的魔力
对象的定义
在面向对象程序设计中,对象,基本上可以看做数据(特性)以及一系列可以存取,操作这些对象的方法所组成的集合.
对象的优点
- 多态(ploymorphism):意味着可以对不同的类的对象使用相同的操作
- 封装(encapsulation):对外部世界隐藏对象的工作细节
- 继承(inheritance):以通用的类为基础建立专门的类的对象
7.1.1 多态
多态的定义
- 多态术语来源于希腊语,意思是有多种形式
- 意味着就算不了解变量所引用的对象类型是什么,还是能对它进行操作,而它也会根据不同的对象(或类)类型的不同表现出不同的行为
多态和方法
- 绑定到对象特性上的函数,叫做方法
- 不需要检测类型,只需要知道x有个叫做xfunc的方法,接受某个参数,并有某种功能即可
多态的多种形式
- 很多内建的运算符和函数都有多态的性质,它们的参数可以是任何支持此种运算或者操作的对象.
- 事实上,唯一能毁掉多态的就是使用函数显示地类型检查,比如type isinstance issubclass函数等,如果可能,尽量避免使用这些毁掉多态的形式
7.1.2 封装
封装的定义
- 封装是指向函数中其他部分隐藏对象的具体实现细节的原则
- 多态可以让用户对于不知道是什么类的对象进行方法调用,而封装是可以不用关心对象是如何构建的而直接使用
封装的优势
- 变量可以作为特性(attribute)储存在类/对象之中
- 对象有自己的状态(state),由自己的特征描述
- 对象的方法有访问特性的权利
7.1.3 继承
以通用的类为基础建立专门的类的对象
7.2 类和类型
7.2.1 类到底是什么
- 类,可以将它多少地视为种类或者类型的同义词
- 类也是一种对象
- 所有的对象都属于某一个类,被称为类的实例
- 当一个对象所属的类是另外一个对象所属类的子集时,前者就被称为后者的子类(subclass),相反,后者是前者的超类(superclass)
命名规则
日常中,可能经常用复数来描述对象的类,python中,习惯使用单数名词,并且首字母大写
注意
在旧版本python中,类和类型有明显的区别,内建的对象是基于类型的,自定义的对象是基于类的.可以创建类但不能创建类型.最近版本中,基本类型和类之间界限开始模糊,乃至python3的最新版本中,默认全部为新式类,可以创建内建类型的子类
7.2.2 创建自己的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
__metaclass__=type #确定使用新式类,在python3中不需要使用此则声明,因为已经默认新式类了 class Person: def setName(self,name): self.name=name def getName(self): return self.name def greeting(self): print "Hello world! I'm %s"% self.name |
7.2.3 特性,函数和方法
自己参数
- self参数实际上正是函数和方法的显式区别,方法(或称为绑定方法)将他们的第一个参数绑定到所属的实例上,在调用时,无需显式的提供这个参数
- 当然也可以在外部定义一个函数,然后将方法绑定到其上,这样就没有特殊的self参数了
- self参数并不依赖于调用的方法,即使你将一个名字绑定到上面,提供参数时也无需self的显式提供
再论私有化
一般来说,作为封装的辅助和保证,面向对象语言应该提供私有(private),但是python并不直接支持私有方式,要靠程序员在外部把握进行特征修改的时机
以下的一些小技巧,可能间接的达到私有的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Secretive: def _inaccessible(self): print "you should not use me if i have a _ prefix" def __inaccessible1(self): print '''Ah ha, now you can use me as __inacessible1 because i'm transfered to _Secretive_=inacesssible1. Oh no, you mustn't use it...''' >>> s=Secretive() >>> s._inacessible() you should not use me if i have a _ prefix >>> s.__inacessible1() Traceback (most recent call last): File "<pyshell#112>".line4, in ? s.__inacessible1() >>> s._Secretive__inacessible1() Ah ha, now you can use me as __inacessible1 because i'm transfered to _Secretive__inacesssible1. Oh no, you mustn't use it... |
前面带有下划线的语句都不会被from module import *
导入
7.2.4 类的命名空间
下面两个语句(几乎)等价:
1 2 3 |
def foo(x): return x*x foo = lambda x:x*x |
所有位于class语句都在特殊的命名空间内执行–类命名空间
这个命名空间可由类内所有成员访问
7.2.5 指定超类
形式
1 2 |
class Subclass(Superclass): |
两个要点
- 可以使用新定义的方式重写方法的定义
- 可以继承一些定义避免全部重写
7.2.6 检查继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
issubclass(superclass,subclass or a tuple of many subclasses)->bool isinstance(object, class or a tuple of many classes)->bool #使用以上这些检查并不是好习惯,使用多态会更好一些 #想要知道一个子类所属的超类(们),可以使用__bases__属性 >>> subclass.__bases__ (<class __main__.class at memoryaddress>[,<>..]) #想知道一个对象属于哪个类,可以使用__class__属性 >>> instance.__class__ <class __main__.class at memoryaddress> #如果使用__metaclass__=type或从object继承来定义新式的类,那么可以是type(instance)来查看实例所属的类 |
7.2.7 多个超类
- 一个子类可以从多个超类继承–这种行为可以称为多重继承
- 如果一个方法从多个超类继承时,必须注意继承的顺序,先继承的方法会重写后继承的类的方法
- 如果超类们又共享一个超类,那么在查找给定方法或属性时,访问超类的顺序称为(MRO,Method Resolution Order),使用的算法相当复杂,工作的很好,无需关心
7.2.8 接口和内省
接口
- 接口的概念与多态有关,在处理多态对象时,只要关心他的接口(或称为协议),也就是公开的方法或特性
- 在python中,不需要显式的编写接口,可以在使用对象时假定他可以实现你要求的行为,如果不能实现就会引起错误
- 除了调用方法然后期待一切顺利之外,还可以检查所需方法是否已经存在.如果不存在还要做一些其他的事情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
>>> hasattr(tc,'talk') True >>> hasattr(tc,'fnord') False #以下这些是python2 >>>callable(getattr(tc,'talk',None)) True >>>callable(getattr(tc,'fnord',None)) False #以下这些是python3 >>>hasattr(getattr(tc,'talk',None),'__call__') True >>>hasattr(getattr(tc,'fnord',None),'__call__') False #与getattr对应的setattr使用如下 >>> setattr(tc,'name','python') >>> tc.name 'python' |
要查看对象内所有储存的值,可以使用__dict__
特性,要想真正找到对象由什么组成,可以看看inspect
模块
7.3 一些面向对象的思考
- 将属于一类的对象放在一起,如果一个函数操纵全局变量,那么两者最好都在类内作为特征和方法出现
- 不要让对象过于亲密,方法应高只关心自己的实例
- 要小心继承,尤其是多重继承,但在某些情况下会变得复杂,它难以正确使用,也难以调试
- 简单就好,让你的方法小巧:一般来说,多数方法都应该能在半分钟之内读完并且理解
- 当考虑需要什么类以及应当有什么方法时,尝试以下方法
- 写下问题的描述,把所有名词动词以及形容词加下划线
- 对于所有的名词,用于可能的类
- 对于所有的动词,用于可能的方法
- 对于所有的形容词,用于可能的特性
- 把所有的方法和特性分配到类