- 一、面向对象与面向过程的区别
- 二、对象和类
- 三、面向对象的特征
- 四、类的定义和对象的创建
- 五、方法
- 六、方法
- 6.1 对象方法
- 6.2 类方法
- 6.3 静态方法
- 6.4 类的内置方法
- 6.4.1 init(self)
- 6.4.2 del(self)
- 6.4.3 str(self)
- 6.4.4 repr(self)
- 6.4.5 call(self,*args)
- 6.4.6 getitem(self,index)
- 6.4.7 len(self)
- 6.4.8 add(self,other)
- 6.4.9 iadd(self,other)
- 6.4.10 sub(self,other)
- 6.4.11 isub(self.other)
- 6.4.12 mul(self,other)
- 6.4.13 imul(self,other)
- 6.4.14 mod(self,other)
- 6.4.15 imod(self,other)
- 6.4.16 neg(self)
- 6.4.17 pos(self)
- 6.4.18 判断大小的内置方法
- 七、继承
- 八、多态性
- 九、封装
Python既支持面向对象编程,也支持编写过程编程。它从设计之初是一门面向对象的语言。
一、面向对象与面向过程的区别
面向过程
面向过程(Procedure Oriented Programming)的程序设计方式是把求解问题中的数据定义为不同的数据结构,以功能为中心进行设计,用一个函数实现一个功能。为了简化程序设计,面向过程把函数继续切分成 子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
面向过程的程序设计方法中,所有的数据都是公用的,一个函数可以使用任何一组数据,而一组数据又能被多个函数使用,函数与其操作的数据是分离的;它的流程控制由程序中预定的顺序决定,通过分析得出解决问题所需要的步骤(功能),然后用函数把这些步骤一一实现,使用的时候再依次对函数进行调用。
面向对象
面向对象(Object Oriented Programing)的程序设计识别求解问题中的所有的独立个体都看作各自不同的对象,将数据和对数据的操作方法都封装再一起,数据和操作是一个相互依存、不可分割的整体,整个整体就是对象,将相同类型的对象的共性方法抽象就形成了类,为了类能够与外界进行联系,再类中必须声明一些函数(方法),这些方法用于与外界通信。
面向对象是以数据为中心描述系统,其流程控制由运行时各种事件的实际发生触发,而不再由预定顺序决定,它建立对象的目的不是为了完成一个个步骤,而是为了描述某个对象在整个解决问题的步骤中的行为。
将计算机程序视作一组对象的集合,每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
三个特点:数据封装、继承、多态
数据封装: 一个对象由一个或多个乘坐属性的数据项组成,在这些数据上可以执行一个或者多个称为服务的过程。隐藏对象的属性和实现细节,进对外提供公共的访问方式。类、方法就是封装的体现。
继承:子类重用父类代码。子类对象初始化时,会继承到父类中的数据,必须要看父类是如何对自己的数据进行初始化的。
多态:程序中定义的引用变量指向的具体数据类型和通过该引用变量发出的方法在调用程序的时候不确定,而是在程序运行期间才确定。不用修改源代码,就可以让引用变量绑定各种不同的类实现上,从而导致该调用的具体方法随之实现,让程序可以选择多个运行状态。
多态分为编译时多态和运行时多态,编译时,多态是静止的,主要是方法的重载,它是根据参数列表的不同来区分不同的函数,通过编译之后会成为两个不同的函数。运行时多态是动态的,它是通过动态绑定来实现的。
面向对象以对象为最基本的元素,是一种由对象、类、封装、继承和多态等概念构造系统的软件开发方法。
二、对象和类
2.1 对象(object)
对象是面向对象技术的核心。
对象是现实世界中客观存在的某种事物,可以将人们感兴趣或者要加以研究的事、物和概念等都称为对象。
对象既能表示结构化的数据,也能表示抽象的事件、规则以及复杂的工程实体等。eg:人、山、水、交通工具等,也可以是生活中的一种逻辑结构或抽象概念(班级、部门等)。
对象是类的实例化。对象分为静态特征和动态特征两种。
静态特征是指对象的外观、性质、属性等,动态特征是指对象具有的功能、行为等。
静态特征被抽象为属性,用数据来描述,在Python语言中称之为变量。
动态特征被抽象为行为,用一组代码来表示,完成对数据的操作,在Python语言中称之为方法(method)。
一个对象由一组属性和一系列对属性进行操作的方法构成。
对象的组成:
1.一组包含各种类型数据的属性
2.对属性中的数据进行操作的相关方法
在面向对象的系统中,对象是一个将数据属性和操作行为封装起来的实体。数据描述了对象的状态,是对象的静态特性;操作用来描述对象的动态特性,是行为或功能,可以操纵数据,改变对象的状态。
Python中的一切内容都可以是对象,不一定必须是某个类的实例。eg:字符串、列表、元组、字典等都内置数据类型都可以具有和类完全相似的语法和用法。
2.2 类 (class)
将具有相同属性及相同行为的一组对象称为类。
类是人们对客观事物的高度抽象。抽象是指抓住事物的本质特征,找出事物之间的共性,并将具有共同特性的事物划分为一类,得到一个抽象的概念。eg:人、汽车、房屋、水果等都是类的例子。
在面向对象程序设计中,类是一个独立的单位,有一个类名,其内部包括成员变量和成员方法 ,分别用于描述对象的属性和行为。
类是由使用封装的数据及操作这些数据的接口函数组成的一群对象的集合。
类还可以有子类和父类,子类通过对父类的继承,形成层次结构。
2.2 类和对象的关系
类和对象的关系受抽象和具体的关系,类的作用是定义对象,类给出了属于该类的全部对象的抽象定义,而对象是类的具体化,是符合这种定义的一个类的实例。
对象是类的实例化结果,对象的抽象就是类
三、面向对象的特征
面向对象方法(Object-Oriented Method)是一种把面向对象的思想应用于软件开发过程中,指导开发活动的系统方法,简称OO(Object-Oriented)方法。
对象是由数据和允许的操作组成的封装体,与客观实体有者直接对应关系。一个对象类定义了具有相似性质的一对对象,而继承性和对具有层次关系的类的属性和操作进行共享的一种方式。
3.1 封装(Encapsulation)
封装是指将对象的数据项和行为(数据和操作)包裹起来形成一个封装体。该封装体内包含对象的属性和行为,对象的属性由若干个数据组成,而对象的行为则由若干个操作组成,这些操作是通过函数实现的,也称为方法。
封装体具有独立性和隐藏性。
独立性是指封装体内所包含的属性和行为构成了一个不可分割的独立单位。
隐藏性是指封装体内的某些成员(数据或方法)在封装体外是不可见,既不能被访问,也不能被改变,这部分成员被隐藏了,具有安全性。
一般来说,封装体和外界的联系是通过接口函数进行的。
3.2 继承(Inheritance)
继承的本质就是行为共享,通过行为共享,可以减少冗余性,提高软件的重用性。
通过继承的特性,衍生类(derived class)继承了其基本类(base class)的成员变量(data member)与类方法(class method)。衍生类具有基本类的全部属性和行为,并且还具有自己特殊的属性和行为。
衍生类也叫次类(subclass)、子类(child class)或者派生类。
基础类也叫父类(parent class)。
3.3 多态性(Polymorphism)
多态性是指一种行为对应多种不同的实现,即对象根据接收的消息而做出动作,同样的消息在被不同的对象接收的时候可以产生完全不同的结果。
多态允许一个函数有多种不同的接口。依照调用函数使用的参数,类知道使用哪一种接口。
Python使用动态类型(dynamic typing)与后期绑定(latebinding)实现多态功能。
四、类的定义和对象的创建
4.1 类的定义
使用class关键字定义类,并在类中定义属性(数据成员)和方法(成员函数)
class
<属性定义 类的帮助信息> ##类文档字符串
< classsuite 方法定义 > ##类体
ClassName为类名
类的帮助信息可以通过ClassName.doc查看
class_suite由类成员、方法、数据属性组成。
ClassName. name__ 属性返回此类的名称
ClassName. module__ 属性返回此类的模块名称
ClassName. bases_ 属性是一个tuple(元组)对象,返回此类的基类名称。
class Person: ##声明类Personnumber = 0 ##类属性 ##类属性def __init__(self,name,gender,age): ##构造类的函数self.name=name ##初始化对象属性self.gender=genderself.age=agePerson.number+=1def displayPersion(self): ##类的方法print("Name:",self.name,"Gender:",self.gender,"Age:",self.age)def displayNumber(self): ##类的方法print("Total person:",Person.number)
1.number为类属性,name、gender和age为实例属性,displayPersion()和 displayNumber()为方法。
2.init()是类的特殊方法,称为构造函数,在创建对象的时候系统自动调用,用于初始化实例属性(对象属性)
3.self是类中定义方法的时候参数表的一个参数,定义方法的时候,参数列表至少要有一个参数,通常self被指定为第一个参数,表示所创建的对象
class Cc:"这是一个类的例子"a = 100print (Cc.__bases__)class a(Cc):"a 继承类Cc"b=100print(a.__bases__)##结果(<class 'object'>,)(<class '__main__.Cc'>,)
4.2 对象的创建
对象是类的实例(class instance),对象的创建过程也是类的实例化过程。每一个Python对象都包含识别码(identify)、对象类型(object type)、属性(attribute)、方法(method)、数值(value)等属性。
构造方法(constructor),是指创建对象的时候其本身所运行的函数。
Python中使用init() 函数作为对象的构造方法。
当用户要在对象内指向对象本身的时候,可以使用self关键字。self是必要的参数且为第一个参数。用户可以在init()构造方法
创建对象和调用函数类似,如果构造函数init()声明有参数,还需要传入相应的参数,创建对象之后,还要把它赋给一个变量,使该变量指向对象,否则将无法引入所创建的对象。
class Person:number = 0def __init__(self,name,gender,age):self.name=nameself.gender=genderself.age=agePerson.number+=1def displayPersion(self):print("Name:",self.name,"Gender:",self.gender,"Age:",self.age)def displayNumber(self):print("Total person:",Person.number)print(Person.__name__)stu1 = Person("lars","M",19) ##创建对象stu1并初始化,stu1是一个类实例变量stu2 = Person("Gyon","G",200) ##创建对象stu2并初始化stu1.displayPersion() ##调用方法显示对象stu1的属性stu2.displayPersion() ##调用方法显示对象stu2的属性print("Total students:",Person.number) ##输出类属性numberstu1.displayNumber() ##调用方法显示类属性numberstu2.displayNumber() ##调用方法显示类属性numberprint(stu1.name) ##访问对象的属性print(id(f)) ##返回类的识别码print(type(f)) ##放回类的对象类型##结果PersonName: lars Gender: M Age: 19Name: Gyon Gender: G Age: 200Total students: 2Total person: 2Total person: 2lars2637844051144<class '__main__.Person'>
1.创建对象的时候需要指定相应的参数,在构造函数调用的时候进行参数的传递
2.调用类的方法的时候需要使用 “ . ”操作符指明调用哪个对象的方法,eg:stu1.displayPersion(),访问属性也可以通过“.”直接进行,eg:stu1.age或Person.number
3.显示类属性number的时候,既可以直接输出,也可以调用对象方法displayNumber()实现。
在类定义之外也可以根据需要通过对象随时添加、修改或删除属性。
属性添加、修改和删除
##属性添加、修改和删除class Person:number = 0def __init__(self,name,gender,age):self.name=nameself.gender=genderself.age=agePerson.number+=1def displayPersion(self):print("Name:",self.name,"Gender:",self.gender,"Age:",self.age)def displayNumber(self):print("Total person:",Person.number)print(Person.__name__)stu1 = Person("lars","M",19)stu2 = Person("Gyon","G",20)stu1.score=100 ##添加属性scoreprint(stu1.name,"的分数",stu1.score)stu2.score=99 ##添加属性scoreprint(stu2.name,"的分数",stu2.score)stu1.displayPersion()stu2.displayPersion()print("Total students:",Person.number)stu1.age=20 ##修改属性 agestu1.displayPersion()del stu1.score ##删除属性scorestu1.displayPersion() ##对于输出displayPersion()无影响del stu1.name ##删除属性nameprint(stu1.name)##结果Traceback (most recent call last):PersonFile "C:/Users/lh/.PyCharm2017.1/config/scratches/scratch_1.py", line 65, in <module>lars 的分数 100print(stu1.name) 由于删除name属性会报错Gyon 的分数 99AttributeError: 'Person' object has no attribute 'name'Name: lars Gender: M Age: 19Name: Gyon Gender: G Age: 20Total students: 2Name: lars Gender: M Age: 20Name: lars Gender: M Age: 20
通过调用函数实现属性添加、修改和删除
1.函数getattr(对象 , 属性名)的功能使访问对象的属性
2.函数hasattr(对象 , 属性名)的功能使检查是否存在一个属性,结果为逻辑值,存在为True,不存在为False。
3.函数setattr(对象 , 属性名 , 属性值)的功能是设置一个属性,如果属性不存在,则创建一个新属性
4.函数delattr(对象 , 属性名)的功能是删除属性。
class Person:number = 0def __init__(self,name,gender,age):self.name=nameself.gender=genderself.age=agePerson.number+=1def displayPersion(self):print("Name:",self.name,"Gender:",self.gender,"Age:",self.age)def displayNumber(self):print("Total person:",Person.number)stu1 = Person("lars","M",19)stu2 = Person("Gyon","G",20)setattr(stu1,'score',99) ##创建一个新属性score,并赋值print(getattr(stu1,'score')) ##返回属性的值print(stu1.score)delattr(stu1,'score') ##删除属性的值print(hasattr(stu1,'score')) ##如果存在属性score,就返回True,不存在,就返回False##结果9999False
五、方法
类是由属性和方法组成的,属性是对数据的封装,方法是对象所具有的行为。
属性和方法又可以分为公有的和私有的。在Python中,属性和方法的公有和私有是通过标识符的约定区分的。
5.1 类属性和对象属性
根据所属的对象,Python的属性分为类属性和对象属性(实例属性)两种。
类属性是在类方法之外定义的属性,既可以通过类名访问,也可以通过对象名访问。
对象属性只为单独的特定对象拥有,可以在类外显示定义,也可以在类的构造函数init() 中定义,定义的时候以self作为前缀,且只能通过对象名访问。
5.2 公有属性和私有属性
Python的公有属性和私有属性通过属性命名方式区分,如果属性名以两个下划线开头,说明是私有属性,否则是公有属性。
私有属性的访问形式如下:
<类(对象)名>.<__类名__私有属性名>
class Car:salesPrice = 15000__manufacturePrice = 12000def __init__(self,brand,serial):self.brand =brandself.__serial=serialprint("公有类属性:",Car.salesPrice)print("私有类属性:",Car._Car__manufacturePrice)c1=Car('奔驰',"丰田")print("Public data brand if c1 ",c1.brand)print("Private data serial of c1",c1._Car__serial)##结果公有类属性: 15000私有类属性: 12000Public data brand if c1 奔驰Private data serial of c1 丰田公有类属性salesPrice,可以通过类名直接访问Car.salesPrice私有类属性__manufacturePrice,需要通过特定方式访问Car._Car__manufacturePrice类的公有对象属性brand可以直接通过对象名访问c1.brand私有对象属性需要通过特定方式访问c1._Car__serial
六、方法
Python方法可以分为公有方法和私有方法、类方法和静态方法。
6.1 对象方法
公有方法和私有方法都属于对象方法,公有方法的定义无序特别说明,而私有方法在定义的时候,方法名要以两个下划线开头。
每个对象都有自己的公有方法和私有方法,在这两类方法中可以访问类和对象的属性,这两类方法也都可以通过类或者对象调用。
公有方法通过对象名直接嗲用,但是私有方法不能通过对象名直接调用,只能在属于对象的公有方法中通过self调用。
如果通过类的方式调用方法,则必须传入一个对象,而且在调用私有方法的时候需要采用下面的方式
<类(对象)名>.<__类名__私有方法名>()
class Methods:def publicMethoed1(self): ##定义公有方法print('公有方法 publicMethoed !')def __privateMethod(self): ##定义私有方法print('私有方法 privateMethod !')def publicMethoed2(self): ##定义公有方法self.__privateMethod()m=Methods()m.publicMethoed1() ##通过对象调用公有方法Methods.publicMethoed1(m) ##通过类名调用公有方法m.publicMethoed2() ##通过对象的公有方法调用私有方法m._Methods__privateMethod() ##通过对象名调用私有方法Methods._Methods__privateMethod(m) ##通过类名调用私有方法Methods._Methods__privateMethod() ##通过类名调用私有方法,不传入一个对象##结果公有方法 publicMethoed !公有方法 publicMethoed !私有方法 privateMethod !私有方法 privateMethod !私有方法 privateMethod !Traceback (most recent call last):File "e:/pythonstduy/lei.py", line 25, in <module>Methods._Methods__privateMethod()TypeError: __privateMethod() missing 1 required positional argument: 'self'##由于通过类名调用私有方法的时候,没有传入一个对象,所有出现了这个报错。
6.2 类方法
类方法属于类,可以通过Python的装饰器@classmethod定义,也可以通过使用内置函数classmethod()的方式将一个普通的方法转换为类方法。
类方法可以通过类名或者对象名访问。
class Methods:@classmethoddef publicClassMethod(cls): ##定义公有方法print("公有类方法 publicClassMethod ")@classmethoddef __privateClassMethod(cls): ##定义私有方法print("私有类方法 privateClassMethod ")def publicMethoed(self): ##定义公有方法print("普通公有类方法 publicMethoed ")def __privateMethod(self): ##定义私有方法print("普通私有类方法 privateClassMethod ")publicMethoedToClassMethod=classmethod(publicMethoed) ##转换为类方法privateMethodToClassMethod=classmethod(__privateMethod) ##转换为类方法m = Methods()m.publicClassMethod()Methods.publicClassMethod()m._Methods__privateClassMethod()Methods._Methods__privateClassMethod()m.publicMethoedToClassMethod()Methods.publicMethoedToClassMethod()m.privateMethodToClassMethod()Methods.privateMethodToClassMethod()##结果公有类方法 publicClassMethod公有类方法 publicClassMethod私有类方法 privateClassMethod私有类方法 privateClassMethod普通公有类方法 publicMethoed普通公有类方法 publicMethoed普通私有类方法 privateClassMethod普通私有类方法 privateClassMethod
在定义类方法的时候,一般使用cls作为类方法的第一个参数名称,在调用类方法的时候不需要为该参数传递值。
类方法可以通过类名或者对象名访问。eg:m.publicClassMethod()、Methods.publicClassMethod()、m._MethodsprivateClassMethod()、Methods._MethodsprivateClassMethod()。
6.3 静态方法
静态方法也属于类,可以通过Python的修饰器@staticmethod定义,也同样可以通过使用内置函数staticmethod()将一个普通的方法转换为静态方法。
class Methods:@staticmethoddef publiceStaticMethod(): ##定义公有静态方法print("公有静态方法:publiceStaticMethod")@staticmethoddef __privateStaticMethod(): ##定义私有静态方法print("私有静态方法:privateStaticMethod ")def publicMethoed(self): ##定义公有方法print("普通公有方法:publicMethoed")def __privateMethod(self): ##定义私有方法print("普通私有方法:privateMethod ")publiceMethodToStaticMethod=staticmethod(publicMethoed) ##转换为静态方法privateMethodToStaticMethod=staticmethod(__privateMethod) ##转换为静态方法m=Methods()m.publiceStaticMethod()Methods.publiceStaticMethod()m._Methods__privateStaticMethod()Methods._Methods__privateStaticMethod()m.publiceMethodToStaticMethod(m)Methods.publiceMethodToStaticMethod(m)m.privateMethodToStaticMethod(m)Methods.privateMethodToStaticMethod(m)##结果公有静态方法:publiceStaticMethod公有静态方法:publiceStaticMethod私有静态方法:privateStaticMethod私有静态方法:privateStaticMethod普通公有方法:publicMethoed普通公有方法:publicMethoed普通私有方法:privateMethod普通私有方法:privateMethod
类定义静态方法的时候,不需要传入self参数或者cls参数。
静态方法可以通过类名或者对象名访问,eg:m.publiceStaticMethod()、Methods.publiceStaticMethod()、m._MethodsprivateStaticMethod()、Methods._MethodsprivateStaticMethod()。
publiceMethodToStaticMethod()是经过转换得到的静态方法,在调用的是必须传入一个参数。
6.4 类的内置方法
类本身有很多内置方法,这些内置方法的开头和结尾都带有下划线 __
6.4.1 init(self)
这个是类的构造方法,当创建一个类的实例(也就是对象)的时候,系统会自动调用,用来为对象分配内存并且默认为属性进行初始化。
class Cc:def __init__(self):print(self)Cc()##结果<__main__.Cc object at 0x000001C1ABCE6D08>
6.4.2 del(self)
del()方法也称析构函数,用来解释对象占用的存储空间,在Python删除对象和收回对象存储空间的时候被自动调用和执行。如果对象没有编写析构函数,则Python将提供一个默认的析构函数。
class Person:def __init__(self,name,gender,age):self.name = nameself.gender = genderself.age = agedef __del__(self):print('调用析构函数:',self.name,self.gender,self.age)stu1 = Person("lars","M",19)stu2 = Person("gyon","F",20)print("Name:",stu1.name,'Gender:',stu1.gender,'Age:',stu1.age)print("Name:",stu2.name,'Gender:',stu2.gender,'Age:',stu2.age)del stu1del stu2print(stu1)##结果Name: lars Gender: M Age: 19Name: gyon Gender: F Age: 20调用析构函数: lars M 19调用析构函数: gyon F 20Traceback (most recent call last):File "e:/pythonstduy/lei.py", line 139, in <module>print(stu1)NameError: name 'stu1' is not defined##调用析构函数之后,对象会被删除
6.4.3 str(self)
该方法被内置函数str()和print函数调用,用来设置对象以字符串类型出现时如何显示。
str(self)函数的返回值是一个字符串。
class Cc:def __init__(self,arg):self.name = argdef __str__(self):return self.namec=Cc("苹果")print (c)##结果苹果
6.4.4 repr(self)
该方法被repr()内置函数调用,可以让对象以可读的形式出现。
class Cc:def __init__(self,arg):self.name =argdef __repr__(self):return self.nameprint(Cc('香蕉'))##结果香蕉
6.4.5 call(self,*args)
包含该方法的类是可以被调用的
class addNumber:def __init__(self,arg):self.value=argdef __call__(self,other):return self.value+otherx = addNumber(1000)print(x(200))##结果1200返回的结果是原来的name的属性值与调用的时候的参数相加的结果
6.4.6 getitem(self,index)
该方法支持列表对象的索引,返回self[index]的值
class Seq:def __getitem__(self,index):return index+1s = Seq()for i in range(8):print(s[i])##结果12345678
6.4.7 len(self)
该方法用于len()内置函数中,显示类实例变量的长度。
class Cc:def __init__(self,arg):self.name = argdef __len__(self):return len(self.name)c = Cc("吹梦到西洲")print(len(c))##结果5
6.4.8 add(self,other)
用于计算self+other的值
class addNumber:def __init__(self,x,y):self.x=xself.y=ydef __add__(self,other):return(self.x+other.x+self.y+other.y)x = addNumber(2,4)y = addNumber(7,3)print(x,y)print(x+y)##结果<__main__.addNumber object at 0x000002953DE66EC8> <__main__.addNumber object at 0x000002953DE66FC8>16
6.4.9 iadd(self,other)
用于计算self+=other的值
class iaddnumber:def __init__(self,arg):self.value = argdef __iadd__(self,other):return self.value+other.valuex=iaddnumber(10)y=iaddnumber(20)x+=yprint(x)##结果30
6.4.10 sub(self,other)
用于计算self-other的值
class subNumber:def __init__(self,value):self.value = valuedef __sub__(self,other):return (self.value-other.value)x=subNumber(50)y=subNumber(40)print(x-y)##结果10
6.4.11 isub(self.other)
用于计算self-=other
class isubNumber:def __init__(self,value):self.value = valuedef __isub__(self,other):return (self.value-other.value)x=isubNumber(50)y=isubNumber(40)x-=yprint(x)##结果10
6.4.12 mul(self,other)
用于计算self*other的值
class mulNumber:def __init__(self,value):self.value = valuedef __mul__(self,other):return (self.value*other.value)x=mulNumber(50)y=mulNumber(4)print(x*y)##结果200
6.4.13 imul(self,other)
用于计算self*=other的值
class imulNumber:def __init__(self,value):self.value = valuedef __imul__(self,other):return (self.value*other.value)x=imulNumber(50)y=imulNumber(40)x*=yprint(x)##结果2000
6.4.14 mod(self,other)
用于计算self%other的值
class modNumber:def __init__(self,value):self.value = valuedef __mod__(self,other):return (self.value%other.value)x=modNumber(50)y=modNumber(4)print(x%y)##结果2
6.4.15 imod(self,other)
用于计算self%=other的值
class imodNumber:def __init__(self,value):self.value = valuedef __imod__(self,other):return (self.value%other.value)x=imodNumber(50)y=imodNumber(40)x%=yprint(x)##结果10
6.4.16 neg(self)
用于计算-self的值
class negNumber:def __init__(self,value):self.value=valuedef __neg__(self):return -self.valuex=negNumber(990)print(-x)print(type(-x))##结果-990<class 'int'>
6.4.17 pos(self)
用于计算+self的值
class posNumber:def __init__(self,value):self.value=valuedef __pos__(self):return self.valuex=posNumber(-990)print(+x)print(type(+x))##结果-990<class 'int'>
6.4.18 判断大小的内置方法
| 方法 | 说明 |
|---|---|
| gt(self,other) | 判断self对象是否大于other对象 |
| lt(self,other) | 判断self对象是否小于other对象 |
| ge(self,other) | 判断self对象是否大于或者等于other对象 |
| le(self,other) | 判断self对象是否小于或者等于other对象 |
| eq(self,other) | 判断self对象是否等于other对象 |
七、继承
7.1 继承和派生的概念
类的继承(inheritance),就是新类继承旧类的属性与方法,这种行为称为派生子类(subclassing)。继承的新类称为派生类(derived class),被继承的旧类则称为基类(base class)。
当用户创建派生类后,就可以在派生类内新增或者改写基类的任何方法。
派生类也可以作为其他类的基类,这个过程可以一直进行下去,从一个基类派生出来的多层次类就形成了类的层次结构。
7.2 派生类的定义
定义格式:
class <派生类名>(<基类名>):def __init__(self[,<参数>]):<基类名>.__init__(self[,<参数>])<新增属性定义>
派生类定义的时候必须指定基类类名,通常在类的定义的时候都会包含init()构造函数,需要先定义派生类的构造函数,并在派生类的基类中调用基类的构造函数,并传入以必要的参数,用于初始化基类的属性,然后再通过赋值语句初始化派生类中新增加的属性成员。
class Person: ##基类定义def __init__(self,name,gender,age):self.name=nameself.gender=genderself.age=agedef display(self):print("NAME:",self.name,"GENDER:",self.gender,"Age:",self.age)class Student(Person): ##派生类定义def __init__(self,num,major,name,gender,age): ##派生类构造函数Person.__init__(self,name,gender,age) ##调用基类构造函数self.num = num ##派生类新增属性self.major=major ##派生类新增属性def displayStudent(self):print("Number:",self.num,"Major:",self.major)Person.display(self) ##调用基类方法stu1 = Student("001","Chinese","lars","boy",19)stu2 = Student("002","English","gyon","boy",20)stu1.displayStudent()stu2.displayStudent()
派生类在调用基类方法的时候,一般采用非绑定的类方法,即通过类名方法基类的方法,并在参数列表中引入对象self,从而达到调用基类方法的目的。
但是Person.init(self,name,gender,age)的方式调用,当基类的类名改动或者派生类继承其他类的时候需要修改的内容很多。可以使用Python内置的super()函数调用基类方法。
class Person: ##基类定义def __init__(self,name,gender,age):self.name=nameself.gender=genderself.age=agedef display(self):print("NAME:",self.name,"GENDER:",self.gender,"Age:",self.age)class Student(Person): ##派生类定义def __init__(self,num,major,name,gender,age): ##派生类构造函数super(Student,self).__init__(name,gender,age) ##调用基类构造函数self.num = num ##派生类新增属性self.major=major ##派生类新增属性def displayStudent(self):print("Number:",self.num,"Major:",self.major)super(Student,self).display() ##调用基类方法stu1 = Student("001","Chinese","lars","boy",19)stu2 = Student("002","English","gyon","boy",20)stu1.displayStudent()stu2.displayStudent()##结果Number: 001 Major: ChineseNAME: lars GENDER: boy Age: 19Number: 002 Major: EnglishNAME: gyon GENDER: boy Age: 20
使用super()内置函数调用基类中的方法,当基类的名称改变或者派生类改为继承其他类的时候,只需要修改派生类继承基类的名称即可。
7.3 派生类的组成
派生类中属性和方法包括从基类继承的属性和方法,以及在派生类中新增加的属性和方法两个部分。从基类继承的属性和方法体现了派生类从基类继承而获得的共性,而新增加的属性和方法则体现了派生类的个性。
派生类对象包括基类成员和派生类成员两个部分。
派生类在构造过程中,完成的工作:
1.从基类接收属性和方法
基类的全部成员,包括所有属性和方法都被派生类继承,作为派生类成员的一部分。
2.调整基类的属性和方法
基类的属性和方法不能有选择地继承,但是可以对这些属性和方法进行调整。可以通过在派生类中声明一个与基类同名的属性和方法,这样在派生类中的新属性和新方法将会覆盖基类的同名属性和方法。
如果要重新定义方法,不仅方法名要相同,而且方法的参数表也要相同,否则方法即为重载而不是覆盖。
3.定义新增属性和方法
在派生类中可以增加新的属性和方法来拓展基类的功能。
7.4 名称空间的搜索顺序
当用户在类内编写函数的时候,类函数名称空间的搜索顺序是:类的实例——->类——->基类
7.5 多继承
一个派生类有两个或多个基类,派生类从两个或多个基类中继承所属的属性和方法。
Python允许一个派生类同时继承多个基类,称为多继承(Multiple Inheritance)
class Student(): ##基类Studentdef __init__(self,num,name,gender):self.num=numself.name=nameself.gender=genderdef displayStudent(self):print('num:%s,name:%s,gender:%s'%(self.num,self.name,self.gender))class Teacher(): ##基类Teacherdef __init__(self,title,major,subject):self.title=titleself.major=majorself.subject=subjectdef displayTeacher(self):print('tilte:%s,major:%s,subject:%s'%(self.title,self.major,self.subject))class Assistant(Student,Teacher): ##派生类Assistantdef __init__(self,num,name,gender,title,major,subject,salary):Student.__init__(self,num,name,gender)Teacher.__init__(self,title,major,subject)self.salary=salarydef displayAssistant(self):super(Assistant,self).displayStudent()super(Assistant,self).displayTeacher()print("salary",self.salary)lhuan = Assistant("12",'lhuan','M',"zhujiao","Chinese","c++",8000)lhuan.displayAssistant()##结果num:12,name:lhuan,gender:Mtilte:zhujiao,major:Chinese,subject:c++salary 8000
ps:在派生类的构造函数中不能使用super()内置函数的方式调用基类构造函数,但是在 displayAssistant()方法中可以使用super()内置函数的方式调用基类的方法,派生类有两个基类,且这两个基类有同名的方法,使用super()函数不能分辨出调用哪一个基类的方法,如果参数数目相同的话,可能出现某些基类的方法多次调用,而某些基类的方法一次都没有被调用。
八、多态性
类的多态(polymorphism)指类可以有多个名称相同、参数类型却不同的函数。
如果用户在Python的类内声明多个名称相同、参数却不同的函数,Python会使用类内最后一个声明的函数。
多态是指基类的同一个方法在不同派生类中具有不同的表现和行为,派生类继承了基类的行为和属性后,还会增加某些特定的行为和数学,同时还可能对继承来的某些行为进行一定的改变。
Python通过使用方法重载和运算符重载两种方式实现多态性。
8.1 方法重载
方法重载就是在派生类中使用与基类完全相同的方法名。
class Aniaml(): ## 基类Aniamldef display(self):print("I am an aniaml")class Dog(Aniaml): #派生类Dogdef display(self): ##方法重写print("I am a dog")class Cat(Aniaml): #派生类Catdef display(self): ##方法重写print("I am a cat")class Wolf(Aniaml): #派生类Wolfdef display(self): ##方法重写print("I am a Wolf")x = [item() for item in(Aniaml,Dog,Cat,Wolf)]for i in x:i.display()##结果I am an aniamlI am a dogI am a catI am a Wolf
8.2 重载运算符
类的很多内置方法是用来替换运算符的,这种特性称为重载运算符(overloading operator)
在Python解释器内使用这些运算符函数,必须先加载operator模块,然后调用operator模块中的运算符。
import operatorprint(operator.add(100,200))##结果300
| 重载运算符 | 函数 | 说明 |
|---|---|---|
| add(a,b) | add(a,b) | 返回a+b,a与b是数字 |
| sub(a,b) | sub(a,b) | 返回a-b,a与b是数字 |
| mul(a,b) | mul(a,b) | 返回a*b,,a与b是数字 |
| mod(a,b) | mod(a,b) | 返回a%b |
| neg(a,b) | neg(a,b) | 返回-a |
| pos(a,b) | pos(a,b) | 返回+a |
| abs(a,b) | abs(a,b) | 返回a的绝对值 |
| inv(a,b) | inv(a,b) | 返回a的二进制反码 原来位为1,结果为0 原来位为0,结果为1 |
| invert(a,b) | invert(a,b) | 与inv的结果相反 |
| lshift(a,b) | lshift(a,b) | 返回a左移b位的结果 |
| rshift(a,b) | rshift(a,b) | 返回a右移b位的结果 |
class Number():def __init__(self,a,b):self.a =aself.b = bdef __add__(self,x): ##重载加return Number(self.a+x.a,self.b+x.b)def __sub__(self,x): ##重载减return Number(self.a-x.a,self.b-x.b)n1= Number(10,20)n2 =Number(100,200)m = n1+n2p = n1-n2print(m.a,m.b)print(p.a,p.b)
九、封装
类的封装(encapsulation)指类将其属性(方法和变量)封装在该类中,只有该类中的成员才可以使用该类中的其他成员。
这种被封装的变量和方法,称为该类的私有变量(private variable)和私有方法(private method)。
Python类中所有的变量与方法都是公用的(public)。只要知道该类的名称和该变量或方法的名称,任何外部对象都可以直接存取类中的属性与方法。
做到类的封装,需要遵循的原则:
1.如果属性(变量与方法)名称的第一个字符是单下滑线,那么该属性视为类的内部变量,外部的变量不可以引用该属性
2.如果属性(变量或方法)名称的前两个字符都是单下滑线,那么在编译的时候属性名称会自动加上单下划线。
