您的位置:首页 > 其它

第七章 更加抽象

2017-05-01 13:45 253 查看
类和对象是面向对象编程的两个主要方面。类创建一个新类型,而对象是这个类的 实例 。这类似于你有一个
int
类型的变量,这存储整数的变量是
int
类的实例(对象)。
    (比如鸟就是“鸟类”的实例,鸟类有很多子类,比如“百灵鸟类”就是一个“鸟类”的子类,“鸟类”是“百灵鸟”的超类)

所有子类都有父类的方法,定义子类只是个定义更多(或者重载已经存在的)的方法的过程

对象可以使用普通的 属于 对象的变量存储数据。属于一个对象或类的变量被称为域。对象也可以使用 属于 类的函数来具有功能。这样的函数被称为类的方法。这些术语帮助我们把它们与孤立的函数和变量区分开来。域和方法可以合称为类的属性。

1.多态:  (百度百科)多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作,也就是说,父亲的行为像儿子,而不是儿子的行为像父亲。 
     把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异。简单的说,建立一个父类对象的引用,它所指对象可以是这个父类的对象,也可以是它的子类的对象。

(python基础教程)就算不知道变量所引用的对象类型是什么,还是能对他操作,它会根据对象(或类)类型的不同表现出不同的行为

(个人理解):把子对象都看作父对象,当赋值时,根据所赋的值,通过父对象调用不同的子对象的方法

2.封装:(百科)封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
(个人理解):就是把对象的工作细节隐藏起来 ,把相关类的相关变量作为特性,封装在类内,对象的方法可以改变对象的特性。

特性

特性是对象内部的变量

对象的状态由它的特性来描述,对象的方法可以改变它的特性

可以直接从对象外部访问特性

3.继承:以通用的类为基础建立专门的类对象(子类型)

1.多态和方法
让用户对不知道是什么类的对象进行方法调用
不需要检测类型,只需要知道x有个叫做count的方法
例如‘+’  无需知道什么加什么,‘+’都可以操作
2.封装
对对象进行抽象,调用时不用关心其他的东西。作为   特性
  存储,“封装”在对象内
对象有自己的状态,状态由他的特性(比如名字)来描述。对象的方法可以改变特性。
3.继承
         

7.2.2创建自己的类

旧式类和新式类:除非是3.0之前版本红默认附带的代码,否则无必要使用旧式类
在2.0中要使用新式类,要在模块或者脚本开始的地方加入赋值语句__metaclass__=type  或者继承新式类
但是在3.0中,只有新类了

7.2.3   特性,函数和方法

self参数是对对象自身的引用,作为第一个参数绑定到所属实例上(因此你无需在调用对象方法的时候显示的提供该参数),好让让成员方法访问他们要对其操作的对象本身

例如:foo是Person的实例,foo.greet()可作为Person.greet(foo)的简写

self参数正是方法和函数的区别
1.你可以把有self方法绑定到一个普通的函数上:这样就不会有self参数了
2.self参数不依赖于调用方法的方式,       可以随意使用其他变量引用同一个方法:比如birdsong=bird.song     ,这样仍然会对self(bird)参数进行访问。

2.再轮私有化  私有特性    
注:其实python并没有真正的私有化支持

默认情况下,程序可以从外部访问一个对象的特性。
>>>c.name
'Sir Lancelot'
>>>c.name='Sir gumby'
>>>c.getname()
'Sir gumby'


但是,有的时候,希望   完全隐藏(不可访问)   。此时,就要使用私有特性(方法)这样,外部对象无法访问,但是内部的方法能够访问。必须得通过使用同一个对象的方法             访问器              访问 ——————访问器的替代者是属性,见第九章

1.只要在名字前面加上双下划线

原理是类的内部定义中,所有以双下划线开始的名字都被翻译成了前面加上单下划线和类名的形式

例如

class   Secretive:

             def    __inacessible(self):

  变为

              def    _Secretive.__inacessible(self)

所以,其实还是可以在类外访问这些私有类

也可以使用单下划线让其他对象不要访问内部数据  和双下划线是两种级别的私有性

附:1、 _xx 以单下划线开头的表示的是protected类型的变量。即保护变量只能允许其本身与子类进行访问。若内部变量标示,如: 当使用“from M import”时,不会将以一个下划线开头的对象引入 。
2、 __xx 双下划线的表示的是私有类型的变量。只能允许这个类本身进行访问了,连子类也不可以用于命名一个类属性(类变量),调用时名字被改变(在类FooBar内部,__boo变成_FooBar__boo,如self._FooBar__boo)
3、 __xx__定义的是特殊方法。用户控制的命名空间内的变量或是属性,如init , __import__或是file 。只有当文档有说明时使用,不要自己定义这类变量。这些特殊方法会在特殊情况下根据名字被python调用。几乎没有直接调用他们的必要。 (就是说这些是python内部定义的变量名)
在这里强调说一下私有变量,python默认的成员函数和成员变量都是公开的,没有像其他类似语言的public,private等关键字修饰.但是可以在变量前面加上两个下划线"_",这样的话函数或变量就变成私有的.这是python的私有变量轧压(这个翻译好拗口),英文是(private name mangling.) **情况就是当变量被标记为私有后,在变量的前端插入类名,再类名前添加一个下划线"_",即形成了_ClassName__变量名.**

