第7章 更加抽象
本章将会介绍如何创建对象,以及多态、封装、方法、特性、超类以及继承等概念。
对象的魔力
多态
多态字面意思为有多种形态,在编程语言中意味着即使不知道对象的具体类型,也可以进行某些操作,并且根据对象类型的不同而表现出不同的行为。
为什么会需要多态呢?如下尝试用一个例子来说明多态的好处(原书例子有点不好理解,这里新举一个例子)。假定某购物网站的商品对应程序类Product, 现在要求计算某个顾客购物车内的所有物品,计算每个物品的价格可能会有如下方法:
def getPrice(p):
if p.type == 'type1':
return p.price
elif p.type == 'type2':
return p.price - discountCalculate(p) #促销商品
elif p.type == 'type3':
return p.price + taxCalculate(p) #附加税商品
当出现一种价格计算方式的时候我们要修改代码,增加新的分支,分支多的时候会变得难于管理。如果项目已经被集成使用修改会更加麻烦。这时候多态的好处就体现出来了,每种商品都有自己的价格,那么只要调用product.getPrice就能调用具体每种商品的价格计算方法。
在Python中我们已经接触过的多态的运用,比如:
>>> o = 'abc'
>>> o.count('a')
1
>>> o = ['a', 'b', 'c']
>>> o.count('a')
1
其实不只是方法,很多内建的运算符都实现了多态:
>>> 'a' + 'b'
'ab'
>>> 1+2
3
封装
封装能对外部作用域隐藏内部实现细节只知道如何调用即可。
继承
继承是想要扩展功能,有不想写重复的做法。
类和类型
类到底是什么
类即类型或者种类,所有的对象都属于某一个类,称为类的实例。比如,鸟是鸟类的实例,鸟类有可能有很多子类(subclass)如“百灵鸟”;反过来,鸟类是“百灵鸟”的超类(superclass)。
在面向对象程序设计中,所有实例都包含类的方法,所有子类都必须继承或者重载超类的方法,并且子类中可以新增自己特有的方法。
创建自己的类
__metaclass__ = type
class Person:
def setName(self, name):
self.name = name
def getName(self):
return self.name
def greet(self):
print 'Hello, I am %s' % self.name
foo = Person()
bar = Person()
foo.setName('foo')
print foo.getName()
bar.name = 'bar'
print bar.name
foo.greet()
$ python class.py
foo
bar
Hello, I am foo
如上为类的创建和使用,注意第一行代码:声明新式类需要在模块或者脚本开始地方放置该行(不细究)。
特性、函数和方法
class Bird:
song = 'Squaawk'
def sing(self):
print self.song
bird = Bird()
bird.sing()
singsong = bird.sing
singsong()
$ python class.py
Squaawk
Squaawk
某些编程语言觉得直接通过'对象.属性'的方式访问破坏了封装性,但Python并不支持私有形式(尽管有一些小技巧可以达到私有特性的效果,不是目前学习的重点先跳过)。
类的命名空间
所有位于class中间的代码都在类命名空间中,这个命名空间可以由所有的类的实例访问。需要注意类的定义就是执行代码块,因此如下定义方式虽然傻但是是可以的:
class C:
print 'Class C is defined...'
$ python class.py
Class C is defined...
在类中可以定义供所有成员访问的变量,如下是一个例子(在Java中叫类变量,通常在定义指明MemberCounter.members;而在Python中定义是直接写在members类中。两者调用的时候同样使用MemberCounter.members):
class MemberCounter:
members = 0
def init(self):
MemberCounter.members += 1
m1 = MemberCounter()
m1.init()
print m1.members
m2 = MemberCounter()
m2.init()
print m2.members
$ python class.py
1
2
调用超类
子类可以扩展超类的定义,既可以覆写超类的方法,也可以直接使用超类方法而不用自己再定义:
class Filter():
def init(self):
self.blocked = []
def filter(self, sequence):
return [x for x in sequence if x not in self.blocked]
class SPAMFilter(Filter):
def init(self):
self.blocked = ['SPAM']
filter = Filter()
filter.init()
print filter.filter([1,2,3])
spamFilter = SPAMFilter()
spamFilter.init()
print spamFilter.filter(['SPAM','SPAM', 'zip','lotus','SPAM']);
$ python class.py
[1, 2, 3]
['zip', 'lotus']
Filter类的blocked为[]因此不会对任何内容进行过滤;而在子类SPAMFilter中,我们覆写了init方法,仍旧使用超类的filter方法,因此所有'SPAM'的字符串都被过滤掉了。
介绍几个和类相关的常用方法
s = SPAMFilter()
#想要知道一个类是否是另一个类的子类:
print issubclass(SPAMFilter, Filter) #True
print issubclass(Filter, SPAMFilter) #False
#想要知道一个类已知基类(们):
print SPAMFilter.__bases__ # (<class __main__.Filter at 0x1004b8c18>,)
#想要知道一个对象是否是一个类的实例:
print isinstance(s, SPAMFilter) #True
print isinstance(s, Filter) #True
#想要知道一个对象属于哪个类
print s.__class__ #__main__.SPAMFilter
多个超类
类可以继承自多个超类,除非非常熟悉否则应该尽量避免使用,如下是一个多重继承的例子:
class Calculator:
def calculate(self, expression):
self.value = eval(expression)
class Talk():
def talk(self):
print 'My value is: %s' % self.value
class CalculatorTalk(Calculator, Talk):
pass
ct = CalculatorTalk()
ct.calculate('2+3')
ct.talk()
$ python class.py
My value is: 5
接口和内省
接口的概念和多态有关,即处理多态对象时候只需要关系公开的方法和特性,因此需要检测对象是否真正实现了方法和特性。如下介绍两个检测的方法,第一个是所需方法是否存在,第二个是是所需方法否可调用。
hasattr(ct, 'talk') # True
hasattr(ct, 'test') # False
callable(getattr(ct, 'talk', None)) # True
callable(getattr(ct, 'test', None)) # False
网友评论