6、Python-抽象的类
类这个概念是学习面向对象语言要掌握的知识点,Python做为其一也没啥例外,概念上都差不多,只是在类的定义和使用格式上有些许的差别。这里主要介绍一下Python类的定义和使用方法,并且回顾一下使用类对象的好处。
1、类和对象
“面向对象程序设计”中对象指的是什么?对象基本上可以看做数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。特性只是作为对象的一部分变量,方法则是存储在对象内的函数。绑定在对象内的方法和其他函数的区别在于方法总是将对象的本身作为自己的第一个参数,这个参数一般为self。
类是对象的一个集合,而对象则是类的一个实例。当一个对象所属的类是另一个类的子集时,我们称前面的类为子类,后面的是超类。
2、类的抽象-多态、封装和继承
2.1 多态
可以对不同的类对象使用相同的方法。这就意味着,就算不知道变量所引用的对象类型是什么,还是能对他进行操作,而它也会根据对象类型的不同而表现出不同的行为。函数type、instance、issubclass能够毁掉类的多态性。
2.2 封装
封装是类对外隐藏不必要的细节。类中的状态(特性)只对自己的方法可用。在Python中,所有的特性都是公开可用的,但是程序员应该在直接访问对象状态时谨慎行事,因为他们可能无意中使得这些特性在某些方面不一致。多态可以让用户对于不知道是什么类的对象进行方法的调用;而封装是可以不关心对象是如何构建的而直接进行使用。
2.3 继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。一个类可以是一个或多个类的子类。子类从超类继承所有的方法。
2.4 再说封装,私有化
1 class Person: 2 def setName(self, name): 3 self.name = name 4 def getName(self): 5 return self.name 6 def greet(self): 7 print "Hello, world! I'm %s." % self.name
有些专家认为对象的状态对于外界应该是完全隐藏的,不可直接对类中的特性进行访问。为什么要对外部世界隐藏,如果能够直接访问Person中的name特性时就不用再使用setName和getName方法了。关键在于Person对象可能会在其他对象更改自己的名字时做一些其他的操作,这应该是setName方法的一部分。但是直接使用c.name设定的话就什么也不会发生。为了避免这类事情的发生,应该使用私有的特性,外部对象无法访问,但getName和setName等访问器可以访问这些特性。
为了将类中的方法和特性变为私有,只需在它的名字前面添加双下划线即可。其实在类的内部定义中,所有以双下划线开始的名字都被“翻译”成前面是单下划线和类名的形式_ClassName__method。
3、类的命名空间
类名就可以认为是该类的命名空间,class中的所有代码都在该特殊的命名空间中执行,命名空间可以类中的所有成员访问。
>>> class MemberCounter: members = 0 def init(self): MemberCounter.members += 1 >>> m1 = MemberCounter() >>> m1.init() >>> MemberCounter.members 1 >>> m2 = MemberCounter() >>> m2.init() >>> MemberCounter.members 2 >>> m1.members 2 >>> m2.members 2
上面的例子中,在类的作用域中定义了一个可供所有成员访问的变量,用来计算类的成员个数。类作用域中的变量可以被所有的类实例对象访问。
4、超类
类名括号内的类称为该类的超类。如下实例,类Filter是类SPAMFilter的超类。
1 >>> class Filter: 2 def init(self): 3 self.blocked = [] 4 def filter(self, sequence): 5 return [x for x in sequence if x not in self.blocked] 6 7 >>> class SPAMFilter(Filter): # SPAMFilter is a subclass of Filter 8 def init(self): # Overrides init method from Filter superclass 9 self.blocked = ['SPAM']
内建issubclass函数用来查看一个类是另一个类的子类。
1 >>> issubclass(SPAMFilter, Filter) 2 True 3 >>> issubclass(Filter, SPAMFilter) 4 False
特性__bases__获取类的基类。
1 >>> SPAMFilter.__bases__ 2 (<class __main__.Filter at 0x012203B0>,) 3 >>> Filter.__bases__ 4 ()
isinstance函数检查一个对象是否是一个类的实例。
1 >>> s = SPAMFilter() 2 >>> isinstance(s, SPAMFilter) 3 True 4 >>> isinstance(s, Filter) 5 True 6 >>> isinstance(s, str) 7 False
对象的__class__特性获取该对象属于哪个类。
1 >>> s.__class__ 2 <class __main__.SPAMFilter at 0x012203E8>
多重继承,一个类有多个超类。
注意:如果一方法从多个超类继承,那么必须注意一下超类继承的顺序,先继承的类中的方法会重写后继承类中的方法。
5、接口和内省
接口就是类中公开的方法和特性。内省就是通过一些函数来检查类中方法和特性的有效性。
hasattr(obj, 'method')
hasattr(obj, 'features')用来检查obj对象中是否有method方法和features特性。
getattr(obj, 'method', None)
getattr(obj, 'features', None)获取obj对象中method方法和features特性的属性,在属性不存在的情况下提供默认值None。与之相对应的是setattr函数。
setattr(obj, 'method', value)
setattr(obj, 'features', value)
callable(getattr(obj, 'method')检查对象中method方法是否可以调用。
6、如何设计一个类。
(1)写下问题的描述(程序要做什么?)把所有的名词、动词、形容词加下划线;
(2)对于所有的名词,用作可能的类名;
(3)对于所有的动词,用作可能的方法;
(4)对于所有的形容词,用作可能的特性;
(5)把所有的方法和特性分配到类中。
现在已经有了面向对象模型中类的草图,还要考虑类和对象之间的关系及它们的作用,可以用以下步骤来精炼模型:
(1)写下一系列的使用实例,也就是程序应用时的场景,试着包括所有的功能;
(2)一步步考虑所有的实例,完善模型包括所有的功能和特性,直到满意。