python 面向对象学习总结_第1页
python 面向对象学习总结_第2页
python 面向对象学习总结_第3页
python 面向对象学习总结_第4页
python 面向对象学习总结_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

.引言提到面向对象,总是离不开几个重要的术语:多态(Polymorphism),继承(Inheritance)和封装(Encapsulation)。Python也是一种支持OOP的动态语言,本文将简单阐述Python对面向对象的支持。在讨论Python的OOP之前,先看几个OOP术语的定义: 类:对具有相同数据和方法的一组对象的描述或定义。 对象:对象是一个类的实例。 实例(instance):一个对象的实例化实现。 标识(identity):每个对象的实例都需要一个可以唯一标识这个实例的标记。 实例属性(instance attribute):一个对象就是一组属性的集合。 实例方法(instance method):所有存取或者更新对象某个实例一条或者多条属性的函数的集合。 类属性(classattribute):属于一个类中所有对象的属性,不会只在某个实例上发生变化 类方法(classmethod):那些无须特定的对性实例就能够工作的从属于类的函数。1.Python中的类与对象Python中定义类的方式比较简单:class类名:类变量def _init_(self,paramers):def函数(self,.)其中直接定义在类体中的变量叫类变量,而在类的方法中定义的变量叫实例变量。类的属性包括成员变量和方法,其中方法的定义和普通函数的定义非常类似,但方法必须以self作为第一个参数。举例:class MyFirstTestClass:classSpec=it is a test classdef _init_(self,word):print say +worddef hello(self,name):print hello +name在Python类中定义的方法通常有三种:实例方法,类方法以及静态方法。这三者之间的区别是实例方法一般都以self作为第一个参数,必须和具体的对象实例进行绑定才能访问,而类方法以cls作为第一个参数,cls表示类本身,定义时使用classmethod;而静态方法不需要默认的任何参数,跟一般的普通函数类似.定义的时候使用staticmethod。class MethodTest():count= 0def addCount(self):MethodTest.count+=1print I am an instance method,my count is + str(MethodTest.count), selfstaticmethoddefstaticMethodAdd():MethodTest.count+=1printI am a static methond,my count is+str(MethodTest.count)classmethoddefclassMethodAdd(cls):MethodTest.count+=1printI am a class method,my count is+str(MethodTest.count),clsa=MethodTest()a.addCount()I am an instance method,my count is 1 a.staticMethodAdd() ;#I am a static methond,my count is2MethodTest.staticMethodAdd() ;#I am a static methond,my count is3a.classMethodAdd() ;#I am a class method,my count is4 _main_.MethodTestMethodTest.classMethodAdd() ;#I am a class method,my count is5 _main_.MethodTestMethodTest.addCount()Traceback(most recent call last):File, line 1, in MethodTest.addCount()TypeError:unbound method addCount() must be called with MethodTest instance asfirst argument (got nothing instead)从上面的例子来看,静态方法和类方法基本上区别不大,特别是有Java编程基础的人会简单的认为静态方法和类方法就是一回事,可是在Python中事实是这样的吗?看下面的例子:MethodTest.classMethodAdd() ;#I am a class method,my count is5 _main_.MethodTestclass subMethodTest(MethodTest):passb=subMethodTest()b.staticMethodAdd();#I am a static methond,my count is6b.classMethodAdd();#I am a class method,my count is7 _main_.subMethodTesta.classMethodAdd() ;#Iam a class method,my count is8 _main_.MethodTest如果父类中定义有静态方法a(),在子类中没有覆盖该方法的话,Sub.a()仍然指的是父类的a()方法。而如果a()是类方法的情况下,Sub.a()指向的是子类。staticmethod只适用于不想定义全局函数的情况。看看两者的具体定义:staticmethod function is nothing morethan a function defined inside a class. It is callable withoutinstantiating the class first. Its definition is immutable viainheritance.classmethod function also callablewithout instantiating the class, but its definition follows Subclass, not Parent class, via inheritance. Thats because the firstargument for classmethod function must always be cls (class). 封装和访问控制与Java不同,Python的访问控制相对简单,没有public,private,protected等属性,python认为用户在访问对象的属性的时候是明确自己在做什么的,因此认为私有数据不是必须的,但是如果你必须实现数据隐藏,也是可以的,具体方法就是在变量名前加双下划线。如_privatedata=0,定义私有方法则是在方法名称前加上_下划线。但即使对于隐藏的数据,也是有一定的方法可以访问的。方法就是_className_attrName。Python对于私有变量会进行Namemangling是Python中为了方便定义私有的变量和方法,防止和继承类以及其他外部的变量或者方法冲突而采取的一种机制。在python中通过_spam定义的私有变量为最终被翻译成_classname_spam,其中classname为类名,当类名是以_开头的时候则不会发生Namemangling。Namemangling存在的一个问题是当字符串长度超过255的时候则会发生截断。class PrivateTest:_myownedata=12def _myownmethod(self):printcan you see me?def sayhi(self):printsay hiclass subPrivateTest(PrivateTest):passsubPrivateTest._myownedataTraceback(most recent call last):File, line 1, in subPrivateTest._myownedataAttributeError:class subPrivateTest has no attribute _myownedatasubPrivateTest._PrivateTest_myownedata 构造函数和析构函数Python的构造函数有两种,_init_和_new_,_init_的调用不会返回任何值,在继承关系中,为了保证父类实例正确的初始化,最好显示的调用父类的_init_方法。与_init_不同,_new_实际是个类方法,以cls作为第一个参数。如果类中同时定义了_init_和_new_方法,则在创建对象的时候会优先使用_new_.class A(object):def _init_(self):print(in init)def _new_(self):print(in new)A()如果_new_需要返回对象,则会默认调用_init_方法。利用new创建一个类的对象的最常用的方法为:super(currentclass,cls)._new_(cls, .)class A(object):def _new_(cls):Object = super(A,cls)._new_(cls)print in Newreturn Objectdef _init_(self):print in initclass B(A):def _init_(self):print in Bs initB()_new_构造函数会可变类的定制的时候非常有用,后面的小节中会体现。Python由于具有垃圾回收机制,通常不需要用户显示的去调用析构函数,即使调用,实例也不会立即释放,而是到该实例对象所有的引用都被清除掉后才会执行。class P:def _del_(self):printdeletedclass S(P):def _init_(self):printinitializeddef _del_(self):P._del_(self)printchild deleteda=S() #initializedb=ac=aid(a),id(b),id(c) #(18765704,18765704, 18765704)del adel bdel c#deleted #childdeleted 绑定与非绑定在前面的例子中我们讨论过类的实例方法必须通过实例调用,如果直接通过类去访问会抛出异常,这种通过实例来访问方法就叫绑定,调用的时候不需要显示传入self参数,而调用非绑定方法需要显示传入self参数,比如当子类继承父类定义构造函数的时候,需要显示调用父类的构造函数,但此时该方法并未与任何实例绑定,调用的时候需要使用superclassName._init_(self)。静态方法可以直接被类或类实例调用。它没有常规方法那样的特殊行为(绑定、非绑定、默认的第一个参数规则等等)。完全可以将静态方法当成一个用属性引用方式调用的普通函数来看待。任何时候定义静态方法都不是必须的(静态方法能实现的功能都可以通过定义一个普通函数来实现)3. Python中的继承 继承Python同时支持单继承与多继承,继承的基本语法为class新类名(父类1,父类2,.),当只有一个父类时为单继承,当存在多个父类时为多继承。子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的变量和方法。在传统类中,如果子类和父类中同名的方法或者属性,在查找的时候基本遵循自左到右,深度优先的原则。如下列:class A:defsayhi(self):printI am A hiclass B:defsayhi(self):printI am B Hiclass C(A,B):passd=C()d.sayhi()Iam A hiB.sayhi(d)Iam B Hi如果想调用父类B的sayhi方法则需要使用B.sayhi(d).而在python引入新式类后,在继承关系中,方法和属性的搜索有所改变,使用C3算法。具体将在MRO中详细讨论。关于继承的构造函数:1. 如果子类没有定义自己的构造函数,父类的构造函数会被默认调用,但是此时如果要实例化子类的对象,则只能传入父类的构造函数对应的参数,否则会出错classAddrBookEntry(object):addressbook entry classdef_init_(self, nm, ph):= nmself.phone= phprintCreated instance for:, defupdatePhone(self, newph):self.phone = newphprintUpdated phone# for:, classEmplAddrBookEntry(AddrBookEntry):EmployeeAddress Book Entry classdefupdateEmail(self, newem):self.email= newemprintUpdated e-mail address for:, john= EmplAddrBookEntry(John Doe, 408-555-1212)2. 如果子类定义了自己的构造函数,而没有显示调用父类的构造函数,则父类的属性不会被初始化classAddrBookEntry(object):addressbook entry classdef_init_(self, nm, ph):= nmself.phone= phprintCreated instance for:, defupdatePhone(self, newph):self.phone = newphprintUpdated phone# for:, classEmplAddrBookEntry(AddrBookEntry):EmployeeAddress Book Entry classdef_init_(self, nm, ph, id, em):#AddrBookEntry._init_(self, nm,ph)self.empid= idself.email= emdefupdateEmail(self, newem):self.email= newemprintUpdated e-mail address for:, john= EmplAddrBookEntry(John Doe, 408-555-1212,42, johnspam.doe)printjohn.emailprintjohn.empid输出:johnspam.doe42Traceback(most recent call last):AttributeError:EmplAddrBookEntry object has no attribute name3. 如果子类定义了自己的构造函数,显示调用父类,子类和父类的属性都会被初始化classAddrBookEntry(object):addressbook entry classdef_init_(self, nm, ph):= nmself.phone= phprintCreated instance for:, defupdatePhone(self, newph):self.phone = newphprintUpdated phone# for:, classEmplAddrBookEntry(AddrBookEntry):EmployeeAddress Book Entry classdef_init_(self, nm, ph, id, em):AddrBookEntry._init_(self, nm,ph)self.empid= idself.email= emdefupdateEmail(self, newem):self.email= newemprintUpdated e-mail address for:, john= EmplAddrBookEntry(John Doe, 408-555-1212,42, johnspam.doe) MROMRO:即methodresolutionorder.简单的说就是python针对多继承查找一个属性或者方法的一种算法。在引入新型类之前,MRO比较简单,采取自左到右,深度优先的原则。比如有如下关系的类和属性:要查找对象x的attr属性,其根据自左到右,深度优先的原则,其搜索顺序为D,B,A,C,位于树结构底层的节点具有较高的level,当从高的level向低的level查找的时候遇到第一个属性则不再继续查找,因此上面的例子x的属性值为1. classA: attr=1 classB(A):pass classC(A):attr=2 classD(B,C):pass x=D() printx.attr1但按照多继承的理解,level高的属性应该覆盖了level低的属性,D同时继承于B,C,而C是A的子类,那么D的实例的属性值理应为attr=2而不是1,产生这个问题的主要原因是在继承关系中产生了菱形,针对经典类的MRO算法有一定的局限性,特别是在python2.2中加入了新型类后,由于object是所有对象之母,很容易形成菱形。因此python2.2采用改进的C3MRO算法进行搜索。算法描述:假设C1C2.CN表示类节点C1,C2,.CN);head=C1;tail=C2.CNC+(C1 C2.CN)=C C1C2.CN若c继承于B1,B2.BN,那么在节点C的搜索顺序LC=C加上其所有父节点的搜索顺序和各个父节点的列表之和,也即LC(B1, . , BN)= C + merge(LB1, . ,LBN, B1 . BN)其中merge的计算方法为:如果B1不在其它列表的tail中,则将其并入C的搜索列表中,同时将其从merge列表中移除,否则跳过改节点,继续B2.。如此重复知道merge为空。如果C是object对象或者没有其他的父节点,则Lobject= object.。对于单继承,则LC(B)= C + merge(LB,B) = C + LB假设有如下继承关系:则:LO= OLD= D OLE= E OLF= F OLB= B + merge(DO, EO, DE)= B+D+merge(O,EO,E)=B+D+merge(O,EO,E)=B+D+E+merge(O,O)=B D E OLA= A + merge(BDEO,CDFO,BC)=A + B + merge(DEO,CDFO,C)=A + B + C + merge(DEO,DFO)=A + B + C + D + merge(EO,FO)=A + B + C + D + E + merge(O,FO)=A + B + C + D + E + F + merge(O,O)=A B C D E F O针对上面的计算方法,利用Python的mro函数也可以说明该搜索顺序:class F(object):passclass E(object):passclass D(object):passclass C(D,F):passclass B(D,E):passclass A(B,C): passA.mro(), , , , , , B.mro(), , , 对于C3的MRO算法也可以简单的理解为:深度优先,从左到右遍历基类,先遍历高level的,再遍历低level的,如果任何类在搜索中是重复的,只有最后一个出现的位置被保留,其余会从MROlist中删除。也就是说类的共同的祖先只有在其所有的子类都已经被check之后才会check。对于A,其搜索顺序应该是AB (D) (O) C D (O) E (O) F O当然即使C3的MRO,也有其无法处理的情况,看下面的例子:class X(object):passclass Y(object):passclass A(X,Y):passclass B(Y,X):passclass C(A,B):passTraceback(most recent call last):File, line 1, in classC(A,B):TypeError:Error when calling the metaclass basesCannotcreate a consistent method resolutionorder(MRO) for bases X, Y4.自省与反射对于熟悉Java的人来说,自省和反射并不是一个陌生的概念,自省可以查看内存中以对象形式存在的其它模块和函数,获取它们的信息,并对它们进行操作。用这种方法,你可以定义没有名称的函数,不按函数声明的参数顺序调用函数,甚至引用事先并不知道名称的函数。Python提供了一些内置函数,可以方便的或者对象本省的信息。dir(object):查看对象的属性和函数列表。如果对象是个模块,则返回模块的所有属性,如果object是一个类对象,返回类和其父类的所有属性。class dirTest(object):value=1defsayhi(self):=catprinthidir(dirTest)_class_,_delattr_, _dict_, _doc_, _format_,_getattribute_, _hash_, _init_, _module_, _new_,_reduce_, _reduce_ex_, _repr_, _setattr_,_sizeof_, _str_, _subclasshook_, _weakref_, sayhi,valuedir不带参数时,显示调用者的局部变量,作用在模块上时候,显示模块的_dict_内容,显示在类上,显示类和基类的_dict_内容issubclass(sub,sup):判断一个类是另一个类的子类或子孙类isinstance(obj1,obj2):在判定一个对象是否是另一个给定类的实例callable()是一个布尔函数,确定一个对象是否可以通过函数操作符()来调用。如果函数可调用便返回True,否则便是False.模块inspect:inspect模块提供了一系列自省函数,它可以获取模块,类,方法,函数,traceback,帧对象,代码对象的信息。常用的方法getmembers,ismodule,getcallargs,isclass等,更多详细信息参见/library/inspect.htmlimport inspectinspect.isbuildin(abs)inspect.isbuiltin(abs)Trueinspect.ismodule(inspect)True5.新型类与元类对于新型类来说,其默认的元类为type,而对于传统的类,其默认类型为types.ClassTypeclass Classic: passclass Newstyle(object): passprint type(Classic)print type(Newstyle)比如class TypeTestClass:passTypeTestClass =type(TypeTestClass,(),)print TypeTestClassprint TypeTestClass()新型类是在Python2.2中引入的,其在语法和行为上基本和经典类兼容,主要差别在于所有的新式类必须继承至少一个父类,Object是所有类之母,如果类没有继承任何其他父类,则object将作为默认的父类,新型类还支持从内置类型如list,dict, file等创建子类。新型类的实例在具有传统类实例的特性,但在_init_的基础上加入的新的构造函数_new_,同时支持静态方法staticmethod和类方法classmethod.(上面的章节已经阐述),同时增加了Property和_slot_,_getattribute_ _等属性,。Python对Property的定义如下Aproperty is an attribute that is defined by get/set methods.其对应的内建函数有四个参数:property(fget=None,fset=None, fdel=None,doc=None),其中fget,fset,fdel必须有一个方法被申明,否则进行对应的操作会产生AttributeError异常。如果只需要定义只读属性,则只需要实现fget方法,而不实现fset方法即可。10. class PropertyTest(object):11. def_setProperty(self,value):12. self._property=value13. printsetting property14. def_getProperty(self):15. printgetting property16. returnself._property17. def_delProperty(self):18. printdel proerty19. delself._property20. TestProperty=property(fget=_getProperty,fset=_setProperty,fdel=_delProperty,doc=propertytest)21.22.23. 24. p=PropertyTest()25. p.TestProperty=126. settingproperty27. 28. p.TestProperty29. gettingproperty30. 131. del p.TestProperty32. delproertyProperty提供灵活的机制来读取、编写或计算私有字段的值。可以像使用公共数据成员一样使用属性,但实际上它们是称作“访问器”的特殊方法。这使得可以轻松访问数据,此外还有助于提高方法的安全性和灵活性。_slots_类属性:在Python中可以用_dict_属性来跟踪所有实例属性,而事实上_dict_会占用大量的内存,从Python2.2开始可以用类变量_slots_代替_dict_.,它是一个由具有合法标识的实例属性构成的集合。在定义了_slots_属性的类中不会在存在_dict_,因此可以节约内存,同时它能防止动态增加实例属性,从某种程度上讲更为安全。class SlotTest(object):_slots_=(name,age)class Test(object):passs=SlotTest()=carols.age=12s.score=64Traceback(most recent call last):File, line 1, in s.score=64AttributeError:SlotTest object has no attribute scoredir(s)_class_,_delattr_, _doc_, _format_, _getattribute_,_hash_, _init_, _module_, _new_, _reduce_,_reduce_ex_, _repr_, _setattr_, _sizeof_,_slots_, _str_, _subclasshook_, age, names1=test()s1.score=65dir(s1)_class_,_delattr_, _dict_, _doc_, _format_,_getattribute_, _hash_, _init_, _module_, _new_,_reduce_, _reduce_ex_, _repr_, _setattr_,_sizeof_, _str_, _subclasshook_, _weakref_, score元类:对于万物皆是对象的Python,对于定义类的一段代码其本身也是对象,那么这个类对象的类型就是元类,它用来描述类的类。在元类用于创建类的时候,解释器先查找_metaclass_属性,该属性的值便是类的元类,如果没有找到该属性的定义,则会查找其父类的_metaclass_.如果仍然没有找到,对于新型类则会以type(object)作为其元类,如果当前模块有全局变量名为metaclass,则将其值作为其元类,而对于传统的类,其元类类型为types.ClassType.可以有多种方法来创建一个元类,如利用type函数,类工厂模式,或者设置_metaclass_属性等1利用传统工厂函数返回类:def class_creator(func):classinternal:passsetattr(internal,func._name_,func)returninternaldef alive(self):print Hi,I am hereChildClass = class_creator(alive)cc=ChildClass()cc.alive()Hi,Iam here2通过type创建类def _init_(self):self.message=metaclass testdef alive(self):printself.messageattrs=_init_:_init_,alive:alivebases=()ClassTep=type(MetaTestClass,bases,attrs)t=ClassTep()t.alive()metaclasstest3设置_metaclass_属性:只要在类定义中把_metaclass_设置为任意有着与type相同参数的可调用对象,就能够提供自定义的元类。通常继承typeclass Meta(type):def_init_(cls,name,bases,attrs):printI am a meta class templete,Class will be created by mesuper(Meta,cls)._init_(name,bases,attrs)defWelcome(cls): print welcome,cls._name_class MetaTest(object):_metaclass_=Metadefsayhi(self):printHiIam a meta class templete,Class will be created by meMetaTest.Welcome()welcomeMetaTestMetaTest().sayhi()Hi4利用new模块中的类工厂:new.classobj(name,baseclasses, dict)Thisfunction returns a new class object, with name name, derived frombaseclasses (which should be a tuple of classes) and with namespacedict.from new import classobjmetatest=classobj(Meta,(object,),Hello:lambda self:hello)metatest().Hello()hello在元类中也可以定义类方法,一般叫做元方法,元方法和普通的类方法在使用上存在一定的区别,元方法能够被元类或者元类对象(类)直接调用,但不能没类的实例调用,而类方法可以被类或者类的实例直接调用。cla

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论