第七章:更加抽象

创建自己的对象(尤其是类型或者被称为类的对象)是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

例子一目了然,在调用foosetNamegreet函数是,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__
(,)

同样,还能使用isinstance方法检查一个对象是否是一个类的实例:

>>>s=SPAMFilter()
>>>isinstance(s,SPAMGFilter)
True

使用isinstance并不是个好习惯,使用多态会更好一点
如果想知道一个对象属于哪个类,可以使用__class__特性:

>>>s.__class__

如果使用__metaclass__=type或从object继承的方式来定义新式类,那么可以使用type(s)查看实例所属的类。

多个超类

如果一个方法从多个超类继承(也就是说你有两个具有相同名字的不同方法),那么必须注意一下超类的顺序(class语句中):先继承的类中的方法会重写后继承的类中的方法。

本章小结:

本章涉及的新函数如表:

函数 描述
callable(object) 确定对象是否可调用(比如函数或者方法)
getattr(object,name[,default]) 确定特性的值,可以选择提供默认值
hasattr 确定对象是否有给定的特性
setattr(object,name,value) 设定对象的给定特性为value
random.choice(sequence) 从非空序列中随机选择元素

在轻率的进军python特殊方法的魔法阵(第9章)之前,让我们先喘口气。看看介绍异常处理的简短的一章。

痛苦煎熬的一章总算结束了,希望作者所谓的魔法阵能让我明白一些顶层,类似原型链继承等方面的知识吧。