1. 面向对象程序设计思想
1.1 基本概念
面向对象是从现实中客观存在的事物(即对象)出发来构建软件系统,在系统构造中运用人类的自然思维方式,强调直接以问题域(现实世界)中的事物为中心思考问题,认识问题,并根据这些事物的本质特点,把他们抽象地表示为系统中的对象,作为系统的基本构成单位; 系统直接地映射问题域,保持问题域中事物及其相互关系的本来面貌; 面向对象设计强调使用对象、类、继承、封装等基本概念来进行程序设计,是开发者以现实世界中的事物为中心来思考和认识问题,并以人们易于理解的方式表达出来;
1.1.1 对象
对象是现实世界中一个实际存在的事物,可以是一个物理对象,或某一类概念实体的实例;如:一个人、一种语言、一项计划,都可作为一个对象; 任何世界问题的解决都是由之相互联系的一系列对象相互作用的结果; 面向对象程序设计的对象,是系统中用来描述客观事物的一个实体,是构成系统的一个基本单元,由一组行为构成; 对象具有状态,一个对象用数据值来描述它的状态; 对象属性是描述对象状态特性的数据项; 对象还有操作,用于改变对象的状态达到特定的功能; 一个对象可以具有多个属性和多个操作,对象及其操作就是对象的行为; 对象实现了数据和操作的结合,使数据和操作封装于对象的统一体中;
1.1.2 类
类是具有相同或相似性质的对象的抽象; james Rumbaugh(詹姆士·兰宝)对类的定义:类是具有相似结构、行为和关系的一组对象的描述符,类包括属性和操作;类的属性是对象的状态的抽象,用数据结构来描述;类是具有相同属性的操作的一组对象的集合,它为属于该类的所有对象提供统一的抽象类型;对象的抽象是类,类的具体化就是对象,或称为类的实例是对象,类实际就是一种数据类型; 如教师信息管理系统,“教师”即一个类,具有姓名、性别、年龄、学历层次等属性,教师 “李武” 就是一个对象,是教师类的一个实例;
1.1.3 继承
继承反应客观世界中各类事物之间的一种“一般和特殊”的关系; 继承是指类之间有继承关系,子类有条件地继承父类的特征; 对象的一个新类可从现有的类中派生出来,整个过程被称为类继承; 类的对象是各自封闭的,若没继承机制,则类对象中的数据、方法就会出现大量重复;继承不仅支持系统的可重用性,还促进系统的可扩充性;
1.1.4 封装
封装就是把对象的属性和基于属性的操作结合成一个不可分割的独立实体,并尽可能隐藏对象的内部细节,只保留一些对外接口使之与外部发生联系,即一种信息隐蔽技术;封装的目的在于把对象的设计者和使用分开,使用者不需要知道行为实现的细节,只需用设计者对外提供的接口来访问该对象; 面向对象程序设计,对象是封装的最基本单位,封装防止了程序的相互依赖而带来的影响;
1.1.5 多态
多态一般值具有多种形态的能力,如水有三种形态,即气态、液态、固态; 对象的多态是值在一般类中定义的属性或操作被特殊类继承后,可具有不同的数据类型或表现出不同的行为; 利用多态性,用户可以发送一个通用的消息,将所有的实现细节都留给接收消息的对象自行决定,意味着同一消息可调用不同的方法;
1.2. 面向对象设计
面向对象程序设计借助特定的计算机语言实现从现实世界问题域中的实体到计算世界中的对象映射表达; 面向对象程序设计具有以下特性:程序设计的重点在数据而不是函数;程序由对象组成;对象之间通过相互协作来完成功能;大多数对象的定义以数据为中心;函数与相关的数据紧密结合;数据可被隐藏;容易扩充新的数据和函数; 面向对象程序设计的一般步骤包括:分析实际问题,分辨并抽取其中的类和对象;设计相应的类,并根据这些类创建各种对象;协调这些对象完成程序功能(消息); 面向对象程序设计具有如下优点:
- 符合人们习惯的思维方法,便于分解大型的复杂多变的问题;
- 易于软件的维护和功能的增减;对象的封装性及其对象之间的松散组合;给软件的修改和维护提供便利;
- 可重用性好;重复使用一个类(类是对象的定义,对象是类的实例化),比较方便地构造出软件系统,加上继承方式,提高了软件开发的效率;
- 与可视化技术结合,改善工作界面;随着基于图形界面操作系统的流行,面向对象的程序设计方法也将深人心;与可视化技术结合,使人机界面进入GUI时代;
2. 类和对象
对象是某个具体客观事物的抽象,类是对对象的抽象描述,计算机语言中是一种抽象的数据类型; 类定义了数据类型的数据(属性)和行为(方法);类与对象的关系是,对象是类的实例,类是队形的模板;
2.1 创建类
Python 定义一个类使用关键字 class 声明,类的声明格式如下:
class className:
类体
class 是关键字,className (类名)为有效的标识符,命名规则多个单词组成的名称,除每个单词的首字母大写外,剩下的字母均小写;类名后面有个冒号 ; 类体中,可定义属性和方法,有缩进的语句块组成; 例子1:
2.2 创建对象
类是抽象的,必须实例化类才能使用类定义的功能,即创建类的对象; 若把类的定义视为数据结构的类型定义,实例化就是创建了一个这种类型的变量; 创建类的对象、创建类的实例、实例化等说法都是等价的,都说明以类为模板生成了一个对象的操作; 对象的创建和调用格式如下:
anObject = className()
anObject 是对象名,className 是已定义的类名;
3. 属性和数据
类的数据成员是在类中定义的成员变量,用来存储描述类的特征的值,被称为属性; 属性可被该类中定义的方法访问,也可通过类或类的实例进行访问;
3.1 类属性
类属性是类的数据或函数,类的数据属性仅是所定义的类的变量,可以像任何其他变量一样在类定义后被使用; 类属性属于整个类,不是特定实例的一部分,而是所有实例之间共享的一个副本; 类属性通常在类体中初始化,在类定义的方法或外部代码中,通过类名访问: 类变量名 = 初始值 #初始化类属性 类名.类变量名=值 #修改类属性的值 类名。类变量名 #读取类属性的值 注意: 类属性的读、写访问都是通过“类名.”来实现的 ; 例子2:
3.2 实例属性
每个实例对象都有自己的属性,通过“self.” 变量名定义,实例属性属于特定的实例; 实例变量在类的内部通过“self.” 访问,在外部通过对象实例访问; 实例属性初始化:通常在 __init__ 方法利用“self.” 对实例属性进行初始化; “self.” 实例变量名 = 初始值 在其他实例函数中,通过“self.” 访问: “self.” 实例变量名 = 值 或利用对象名访问:
bbc = calssName()
bbc.实例变量名 = 值
bbc.实例变量名
例子3: Persion 类定义了实例属性 name、age 与 grade;s1、s2 是 Persion的两个实例,这两个实例粉笔拥有自己的属性值,与其他对象有别; 实例的属性能像普通变量那样操作; 例子4:
3.3 类属性与实例属性的联系
类与实例有各自的命名空间;类属性可以通过类或实例访问,当通过实例访问,Python 则先查找实例的名字空间,再找类的名字空间,最后找基类的名字空间;若在实例空间找到了该属性名,则访问实例的属性,若类名字空间也有这个属性名,则会被隐藏,直到实例属性被清除; 例子5: 若在实例方法更改某个属性,并且存在同名的类属性,若实例对象有该名称的实例属性,则修改的是实例属性,若实例对象没哟该名称的实例属性,则会创建一个同名称的实例属性;想要修改类属性,若在类外,可通过类对象修改,若在类里面,只有在类方法中进行修改;
3.4 私有属性与公有属性
私有属性指只有在类方法这个才能访问私有属性,对象不能直接访问私有属性;公有属性可被对象直接访问; 通常,约定两个下滑线开头,但不以连个下划线结束的属性是私有属性,其他的即公有属性; 例子6: 例子7: Python 不允许实例直接访问私有数据,可使用 object._className__attrName 访问;
3.5 自定义属性
Python中,可赋予一个对象自定义的属性,即类定义中不存在的属性;对象通过 __dict__ 存储自定义属性; 例子8: 通过定义下面的方法获取属性的值和设置属性的值;
__getatrr__(self , name) :获取属性,访问不存在的属性时调用;__getattribute__(self ,name) :获取属性,访问存在的属性时调用(先调用该方法,查看是否存在该属性,若不存在,接着调用 __getattr__ )__setattr__(self ,name,value) :设置实例对象的一个新的属性时调用;- _
_delattr__(self,name) :删除一个实例对象的属性时调用; 例子9: b.firstname 没有调用 __getattr__ ,是因为__getattr__ 方法只在属性没有找到的时候调用;
3.6 self 的作用
self 指类的实例对象,不是类;self 只有在类的方法中才会有,独立的函数或方法是不必带有self 的;self 在定义类的方法时思必须有,虽然在低啊用时不必传入相应的函数; 例子10: self 指向 Sersion 的 b;
4. 方法
方法是与类相关的函数;方法与函数不同,函数是封装操作的小程序;方法是定义在类内部的函数,并定义方法与普通函数有所不同;
4.1 方法的声明和调用
在类的内部,使用 def 关键字可为类定义一个方法,与一般函数定义不同,类方法必须包含对象本身的查收农户,通常为 self ,且为第一个参数; 方法的声明格式如下:
def 函数名(self,[形参列表])
函数体
方法的调用格式如下:
对象.方法名([实例参数])
方法定义时的第一个参数 self 在调用时,用户不用给该参数传值,Python 自动把对象实例传递给该参数; 例子11:
4.2 实例方法、类方法和静态方法
类的方法主要有三种类型:实例方法、类方法和静态方法 ,不同的类方法有不同的使用场景和声明调用形式,,不同的方法也具有不同的访问限制; 实例方法是属于实例的方法,通过实例名.方法名 调用,该方法可访问类属性、实例属性、类方法、实例方法和静态方法; 类方法属于类的方法,可通过实例名.方法名 ,或通过类名.方法名 调用;类方法不能访问实例属性和实例方法; 静态方法是同类实例对象无关的方法,调用形式同类方法类似;
4.2.1 实例方法
实例方法是在类中最常定义的成员方法,至少有一个参数并且必须以实例对象为其第一个参数,以 "self" 为第一个参数; 类外,实例方法只能通过实例对象去调用; 实例方法的声明格式如下:
def 方法名(self, [形参列表]):
函数体
实例方法通过实例对象调用: 对象.方法名([实参列表]) 例子12:
b = Sersion()
b.say_hi()
4.2.2 类方法
类方法不对特定实例进行操作,在类方法中访问实例属性会导致错误 ; 类方法需要修饰器 “@classmethod” 来标识其为类方法; 对于类方法,第一个参数必须是类对象,一般以“cls” 作为第一个参数,类方法可通过实例对象去访问; 类方法的声明格式如下:
@ classmethod
def 类方法名(cls,[形参列表]):
函数体
类方法调用格式如下: 类名.类方法名([实参列表]) 例子13: 类方法还有一个用途即对类属性进行修改; 注意: 虽然类方法的第一个参数为 cls ,但调用时,用户不需要也不能给该参数传值; Python 自动把类对象传递给该参数;类对象与类的实例对象不同,在 Python 中,类本身也是对象;
4.2.3 静态方法
静态方法与类的对象实例无关的方法; 静态方法不对特定实例进行操作 ,在静态方法中访问对对象实例会导致错误 ; 静态方法需要通过修饰器“@staticmethod” 来进行修饰; 声明格式如下:
@staticmethod
def 静态方法名([形参列表])
函数体
静态方法名一般通过类名访问,或通过对象实例来调用, 调用格式如下:
类名.类方法名([实参列表])
从类方法和实例方法以及静态方法的定义形式就可看出来,类方法的第一个参数是 类对象 cls ,那么通过 cls 引用的必定是类对象的属性和方法 ;而实例方法的第一个参数是实例对象 self ,通过 self 引用的可能是类属性,也可能是实例属性,不过在 存在相同名称 的类属性和实例属性的情况下,实例属性的优先级更高; 静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用; 例子14:
4.3 绑定方法和非绑定方法
4.3.1 绑定方法
上面的对象方法和类方法都属于绑定方法; 实例方法必须通过实例调用 ,若直接通过类访问会抛出 异常 ,这种属于实例绑定方法; 类中方法默认都是绑定给对象用,当对象调用绑定方法时,会自动将对象作为第一个参数传递进去;通过类来调用,必须遵循函数参数一 一对应的规则,有几个参数,就必须传递几个参数; 例子15: 若一个方法时用了 @classmethod 修饰器,将这个方法绑定到类上,不管是对象来调用还是类调用,都将类作为一个参数传递进去; 例子16:
4.3.2 非绑定方法
非绑定方法与类或对象绑定,类和对象都可调用,但没有自动传值; 使用 @staticmethod 修饰的静态方法属于非绑定方法; 没有常规方法那样的特殊行为(绑定、非绑定、默认第一个参数规则等); 没有传值的普通函数并不是非绑定方法,只有被 staticmethod 修饰的才是非绑定方法; 例子17:
4.4 私有办法和公有办法
与私有属性相似,以双下滑线开始,但不以双下滑线结束的方法为私有方法,其他为公有方法; 以双下划线开始和结束的方法为 Python 的方法,不能直接访问私有方法,但在其他方法中访问; 例子18(私有方法和公有方法示例):
4.5 构造方法与析构方法
两种特殊的方法(函数):两个构造方法 [__init__(),__new__] 和一个析构方法 [__del__] ;
4.5.1 __init__() 方法
__init__() 方法是类的构造方法,用于执行类的实例的初始化工作,创建完实例后调用,初始化当前对象的实例,无返回值; 在默认情况,Python 自动建立一个没有任何操作的默认的 __init__() 方法,若用户建立自己的 __init__() 方法,将覆盖默认的__init__() ; __init__() 方法的例子如下: 例子19:
4.5.2 __new__() 方法
__new__() 方法是一个类方法,创建对象时调用,返回当前对象的一个实例,一般不需要重载方法; __new__() 方法的实例如下: 例子20: 调用 __init__() 调用之前,先调用 __new__() 方法; __new__() 至少要有一个参数 cls,代表要实例化的类; 这参数在实例化是由 Python 解释器自动提供; __new__() 必须要有返回值,返回实例化出来的实例; __init__() 有一个参数 self ,就是这个 __new__() 返回的实例; __init__() 在 __new__() 的基础上可完成一些其他初始化的动作,__init__() 不需要返回值; 若 __new__() 没有正确返回当前类 cls 的实例,那么 __init__() 是不会被调用的;
4.5.3 __del__() 方法
__del__() 析构方法用于实现销毁类的实例所需的操作,如释放占有的非托管资源(如打开的文件、网络链接等); 在默认情况,当对象不在被使用时,__del__() 方法运行,Python 解释器实现自动垃圾回收; __del__() 方法示例如下: 例子21:
4.6 特殊方法
·Python 对象中包含许多以双下滑线开始和结束的方法,称为特殊方法; 特殊方法通常在针对对象的某个操作时自动调用; Python 特殊方法如下图:
5. 继承
5.1 继承的概念
每个类至少有一个父类,这两个类之间的关系可描述为 “父 — 子” “超类 — 子类” “基类 — 派生类”的关系,是一种 “is - a” 的关系; 子类是从父类派生来的类,父类及其所哟高层类被认为是基类,子类可继承父类的任何属性; 父类是一个定义好的类,子类会继承父类的所有属性和方法,子类也能覆盖父类同名的变量和方法; 在传统类中,若子类和父类中有同名的方法或属性,在查找的时候遵循从左到右,深度优先的原则;
5.2 单继承
程序开发过程中,若定义了一个类 A,又想建立另一个类 B,但类 B的大部分内容与类 A 的相同,就不可能从头开始写一个类 B,可用类的继承的概念; 通过继承的方式新建类 B,让类 B继承类 A,类 B会 “遗传” 类 A的所有属性(数据属性和函数属性),实现代码重用; Python 支持单继承和多继承,当只有一个基类时为单继承; 单继承的声明格式如下:
class <类名>(基类名):
<类体>
若在类定义中没有指定基类,默认其基类为 object ;object 是所有对象的根基类,定义了公用方法的默认实现;
class Student:pass
等同于
calss Student(object):pass
例子22: 子类默认无__init__ ,则会直接继承基类__init__ ,那么子类会继承基类的属性; 若子类中有定义__init__ ,子类必须显式地在__init__() 函数中再次调用服了中的__init__() 函数; 调用格式如下:
基类名.__init__(self, 参数列表)
5.3 继承和抽象
继承是基于抽象的结果,通过编程语言去实现它,需先经历抽象过程,才能通过继承的方式去表达出抽象的结果; 抽象是程序分析与设计过程中中的一个动作或一种技巧,通过抽象可得到 类 ; 例子23:
class Student:
def __init__(self,name,age,stu_id):
self.name = name
self.age = age
self.stu_id = stu_id
def say_hi(self):
print('hello,my name is %s,%s years odl'%(self.name,self.age))
print('I am a student,my ID is:%s'%(self.stu_id))
class Teacher:
def __init__(self,name,age,tea_id):
self.name = name
self.age = age
self.tea_id = tea_id
def say_hi(self):
print('hello,my name is %s,%s years old'%(self.name,self.age))
print('I am a teacher,my ID is:%s'%(self.tea_id))
class Person:
def __init__(self,name,age,id):
self.name = name
self.age = age
self.id = id
def say_hi(self):
print('hello,my name is %s,%s years old'%(self.name,self.age))
print('My ID is:%s'%(self.id))
class Strudent(Person):
pass
class Teacher(Person):
pass
s1 = Student('王武',22,'2017101001')
s1.say_hi()
t1 = Teacher('张四',41,'11110')
t1.say_hi()
5.4 覆盖方法
通过继承,派生类继承基类中除构建方法外的所有成员;若在派生类中重新定义从基类继承的方法,则派生类中定义的方法覆盖从基类继承的方法; 覆盖方法的定义格式如下所示:
def 方法名(方法名与父类继承的方法名相同)
若在方法中调用父类方法,则调用格式如下:
父类名.方法名
例子24:
class Student:
def __init__(self,name,age,stu_id):
self.name = name
self.age = age
self.stu_id = stu_id
def say_hi(self):
print('hello,my name is %s,%s years odl'%(self.name,self.age))
print('I am a student,my ID is:%s'%(self.stu_id))
class Teacher:
def __init__(self,name,age,tea_id):
self.name = name
self.age = age
self.tea_id = tea_id
def say_hi(self):
print('hello,my name is %s,%s years old'%(self.name,self.age))
print('I am a teacher,my ID is:%s'%(self.tea_id))
class Persion:
def __init__(self,name,age,id):
self.name = name
self.age = age
self.id = id
def say_hi(self):
print('hello,my name is %s,%s years old'%(self.name,self.age))
class Student(Persion):
def say_hi(self):
Persion.say_hi(self)
print('I am a student,my ID is:%s'%(self.id))
s1 = Student('李武',20,'2019101011')
s1.say_hi()
t1 = Teacher('王弼',40,'10210')
t1.say_hi()
5.5.多重继承
Python 支持多重继承,当有多个基类时为多重继承, 定义格式如下:
class 基类(基类1,基类2,....,基类n)
基类
例子25: son 类同时继承 father1类和father2类,可访问fanther2类,可访问两个父类的属性和方法; 多重继承的目的是从两种继承树种分别并继承出子类,以便组合功能使用; 创建多线程模式的 TCPServer:
class MyTCPServer(tCPServer,ForkingMixin):
pass
创建多线程模式的 UDPServer:
class MyUDPServer(UDServer,ThreadinigMixin):
pass
若没有多重继承,要实现上述所哟可能的组合需要 4X2 = 8 个子类; Python 中的继承具有如下特点:
- 当派生类中国定义了
__int__() 方法,基类的 init() 方法不会被自动调用,需要在其派生类的 __init__() 方法中显示调用; - 在派生类中调用基类中调用基类的方法时,以基类的名作为前缀,,并以 self 作为第一个参数,区别于在类中调用普通函数时并不需要带上 self 参数;
- Python 总是首先在派生类中查找对应的方法,若派生类中没有找到对应的方法,他才开始到基类中逐个查找;
6. 多态和封装
面向对象编程语言包括三大特性:继承、多态和封装;Python 语言作为一门面向对象编程语言也不例外,除前面介绍的继承特性外,也具有多态和封装特性; 多态意味着不同类的对象使用相同的操作;封装意味着对外部隐藏对象的行为;
6.1 多态性
多态性即多种形态,在运动时确定其状态,在编译阶段无法确定其类型,这既多态; 如:序列形态有多种形态:字符串、列表、元组; 多态性指的是:向不同对象发送同一条消息,不同对象在接收时会产生不同的行为(即方法); 消息,就是调用函数,不同的行为就是指不同的行为就是指不同的实现,即执行不同的函数; 在继承关系中,派生类覆盖父类的同名方法,当调用同名方法时,系统会根据对象来判断执行哪个方法,就是多态性的体现; 例子26:
class Dimension:
def __init__(self,x,y):
self.x = x
self.y = y
def area(self):
pass
class Circle(Dimension):
def __init__(self,r):
Dimension.__init__(self,r,0)
def area(self):
return 3.14*self.x*self.y
class Sersion(Dimension):
def __init__(self,w,h):
Dimension.__init__(self,w,h)
def area(self):
return self.x*self.y
def func(obj):
return obj.area()
d1 = Circle(2.0)
d2 = Sersion(2.0,4.0)
print(func(d1))
print(fnnc(d2))
多态性的优点如下:
- 增加程序的灵活性,使用者都是同一种形式去调用;
- 增加程序的可扩展性;
6.2 封装和私有化
封装数据的主要原因是保护隐私;程序设计过程中,封装 (Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,程序外看不到,其含义是其他程序无法调用; 了解封装,离不开 “私有化 ”,即将类或函数中的某些属性限制在某个区域之内,外部无法调用; Python 通过在变量名前加双下滑线来实现“私有化 ”; 例如:__privatedate = 0,定义私有化方法是在方法名称前加上下滑线; 封装分为两个层面: 第一个层面的封装: 创建类和对象会分别创建二者的名称空间,只能用**“类名.”或“实例名.”**的方式去访问里面的属性和方法,也就是一种封装;
1. print(s1.name)
2. print(motor_vehicle.tag)
注意: 这一层面的封装(隐藏),“类名. ” 和 “实例化 .”就是访问隐藏属性的接口; 第二个层面的封装: 类把某些属性和方法 隐藏起来(或者定义成私有的),只在类的内部使用,外部无法访问,或留下少量接口(函数)提供外部访问; 例子27:
class B:
__a = 0
def __init__(self):
self.__a = 10
def __too(self):
print("from B")
def bar(self):
self.__too()
注意: 这一层面的封装(隐藏),需要在类中定义一个函数(接口函数)在类内部访问私有属性,之后外部就可使用了; 例子28:
class B:
def ble(self):
print("from B")
def test(self):
self.ble()
class C(B):
def ble(self):
print("from C")
c = C()
c.test()
将 ble() 定义成私有的,即__ble(),输出结果变成 “from B”,说明子类D没有覆盖父类B的__ble() 方法; 例子29:
class B:
def __ble(self):
print("from B")
def test(self):
self.__ble()
class D(B):
def __ble(self):
print("from D")
d = D()
d.test()
7. 定制类
在设计类的过程中,若想要类表现出一些特殊行为或能够响应某些内置函数或操作符,这就需要构架一些特殊方法; 这些特殊方法的标识是方法名下双下滑线 “__” 开头和结尾的;
7.1 __str__() 方法
若要把一个类的实例变成 str 类型,需要特殊方法 __str__() ; 例子30:
class Sersion:
def __init__(self,name,gender):
self.name = name
self.gender = gender
def __str__(self):
return '(Sersion:%s,%s)' % (self.name,self.gender)
若直接输入s,运行结果如下所示: Python 定义了 __str__() 和 __repr__() 两种方法,__str__() 用于显示给用户,而 __repr__() 用于显示给开发人员; 一般定义 __repr__() 的快捷方法时:
__repr__ = __str__
7.2 __len__ () 方法
若一个类表现的像一个 list,要获取多少个元素,就需要自定义特殊方法 __len__() ,返回元素的个数; 例子31:
class Sersion(object):
def __init__(self,num):
a,b,L = 0,1,[]
for n in range(num):
L.append(a)
a,b = b,a + b
self.number = L
def __str__(self):
return str(self.number)
__repr__ = __str__
def __len__(self):
return len(self.number)
s = Sersion(10)
print(s)
print(len(s))
7.3 __slota__
由于 Python 时动态语言,任何实例在运行期间都可动态地添加属性;若要限制添加属性, 例如 Sersion 类只允许添加 name、gender、score 这3个属性,则可利用 Python 的特殊方法 __slota__ 来实现; 例子32:
class Sersion(object):
__slots__ = ("name","gender","score")
def __init__(self,name,gender,score):
self.name = name
self.gender = gender
self.score = score
__slots__ 的目的是限制当前类所能拥有的属性,若不需要添加任意动态的属性,使用 __slots__ 也能节省内存;
7.4 __call__ 方法
一个类实例也可以变成一个可调用对象,只需实现一个特殊方法__call__() 。 例子33(将 Sersion 类变成一个可调用对象):
class Sersion(object):
def __init__(self,name,gender):
self.name = name
self.gender = gender
def __call__(self,friend):
print('My name is %s...' % self.name)
print('My friend is %s...' % friend)
例子34(通过实现__call__方法,将 Serson 的对象编程可带员工对象,可与上面的例子做对比):
class Serson(object):
def __call__(self,num):
a,b,L = 0,1,[]
for n in range(num):
L.append(a)
a,b = b,a + b
return L
s = Serson()
print(s(15))
8. 迭代器
迭代器是一个带状态的对象,能在调用 next() 方法的时候返回容器中的下一个值,任何实现了 __iter__() 和 __next__() 方法的对象都是迭代器, __iter__() 返回迭代器本身, __next__() 返回容器中的下一个值,若容器中无元素,则抛出 StopIteration 异常 ; 迭代器是实现了工厂模式的对象,在每次询问要下一个值的时候将下一个值返回; 例子35( itertools 函数返回都是迭代器对象): 若一个类计划被用于 for ... in 循环,类似于 list 或 tuple,就必须实现一个__iter__() 方法,该方法返回一个迭代对象; 然后,Python 的 for 循环就会不断调用该迭代对象的 __next__() 方法获取循环的下一个值,直到 StopIteration 异常 发生; 例子36:
class Sersion:
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
if self.curr > 100000:
raise StopIteration();
value = self.curr
self.curr += self.prev
self.prev = value
return value
9. 生成器
生成器是一种特殊的迭代器;生成器不会把结果保存在一个系列中,而是保存生成器的状态,在每次进行迭代时返回一个值,直到遇到 StopIteration 异常 结束; 不需要定义 __iter__ 与 __next__() 方法,只需 yield 关键字; 任何使用 yield 的函数都称为生成器; 例子37:
def count(n):
while n > 0 :
yield n
n += 1
例子38(用生成器实现斐波那契数):
def Sersion():
prev,curr = 0,1
while True:
yield curr
prev,curr = curr,curr + prev
结束语
若这篇文章有帮到你,给个赞,收个藏,欢迎大家评论; 若文章有什么错误,欢迎大家指教。
|