Python类与面向对象 程序=指令+数据 (或算法+数据结构)。代码可以选择以指令为核心或以数据为核心进行编写。 两种类型 (1)以指令为核心:围绕“正在发生什么”进行编写(面向过程编程:程序具有一系列线性步骤;主体思想是代码作用于数据)以指令为中心,程序员的主要工作在于设计算法。 (2)以数据为核心:围绕“将影响谁”进行编写(面向对象编程OOP:围绕数据及为数据严格定义的接口来组织程序,用数据控制对代码的访问) 面向对象编程的核心概念 所有编程语言的最终目的都是提供一种抽像方法。 在机器模型("解空间"或"方案空间")与实际解决的问题模型("问题空间")之间,程序员必须建立一种联系。 (1)面向过程:程序=算法+数据结构 (2)面向对象:将问题空间中的元素以及它们在解空间中的表示物抽象为对象,并允许通过问题来描述问题而不是方案(可以把实例想象成一种新型变量,它保存着数据,但可以对自身的数据执行操作) 类是由状态集合(数据)和转换这些状态的操作集合组成 类:定义了被多个同一类型对象共享的结构和行为(数据和代码) (1)类的数据和代码:即类的成员 数据:成员变量或实例变量 成员方法:简称为方法,是操作数据的代码,用于定义如何使用成员变量;因此一个类的行为和接口是通过方法来定义的。 (2)方法和变量: 私有:内部使用;公共:外部可见 面向对象的程序设计方法 所有东西都是对象;程序是一大堆对象的组合。 通过消息传递,各对象知道自己该做什么。
消息:即调用请求,它调用的是隶属目标对象的一个方法 每个对象都有自己的存储空间,并可容纳其它对象;通过封装现有对象,可以制作成新型对象 每个对象都属于某一类型: 类型,也即类; 对象是类的实例;类的一个重要特性为“能发什么样的消息给它”。 同一个类的所有对象都能接收相同的消息。 对象的接口 定义一个类之后,可以根据需要实例化出多个对象 如何利用对象完成真正有用的工作呢? 必须有一种办法能向对象发出请求,令其做一些事情(方法) 每个对象仅能接受特定的请求(方法);能向对象发送的请求由其“接口”进行定义;对象的“类型”或“类”则规定了它的接口形式。 类 将同一种具体物事的共同特性抽象出来的表现。 类内部定义了其状态和转换这些状态的操作。 类被实例化为对象时才有相关的一些数据,才会对该对象的一些属性进行赋值,这些值就是所谓的数据。从某种角度理解这些对象属性就是变量。为对象属性赋值就是为变量赋值。这些对象的属性(即变量)都是在类中定义的数据结构体。 方法:就是函数,就是操作变量引用的数据代码。方法的作用就是为了操作数据。 类间的关系: 依赖("uses-a"):一个类的方法操纵另一个类的对象 聚合("has-a"):类A的对象包含类B的对象 继承("is-a"):描述特殊与一般关系 面向对象编程的原则 面向对象编程的模型机制有3个原则:封装、继承及多态 封装(encapsulation) 隐藏实现方案细节;将代码及其处理的数据绑定在一起的一种编程制作,用于保证程序和数据不受外部干扰且不会被误用。 继承(Inheritance) 一个对象获得另一个对象属性的过程;用于实现按层分类的概念 一个深度继承的子类继承了类层次中它的每个祖先的所有属性 超类、基类、父类;子类、派生类 多态性(Polymorphism) 允许一个接口被多个通用的类动作使用的特性,具体使用哪个动作与应用场景相关。 一个接口,多个访问:用于为一组相关的动作设计一个通用的接口,以降低程序复杂性。 Python的类和实例 类是一种数据结构,可用于创建实例。一般情况下,类封装了:数据和要用于该数据的方法 python类是一个可调用对象,即类对象;python2.2之后,类是一种自定义类型,而实例则是声明某个自定义类型的变量 实例初始化;通过调用类来创建实例 instance = ClassName(args...) 类在实例化时可以使用__init__和__del__两个特殊的方法 类本身是一个对象,被实例化出来的实例也是一种对象。也就是所谓的类对象与实例对象。 编写类的代码就是一个类对象,但这些代码不会自动执行,只有在被实例化之后,这些类对象的代码执行从而产生了一个具体的实例对象。 type(list) type(l1) Python中创建类 python使用class关键字创建类,语法格式如下:
class class_name(bases):
'class documentation string'
class_suite
超类是一个或多个用于继承的父类的集合 类体可以包含:声明语句、类成员定义、数据属性、方法 注意:如果不存在继承关系,ClassName后面的"(bases)"可以不提供;类文档为可选 class 语句的一般形式
class class_name(bases):
data = value 定义数据(类属性)
self method(self,....): 定义方法
self.member = value 定义实例属性
class TestClass(): 定义类对象
pass
type(TestClass)
obj1 = TestClass() 被实例化出来的实例对象
例:Python中,class语句类似def,是可执行代码;直到运行class语句后类才会存在
class FirstClass(): 定义类名为FirstClass
spam = 30 定义数据属性
def display(self): 定义类方法,属于可调用的属性
print self.spam
x = FirstClass() 实例化出一个对象x
x.display() 调用x对象的display方法
class语句内,任何赋值语句都会创建类的属性;每个实例对象都会继承类的属性并获得自己的名称空间。
class SecClass():
data = 'hello SecClass'
def printdata(self):
print "Content from method: %s" % self.data
inst2 = SecClass()
inst2.data 获取对象的data属性
inst2.printdata() 执行对象的printdata()方法
class ThirdClass():
data = 'hello Thirdclass'
def setdata(self,x)
self.str1 = x
def printdata(self):
print self.str1
inst3 = ThirdClass()
inst3.data 返回对象的属性
inst3.setdata('test') 调用对象的setdata()方法,并传递参数'test'
inst3.getdata() 调用对象的getdata()方法
python类方法及调用 实例(对象)通常包含属性 可调用的属性:方法 object.method() 数据属性 在OOP中,实例就像是带有"数据"的记录,而类是处理这些记录的"程序" 通过实例调用方法相当于调用所属类的方法来处理当前实例。类似instance.method(args...)会被自动转换为class.method(instance,args...)。如前面的例子,x.display()会被自动转换为FirstClass.display(x),即调用类的方法来处理实例x。 因此,类中每个方法必须具有self参数,它隐含当前实例之意。在方法内对self属性做赋值运算会产生每个实例自己的属性。 python规定,没有实例,方法不允许被调用,此即为“绑定” class语句中的赋值语句会创建类属性,如前面例子中的spam 在类方法中对传给该方法的特殊参数self进行赋值会创建实例属性。 例 class Myclass(): gander = 'Thompson' def setName(self,who): self.name = who x = Myclass() y = Myclass() x.gender 返回x对象的gender属性,通过爬树搜索,gender属性会从MyClass类中获取到 x.name 在SetName方法调用之前,Myclass类不会把name属性附加到实例x上,当然也可以重载__init__创建构造器直接为实例提供。所以系统提示错误 x.setName('tom') y.setName('jerry') 调用y对象的setName方法,并传递参数给name属性 y.gender, y.name x.gender, x.name Python构造器 python中都有一个构造器的方法 __varname__() 方法,会被python解释器自动调用。当类被实例化成对象时,这些访问就会被自动调用。 创建实例时,python会自动调用类中的__init__方法,以隐性地为实例提供属性。__init__方法被称为构造器。如果类中没有定义__init__方法,实例创建之初仅是一个简单的名称空间。 例 class Myclass(): gender = 'Male' def __init__(self,who): self.name = who x = Myclass('tom') y = Myclass('jerry') x.gender, x.name y.gender, y.name 析构器 class Animal: name = 'someone' 定义数据属性(成员变量) def __init__(self,voice='HI'): 定义构造函数,参数设置默认值 self.voice = voice def __del__(self): 定义析构函数 pass del saySomething(self): 定义方法(成员函数) print self.voice tom = Animal() tom.saySomething() jerry = Animal("Hello") jerry.saySomething() 类的特殊属性 可以使用类的__dict__字典属性或python内置的dir()函数来获取类的属性 dir(MyClass) MyClass.__dict__ 每个对象自己的__dict__属性中保存了本对象支持的所有属性和方法 Class.__name__ 以字符串方式返回类的名字 Class.__doc__ 返回类class的文档字符串 Class.__bases__ 返回类class的所有父类构成的元组 Class.__module__ 返回类c定义所在的模块 (python1.5后期版本新增功能) Class.__class__ 返回实例Class对应的类(仅新式类中) 实例的属性 实例仅拥有数据属性(严格意义上来说,方法是类属性) 通常通过构造器"__init__"为实例提供属性;这些数据属性独立于其它实例或类;实例释放时,其属性也将被清除 内建函数dir()或实例的特殊属性__dict__可用于查看实例属性 dir(inst1) 实例的特殊属性: Inst1.__class__ 实例化Inst1的类 Inst1.__dict__ Inst1的属性 Python类方法中可用的变量 方法的可用变量 实例变量:指定变量名称及实例自身进行引用 self.变量名 局部变量:方法内部创建的变量,可直接使用 类变量(也称为静态变量):在类中定义的变量;只有通过指定变量名与类名进行引用 类名.变量名 全局变量:直接使用 例: In [1]: class c1(): ...: d1 = 'hello c1 class' ...: def __init__(self,x): ...: self.instdata = x ...: In [2]: inst1 = c1(50) In [3]: inst1.d1 Out[3]: 'hello c1 class' In [4]: inst1.instdata Out[4]: 50 In [5]: inst2 = c1(60) In [6]: inst2.d1 Out[6]: 'hello c1 class' In [7]: inst2.instdata Out[7]: 60 In [8]: inst1.d1 = 'instance1 new value' In [9]: inst1.d1 Out[9]: 'instance1 new value' In [10]: inst2.d1 Out[10]: 'hello c1 class' In [11]: c1.d1 = 'class c1 new value' In [12]: inst1.d1 Out[12]: 'instance1 new value' In [13]: inst2.d1 Out[13]: 'class c1 new value' 继承 继承描述了基类的属性如何“遗传”给派生类 子类可以继承它的基类的任何属性,包括数据属性和方法;一个未指定基类的类,其默认有一个名为object的基类;python允许多重继承 创建子类:创建子类时,只需要在类名后跟一个或从其中派生的父类 class SubClassName(ParentClass1[, ParentClass2, ...]) 'optional class documentation string' class_suite 例: In [14]: class ParentClass(object): ....: 'Parent Class' ....: gender = 'Male' ....: def setName(self,who): ....: self.name = who ....: In [15]: class ChildClass(ParentClass): ....: 'Child Class' ....: def displayInfo(self): ....: print self.gender, self.name ....: In [16]: x = ChildClass() In [17]: x.setName('tom') 该方法是继承父类的 In [18]: x.displayInfo() Male tom In [19]: dir(ParentClass) Out[19]: ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender', 'setName'] In [20]: dir(ChildClass) Out[20]: ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'displayInfo', 'gender', 'setName'] In [21]: x.name Out[21]: 'tom' 继承方法专用化:继承会先在子类寻找变量名,然后才查找超类,因此,子类可以对超类的属性重新定义来取代继承而来的行为。 子类可以完全取代从超类继承而来的属性;也可以通过已覆盖的方法回调超类来扩展超类的方法 例: In [22]: class ParClass(object): ....: def setInfo(self, sex='Male'): ....: self.gender = sex ....: In [23]: class ChiClass(ParClass): ....: def setInfo(self,who): 此方法名与父类方法名相同,所以覆盖父类中的方法 ....: self.name = who ....: In [24]: x = ChiClass() In [26]: x.setInfo('tom') In [27]: x.name Out[27]: 'tom' In [28]: x.gender 由此子类中的setInfo方法已经覆盖父类中的setInfo方法,而子类中的setInfo方法并无gender属性,所以系统提示错误信息 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-28-dcfdb2ec694c> in <module>() ----> 1 x.gender AttributeError: 'ChiClass' object has no attribute 'gender' In [29]: 例: In [29]: class ParClass(object): ....: def setInfo(self,sex='Male'): ....: self.gender = sex ....: In [30]: class ChiClass(ParClass): ....: def setInfo(self,who): 子类中的setInfo方法覆盖父类中的setInfo方法 ....: self.name = who ....: ParClass.setInfo(self) 在子类中再次回调其父类中的setInfo方法 ....: In [31]: x = ChiClass() In [32]: x.setInfo('tom') In [33]: x.name Out[33]: 'tom' In [34]: x.gender 由于子类中再次调用父类中的setInfo方法,所以此时实例x可以使用父类继承来的gender属性 Out[34]: 'Male' In [35]: 例: class father: def __init__(self): self.name="father class" print "father __init__" def fa_method(self): print "father method test" def bad(self): print "smoke、drink" class son(father): def __init__(self): self.name="son class" print "son __init__" father.__init__(self) #在子类中调用父类的构造函数 #super(son,self).__init__() #通过supper函数也可以调用父类中的构造函数,但父类必须指明是(object)的子类 def son_method(self): print "son method test" #def bad(self): #实现基类(父类)重写 # print "smoke" def bad(self): father.bad(self) #引用父类中的bad方法 print "gamble" s1=son() #实例化一个s1对象 s1.son_method() print s1.name s1.fa_method() #继承父类的方法 s1.bad() #调用bad方法,该方法默认继承父类,但如果子类中实现了重写,则调用子类中的新方法 python类的继承和属性搜索 python中几乎所有属性的获取都可以使用“object.attribute”的格式;不过,此表达式会在python中启动搜索--搜索连续的树 class语句会产生一个类对象,对class的调用会创建实例,实例自动连结至创建了此实例的类 派生类连结至其基类的方式:将基类列在派生类头部的括号内,其从左至右的顺序会决定树中的次序;由下至上,由左至右 经典类与新式类 如果类继承了object类,称为新式类;新式类兼容经典类;如果类没有指明继承object,称为经典类 新式类多出了一些内置方法和属性;通过新式类可以设置访问权限 对于其它语言来说,object类默认就是所有任何类的基类,不需要手工指明 继承基类的搜索顺序:经典类深度优先,而新式类广度优先 例1: class A: def __init__(self): print 'This is A class' def save(self): print 'save method from A' class B(A): def __init__(self): print 'This is B class' #在B类中没有定义Save方法,它会继承父类A class C(A): def __init__(self): print 'This is C class' def save(self): print 'save method from C---' class D(B,C): def __init__(self): print 'This is D class' d1=D() d1.save() #本例为经典类,在调用d1对象的save()时,它的继承搜索路径是自左至右(先搜索B类中的save方法,B类又继承了A类) #所以本例的输出结果为: This is D class save method from A 例2: class A(object): def __init__(self): print 'This is A class' def save(self): print 'save method from A' class B(A): def __init__(self): print 'This is B class' #在B类中没有定义Save方法,它会继承父类A class C(A): def __init__(self): print 'This is C class' def save(self): print 'save method from C---' class D(B,C): def __init__(self): print 'This is D class' d1=D() d1.save() #本例为新式类,新式类的搜索为:广度优先(先搜索B,B类没有定义save方法,再搜索C) #所以输出结果为: This is D class save method from C--- 例: class class3: def __init__(self): pass def __del__(self): print "销毁中..." def go(self): print "Go" def __call__(self): #内置__call__方法的使用 print "Call" c3=class3() c3.go() c3() #通过call方法可以调用类的默认方法 特性:经典类和新式类中的特性的区别 ,经典类中的特性全部都是可读可写的(没有只读的功能) class Person: def __init__(self): self.__name__ = 'Thompson' @property def Name(self): return self.__name p1 = Person() print p1.name #通过特性Name,读取self.__name的值 p1.Name = 'xxx' #通过特性Name,设置self.__name的值 #新式类中的特性默认都是只读,如果想要设置,那么就需要再创建一个被装饰@xxxx.setter修饰的特性 class Person(object): def __init__(self): self.__name = 'alex' @property def Name(self): return self.__name p1 = Person() print p1.Name #通过特性Name,读取self.__name的值 p1.Name = 'xx' # Error 通过特性Name设置self.__name 的值时,会出现错误 #新式类这么做好处是什么呢?通过这样来防止对私有变量的修改,所有的对象就只能读取,不能修改 #应用场景: class Person(object): def __init__(self,gene,clothes): self.__gene = gene self.__clothes = clothes @property def Gene(self): #通过该方法读取__gene属性 return self.__gene @property def Clothes(self): #通过该方法读取__clothes属性 return self.__clothes @Clothes.setter #此处的名称必须与上面的读取函数名称相同 def Clothes(self,value): self.__clothes = value p1 = Person('AB','bikini') #创建基因为AB的人,衣服是比基尼 print p1.Gene #可以获取人的基因 print p1.Clothes # 通过Clothes方法读取衣服属性 p1.Clothes = 'skirt' #修改p1的衣服为skirt print p1.Clothes #再次读取Clothes属性 p1.Gene='O' #修改基因时系统提示错误,无法修改。 类、实例和其它对象常见的内建函数 issubclass() 布尔函数,判断一个类是否同另一个类派生,语法:issubclass(sub,sup) isinstance() 布尔函数,判断一个对象是否是给定类的实例,语法:isinstance(obj1, class_obj2) hasattr() 布尔函数,判断一个对象是否拥有指定的属性,语法:hasattr(obj,'attr');同类的函数还有getattr()、setattr()和delattr() super() 在子类中找出其父类以便于调用其属性;一般情况下仅能采用非绑定方式调用祖先类方法 而super()可用于传入实例或类型对象,语法:super(type[, obj]) 例: In [36]: issubclass(ChiClass,ParClass) Out[36]: True In [37]: issubclass(ParClass,object) Out[37]: True In [38]: isinstance(x,ChiClass) #x为对象名 Out[38]: True In [39]: hasattr(x,'name') Out[39]: True In [40]: hasattr(x,'gender') Out[40]: True 运算符重载 运算符重载是指在方法(如__name__的方法)中拦截内置的操作--当类的实例出现在内置操作中,python会自动调用自定义的方法,并且返回自定义方法的操作结果。 运算符重载让类拦截常规的python运算 类可重载所有python表达式运算符; 类也可重载打印、函数调用、属性点号运算等内置运算 重载使类实例的行为像内置类型;重载通过提供特殊名称的类方法实现 运算符重载并非必需,并且通常也不是默认的 类可以重载python的操作符,重载使我们的对象与内置的一样。 __x__的名字的方法是特殊的挂钩(hook),python通过这 种特殊的命名来拦截操作符,以实现重载。 python在计算操作符时会自动调用这样的方法,例如: 如果对象继承了__add__方法,当它出现在+表达式中时会调用这个方法。 通过重载,用户定义的对象就像内置的一样。 在类中重载操作符 1、操作符重载使得类能拦截标准的python操作。 2、类可以重载所有的python的表达式操作符。 3、类可以重载对象操作:print,函数调用,限定等。 4、重载使得类的实例看起来更像内置的。 5、重载是通过特殊命名的类方法来实现的。 方法名 重载的操作说明 调用表达式 __init__ 构造函数 创建对象:class() __del__ 析构函数 释放对象的时候 __add__ “+” x + y __or__ “|” x | y __repr__ 打印,转换 print x, `x` __call__ 函数调用 x() __getattr__ 属性引用 x.undefined __getitem__ 索引 x[key],for循环,in测试 __setitem__ 索引赋值 x[key] = value __getslice__ 分片 x[low:high] __len__ 长度 len(x) __cmp__ 比较 x == Y ,x < y __radd__ 右边的操作符"+" 非实例 + x 例子: 例: class indexer: def __getitem__(self,index): return index**2 x=indexer() for i in range(5): print x[i] print ":",__getitem__(x,5) 输出结果为: 0 1 4 9 16 : 25 例: In [13]: class test: ....: def __init__(self,x): ....: self.x=x ....: def __add__(self,y): ....: return x+y ....: In [14]: c1=test(5) In [15]: print c1.__add__(6) 11 In [16]: print c1.x 5 In [17]: dir(c1) Out[17]: ['__add__', '__doc__', '__init__', '__module__', 'x'] 基于特殊的方法定制类 除了__init__和__del__之外,python类支持使用许多的特殊方法 特殊方法都以双下划线开头和结尾,有些特殊方法有默认行为,没有默认行为的是为了留到需要的时候再实现。 这些特殊方法是python中用来扩充类的强大工具,它们可以实现:模拟标准类型、重载操作符 特殊方法允许类通过重载标准操作符+,*,甚至包括分段下标及映射操作[]来模拟标准类型。 抽象类 from abc import ABCMeta,abstractmethod class Alert: __metaclass__=ABCMeta @abstractmethod def send(self):pass class Email(Alert): def __init__(self): self.name='email alter' def send(self): print 'test' notify=Email() notify.send() #抽象类+抽象方法=定义接口 总结 可调用对象:函数(内置函数/自定义函数)、类、类方法 (支持调用操作,支持小括号的方法) 函数的属性: __doc__ __name__ __dict__ __code__ __globals__ 实例方法/ 类方法 / 静态方法 方法的属性: __doc__ __name__ __class__ 方法所属的类 __func__ 实现该方法的函数对象 __self__ 调用此方法的实例 内置函数: __doc__ __name__ __self__ 类: __doc__ __name__ __bases__ __dict__ 输出对象所有的属性 __module__ 定义了当前类的模块名称 实例: __class__ __dict__ 对象都有特殊方法: __init__ __new__() __del__() __ge__() a >= b a.__ge__(b)