python进阶六_封装与继承(一)


 

我们先来看一个简单的例子:

#author liygcheng 2014-04-04
#object-oriented python 

class Animal():
	def __init__(self):
		self.__name = Animal
		print(self.__name)
	def accessMethod(self):
		print(I am a animal,and my name is:)
		print(self.__name)
		print(
 The Additional message is:)
		self.__innerAccess()
	def __innerAcess():
		print(this can not be seen!)
	@staticmethod
	def  staticMethod():
		print(this is a  static method!)
	@property #python中的属性设置
	def name(self):
		return self.__name
	@name.setter
	def name(self, value):
		self.__name = value
	@name.deleter
	def name(self):
		del self.__name	

例子中的其他问题暂且先不考虑,因为我在编译的时候,出了问题,总是会出现“IndentationError: unindent does not match any outer indentationlevel”这样的错误,在网上一查之后发现原来是混用了Tab键和空格。。下面我们开始看看代码中出现的一些知识点:

私有函数和静态方法在之前的类介绍中详细阐述过,此不再赘述,这里我们把重点放在这个@property这里,

在C++或者Java中,我们经常会遇到setter和getter,在这里@property的作用类似,需要注意的是,这里面的同一属性的三个函数要同名(本例中指的是name),当我们在运用的时候,就可以根据需要,例如是否只读等进行设置。其实除此之外,在python2.6之前,还有一种绑定的方法,先来看一个例子:

/

那么另一种绑定的方法是什么呢?我们来看一下:

/

可以发现,这两种效果是一样的,但是在python中,我们推荐使用前者。

2.继承

关于继承,可能更多的,我们可能是在C++,Java中第一次见到,但是面向对象的思想是不变的,在python中也一样,可能我们经常会在各种专业书籍上发现is-a和has-a,继承关系就是典型的is-a(组合是has-a).在继承中,我们要注意一些事情:

1. 在能用组合解决问题的时候,优先考虑组合

2. 继承者基类的构造方法不会自动调用,需要在子类的构造方法中明确调用

3. Python总是沿着继承树从底至上进行搜索

来看一个简单的例子:

/

/

注意:当用subline Text 运行的时候出现”Decodeerror-output not utf-8”这个错误的时候,是因为中文的原因,直接在Subline Text 的python插件中修改文件即可,具体操作如下:

1. 找到Python.sublime-build文件,具体位置如我的在:

C:UsersliygchengAppDataRoamingSublime Text 2PackagesPython

2. 在该文件最后加上一栏“encoding”:”cp936”,如下,别忘了上一栏最末的逗号

/

1. 至于cp936是什么,维基百科的解释是这样的:

微软的CP936通常被视为等同GBK,连 IANA 也以“CP936”为“GBK”之别名[1]。事实上比较起来, GBK 定义之字符较 CP936 多出95字(15个非汉字及80个汉字)

几个值得思考的问题:

1. 当我们在python中想要调用父类的某个方法的时候,如果总是使用非绑定的类方法,即通过类名引用,并在参数列表中,引入的这个待绑定的对象的时候,如果一个子类的父类发生了变化,必须把所有的父类全部更换,软件工程的直觉告诉我们,这样是不可取的,因为程序员总是偷懒的,稍微查了一下,发现python自从2.2开始之后添加了一个关键字super,来解决这个问题:

官方给出的说明是这样的:

super(type[, object-or-type])

Return the superclass of type. If the second argumentis omitted the super object
returned is unbound. If the second argument is an object,isinstance(obj, type)
must be true. If the second argument is a type, issubclass(type2,type) must be
true. super() only works for new-style classes.

A typical use for calling a cooperative superclassmethod is:

class C(B):
def meth(self, arg):
super(C,self).meth(arg)

New in version 2.2.

简而言之,我们可以这么理解super(type,arg).method(..),当执行这条语句的时候,首先找到类type的父类,然后将类type或其子类的对象arg提升为类type的父类类型对象,然后调用类type的父类方法method.来看一个例子:

/

 

2 多重继承的问题

我们知道,在Java中不存在多重继承,直接改用接口,在C++中,我们引入了虚基类,那么在python中是否会有这个问题呢?

我们先来看一个例子:

/

在这个例子中,我们发现,单独使用super或者单独使用非绑定的函数都会正确初始化,但是当我们混用了super与非绑定的方法,发现存在父类被调用多次的现象,因此需要注意。

下面来分析一下多重继承的继承树结构,简单概括继承树如下图(省去根Object):

/

我们根据之前的结果得知,类F初始化的路径为:F-> C -> D -> A,咋一看,我们估计只能判断这是从底纸上的初始化,至于具体使用了何种算法还不得而知,po主查了一下资料,发现python多重继承在2.3之前是基于深搜算法的,但是在之后采用的是C3算法搜索继承树,

(Mro, 即Method resolution order 主要用于在多重继承时判断调用属性路径),至于为什么要用C3算法,主要是考虑到继承时声明的父类顺序,可以这么简单的理解,C3算法就是要先确定一个线性序列,如下:

1. 如果是继承1个基类

class B(A)

这个时候的mro序列即为mro(B) = [B,A]

2. 如果继承至多个基类

class B(A1,A2,A3,….)

这时mro序列为

mro(B) =

[B] + merge(mro(A1),mro(A2),….,[A1,A2,A3,……])

可以这么说,这么的合并merge操作是整个C3算法的核心,遍历执行merge操作的序列,如果一个序列的第一个元素,在其他序列中也是第一个元素,或不在其他序列出现,则从所有执行merge操作序列中删除这个元素,合并到当前的mro中。merge操作后的序列,继续执行merge操作,直到merge操作的序列为空。如果merge操作的序列无法为空,则说明不合法。

对之前的例子,我们来分析一下继承树://0标志基类object

mro(A) =[A,0]

mro(B) =[B,0]

mro(C) = [C]+ mro(A) = [C] + [A,0] = [C,A,0]

mro(D) =[D] + mro(A) = [D] + [A,0] = [D,A,0]

mro(E) = [E] +merge(mro(B) ,mro(C),[B,C])

= [E] + merge([C,A,0],[B,0],[B,C])

=[E,B] + merge([C,A,0],[0],[C])

=[E,B,C] + merge([A,0],[0])

=[E,B,C,A] + merge([0])

=[E,B,C,A,0]

Mro(F) = [F] + merge(mro(C),mro(D),[C,D])

=[F] +merge([C,A,0],[D,A,0],[C,D])

=[F,C] + merge([A,0],[D,A,0],[D])

=[F,C,D] +merge([A,0],[A,0])

=[F,C,D,A] +merge([0],[0])

=[F,C,D,A,0]

这下总明白为什么类F初始化的顺序是F-> C -> D -> A 了吧



评论关闭