第七章:更加抽象
创建自己的对象(尤其是类型或者被称为类的对象)是python的核心概念——非常核心,事实上,python被称为面向对象的语言。
熟悉面向对象程序设计的读者应该也了解构造函数。本章不会提到构造函数,关于它的完整讨论,请参见第9章。
这一章因为如上的风格,请参加后面的章节,因此有些例子根本就不懂其实现的原理。和之前看的《js高程》一书的叙述感觉相差甚远啊!看来这本书从这一章开始就更难从底部原理去理解。有必要参考其他书了。
对象的魔力
属于对象(object)基本上可以看做数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。使用对象替代全局变量和函数的原因有可能很多。
其中,对象最重要的优点包括以下几个方面:
多态(Polymorphism):意味着可以对不同类的对象使用同样的操作,他们会像被“施了魔法一般”工作。(按照我个人的理解,就是同一个函数或方法,会对参数对象的类型不同,都能获得相应的操作结果。)
封装(Encapsulation):对外部世界隐藏对象的工作细节。
继承(Inheritance):以通用的类为基础建立专门的类对象。
在作者来看,面向对象程序设计的最有趣的特性就是多态。
多态
多态意味着就算不知道变量所引用的对象类型是什么,还是能对它进行操作,而它也会根据对象(或类)类型的不同而表现出不同的行为。
绑定到对象特性上面的函数称为方法(method)。我们已经见过字符串、列表和字典方法。
类和类型
类到底是什么?
所有的对象都属于某一个类,称为类的实例(instance)。
当一个对象所属的类是另外一个对象所属类的子集时,前者就被称为后者的子类(subclass),所以,“百灵鸟类”是“鸟类”的子类。相反,“鸟类”是“百灵鸟类”的超类(superclass)或者叫基类。
书中这章很多地方都是详见第9章……真无语……安排不爽
创建自己的类:
__metaclass__ = type #确定新式类(详见第9章)
class Person:
def setName(self,name):
self.name = name
def getName(self,name):
return self.name
def greetName(self,name):
print "hello,world,i'm %s"%self.name
class
语句会在函数定义的地方创建自己的命名空间。
self
参数看起来有点奇怪,它是对象自身的引用。它是什么对象?
>>>foo = Person()
>>>bar = Person()
>>>foo.setName('michael')
>>>bar.setName('qiuqiu')
>>>foo.greet()
hello,world,i'm michael
>>>bar.greet()
hello,world,i'm qiuqiu
例子一目了然,在调用foo
的setName
和greet
函数是,foo
自动将自己作为第一个参数传入函数中——因此形象地命名为self
。对于这个变量,每个人可以自己命名。只是因为它总是对象自身,所以习惯上总是叫做self
。
*显然,这就是self
的用处和存在的必要性。没有它的话,成员方法就没法访问他们要对其特性进行操作的对象本身了。”
特性、函数和方法
self
参数事实上正是方法和函数的区别。方法(更为专业点可以称为绑定方法)将它们的第一个参数绑定到所属的实例上,因此你无需显示提供该参数。当然,也可以将特性绑定到一个普通的函数上。
第9章中,将会介绍有关属性(property)的知识,它是访问器(accessor)最有利的替代者。
类的命名空间
所有位于class
语句中的代码都在特殊的命名空间中执行——类命名空间(class namespace)。这个命名空间可由于类内所有成员访问。
并不是所有Python程序员都知道类的定义其实就是在执行代码块!!!
比如,在类的定义区并不是只限定只能使用def
语句:
>>>class C:
print 'class c being defined'
class c being defined
>>>
这看起来有些傻,再看一个栗子:
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
那么在实例中重绑定members
特性呢?
>>>m1.members = "two"
>>>m1.members
"two"
>>>m2.members
2
新menmbers
值被写到了m1`特性中,屏蔽了类范围内的变量。这跟函数内的局部变量和全局变量的行为十分类似!
检查继承
如果想要查看一个类是否是另一个的子类,可以使用内建的issubclass
函数:
>>>issubclass(SPAMFilter,Filter)
True
>>>issubclass(Filter,SPAMFilter)
Flase
如果想要知道已知类的基类(们),可以直接使用它的特殊特性__bases__
:
>>>SPAMFilter.__bases__
(<class __main__.Filter at 0x171e40>,)
同样,还能使用isinstance
方法检查一个对象是否是一个类的实例:
>>>s=SPAMFilter()
>>>isinstance(s,SPAMGFilter)
True
使用isinstance
并不是个好习惯,使用多态会更好一点
如果想知道一个对象属于哪个类,可以使用__class__
特性:
>>>s.__class__
<class __main__.SPAMFilter at 0x0330EB58>
如果使用__metaclass__=type
或从object
继承的方式来定义新式类,那么可以使用type(s)
查看实例所属的类。
多个超类
如果一个方法从多个超类继承(也就是说你有两个具有相同名字的不同方法),那么必须注意一下超类的顺序(class
语句中):先继承的类中的方法会重写后继承的类中的方法。
本章小结:
本章涉及的新函数如表:
函数 | 描述 |
---|---|
callable(object) |
确定对象是否可调用(比如函数或者方法) |
getattr(object,name[,default]) |
确定特性的值,可以选择提供默认值 |
hasattr |
确定对象是否有给定的特性 |
setattr(object,name,value) |
设定对象的给定特性为value
|
random.choice(sequence) |
从非空序列中随机选择元素 |
在轻率的进军python特殊方法的魔法阵(第9章)之前,让我们先喘口气。看看介绍异常处理的简短的一章。
痛苦煎熬的一章总算结束了,希望作者所谓的魔法阵能让我明白一些顶层,类似原型链继承等方面的知识吧。