7.2.4     类的命名空间    (这里说明self.attr和attr的区别)
类的定义其实就是执行代码块,而class中的代码都在特殊的命名空间中执行----类命名空间,可由类内所有成员访问
一个特别的例子
一定注意这里,这里在类的作用域内定义了一个可以被所有成员(所有实例)访问的变量(member)  (和方法一样)这个变量是属于类的
  如果这里打的是self.member      就只能被相应的实例访问(这个变量是属于实例的)     屏蔽了类访问内的变量,和函数中的局部和全部变量的概念类似当member和self.member都有的时候,使用   m1.member
  来引用  优先 使用self.member(实例自己的)    (如果这里非要用访问类的member,可以用MemberCount.member   来全名显式制定)

这里我的理解是,相当于在类命名空间内还有一个实例命名空间(类似局部变量),以self.attr  定义,实例命名空间内的变量屏蔽类命名空间的同名变量

另外,如果member是写在类方法函数里面的,那这个member就只是一个局部变量而已
>>> class MemberCount:
member=0
def init(self):
MemberCount.member+=1       #注意这里,init改变的是类的特性,不是改变实例的变量(self.member),所以,m2。member会变成2

>>> m1=MemberCount()
>>> m1.init()
>>> MemberCount.member
1
>>> m2=MemberCount()
>>> m2.init()
>>> MemberCount.member
2


类作用域的变量也可以被所有实例访问
>>> m1.member
2


这里新member值被写到了M1特性中,屏蔽了类范围内的变量
>>> m1.member='two'
>>> m1.member
'two'
>>> m2.member
2


7.2.5   指定超类
 class     Spamfilter(Filter):
Spamfilter是Filter的子类
子类继承超类
也可在子类中重写Filter的某些定义

7.2.6   检查继承
使用issubclass()检查一个类是否另一个类的自类
使用基类的特殊特性__bases__   得到已知类的基类
使用__class__   知道一个对象属于哪个类
或者使用type()来查看新式类的实例的所属类

7.2.7    多个超类(多重继承)
需要注意的事,,如果一个方法从多个超类继承,要注意顺序:先继承的类的方法,重写   后继承  的类的方法。子类的方法 重写超类的方法

Python类继承之深度优先

python 支持多继承,但对与经典类和新式类来说,多继承查找的顺序是不一样的。

经典类:

[python] view
plain copy

 print?



class P1:   

     def foo(self):             

         print 'p1-foo'   

   

class P2 :   

     def foo(self):   

         print 'p2-foo'   

  

     def bar(self):   

         print 'p2-bar'   

   

class C1 (P1,P2):   

     pass    

   

class C2 (P1,P2):   

     def bar(self):   

         print 'C2-bar'     

   

class D(C1,C2):   

     pass   

[python] view
plain copy

 print?





d = D()  

d.foo() # 输出 p1-foo   

d.bar() # 输出 p2-bar   

实例d调用foo()时,搜索顺序是 D => C1 => P1
实例d调用bar()时,搜索顺序是 D => C1 => P1 => P2

换句话说,经典类的搜索方式是按照“从左至右,深度优先”的方式去查找属性。d先查找自身是否有foo方法,没有则查找最近的父类C1里是否有该方法,如果没有则继续向上查找,直到在P1中找到该方法,查找结束。

Python类继承之广度优先

新式类:

[python] view
plain copy

 print?





class P1(object):   

     def foo(self):             

         print 'p1-foo'   

   

class P2(object):  

     def foo(self):   

         print 'p2-foo'   

  

     def bar(self):   

         print 'p2-bar'   

   

class C1 (P1,P2):   

     pass    

   

class C2 (P1,P2):   

     def bar(self):   

         print 'C2-bar'     

   

class D(C1,C2):   

     pass   

[python] view
plain copy

 print?





d=D()   

d.foo() # 输出 p1-foo   

d.bar() # 输出 c2-bar  

实例d调用foo()时,搜索顺序是 D => C1 => C2 => P1
实例d调用bar()时,搜索顺序是 D => C1 => C2
可以看出,新式类的搜索方式是采用“广度优先”的方式去查找属性。

7.2.8   接口和内省
何为接口?
接口的概念与多态有关,在处理对象时,只要关心它的接口(或称协议)即可,也就是公开的方法和特性
在python中,不用显示的制定对象必需包含哪些方法才能做为参数接受。但是在java中要显式地编写接口
python在使用对象的时候假定它可以实现所要求的行为,如果不能实现,就程序失败

判断对象是否符合当前接口(是否能实现当前方法)内省
检查所需方法(特性)是否存在  ————hasattr(x,'method')
使用hasattr(x,'__call__')来判断某个特性是否可调用
或者callable(x)

getattr()————访问特性
setattr()————设置对象的特性

可以使用__dict__特性来查看对象内所有储存的值
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: