九、面向对象程序设计
两大编程思想:面向过程和面向对象。面向过程:功能上的封装;面向对象:属性和行为上的封装。两者的异同点为:
类和对象
类是由N多个对象抽取出“像”的属性和行为从而归纳总结出来的一种类别。
类的组成:
类由类属性、实例属性、实例方法、静态方法和类方法组成。下面分别对组成的个部分进行说明:
类属性:直接定义在类中,方法外的变量
实例属性:定义在__init__方法中,使用self打点的变量
实例方法:定义在类中的函数,而且自带参数self
静态方法:使用装饰器@staticmethod修饰的方法
类方法:使用装饰器@classmethod修饰的方法
【类中的变量称为属性(成员变量);类中的函数称为方法(成员函数)】
class Student:
#类属性:定义在类中,方法外的变量
school='xxx大学'
#初始方法
def __init__(self,xingming,age):#xingming,age是方法的参数,是局部变狼,xingming,age的作用域是整个__init__方法
self.name=xingming#"="左侧是实例属性,xingming是局部变量,将局部变量得的值xingming赋值给实例属性self.name
self.age=age#实例的名称和局部变量的名称可以相同
#实例方法(定义在类中的函数,称为方法,自带一个参数self)
def show(self):
print(f'我叫:{self.name},今年:{self.age}岁了')
#静态方法
@staticmethod
def sm():
print('这是一个静态方法,不能调用实例属性,也不能调用实例方法')
#类方法
@classmethod
def cm(cls):#cls是class的简写
print('这是一个类方法,不能调用实例属性,也不能调用实例方法')
#创建类的对象
stu=Student('qwe',18)#为什么传了两个参数,因为__init__方法中有两个形参,self是自带的参数,无需手动输入
#实例属性,使用对象名打点进行调用
print(stu.name,stu.age)
#类属性,直接使用类名,打点调用
print(Student.school)
#实例方法,使用对象名打点进行调用
stu.show()
#类方法, @classmethod进行修饰的方法,直接使用类名打点调用
Student.cm()
#静态方法,直接使用类名打点调用
Student.sm()
使用类模板创建N多个对象:
class Student:
#类属性:定义在类中,方法外的变量
school='xxx大学'
#初始方法
def __init__(self,xingming,age):#xingming,age是方法的参数,是局部变狼,xingming,age的作用域是整个__init__方法
self.name=xingming#"="左侧是实例属性,xingming是局部变量,将局部变量得的值xingming赋值给实例属性self.name
self.age=age#实例的名称和局部变量的名称可以相同
#实例方法(定义在类中的函数,称为方法,自带一个参数self)
def show(self):
print(f'我叫:{self.name},今年:{self.age}岁了')
#根据“图纸”可以创建N多个对象
stu1=Student('one',1)
stu2=Student('two',2)
stu3=Student('three',3)
stu4=Student('four',4)
Student.school='daxue'#给类的属性赋值
#将学生对象存储到列表中
lst=[stu1,stu2,stu3,stu4]#列表中的元素是Student类型的对象
for item in lst:#item是列表中的元素,所以是Student类型的对象
item.show()#对象名打点调用实例方法
动态绑定属性和方法
类是模板是图纸,可以创建N多个同类型的对象,由于类型是相同的,所以每个对象的属性名称都是相同的,但是属性值却可以不相同。
class Student:
#类属性:定义在类中,方法外的变量
school='xxx大学'
#初始方法
def __init__(self,xingming,age):#xingming,age是方法的参数,是局部变狼,xingming,age的作用域是整个__init__方法
self.name=xingming#"="左侧是实例属性,xingming是局部变量,将局部变量得的值xingming赋值给实例属性self.name
self.age=age#实例的名称和局部变量的名称可以相同
#实例方法(定义在类中的函数,称为方法,自带一个参数self)
def show(self):
print(f'我叫:{self.name},今年:{self.age}岁了')
#创建两个student类型的对象
stu1=Student('one',1)
stu2=Student('two',2)
print(stu1.name,stu1.age)
print(stu2.name,stu2.age)
#为stu2动态绑定一个实例属性
stu2.gender='男'
print(stu2.name,stu2.age,stu2.gender)
#动态绑定方法
def introduce():
print('我是一个普通的函数,被动态绑定成了stu2对象的方法')
stu2.fun=introduce#introduce后不能加(),因为这里是进行赋值,不是进行调用
#fun就是stu2对象的方法了
#调用
stu2.fun()
面向对象的特征
面向对象的三大特征:封装、继承和多态。封装是指隐藏内部细节,对外提供操作方式;继承是指在函数调用时,使用“形参名称=值”的方式进行传参,传递参数顺序可以与定义时参数的顺序不同;多态是指在函数定义时,直接对形式参数进行赋值,在调用时如果该参数不传值,将使用默认值,如果该参数传值,则使用传递的值。
封装
权限控制是通过对属性或方法添加单下划线、双下划线以及首尾双下划线来是实现的。
单下划线开头:以单下划线开头的属性或方法表示受保护的成员,这类成员被视为仅供内部使用,允许类本身和子类进行访问,但实际上它可以被外部代码访问;
双下划线开头:表示私有成员,这类成员只允许定义该属性或方法的类本身进行访问;
首尾双下划线:一般表示特殊的方法
class Student:
#首尾双下划线
def __init__(self,name,age,gender):
self._name=name#self._name受保护,只能本类和子类访问
self.__age=age#self.__age是私有的,只能类本身去访问
self.gender=gender#普通的实例属性,类的内部、外部及子类都可以访问
def _fun1(self):
print('子类及本身可以访问')
def __fun2(self):
print('只有定义的类可以访问')
def show(self):#普通的实例方法
self._fun1()#类本身访问受保护的方法
self.__fun2()#类本身访问私有方法
print(self._name)#受保护的实例属性
print(self.__age)#私有的实例属性
#创建一个学生类的对象
stu=Student('qwe',21,'nv')
#类的外部
print(stu._name)#在类的外部去访问受保护的类的属性
#print(stu.__age)#访问私有属性:AttributeError: 'Student' object has no attribute '__age'
#调用受保护的实例方法
stu._fun1()#子类及本身可以访问
#私有方法
#stu.__fun2()#AttributeError: 'Student' object has no attribute '__fun2'
#私有属性和方法的访问
print(stu._Student__age)
stu._Student__fun2()
若要访问私有的实例属性,可以使用@property修改方法,将方法转成属性使用:
class Student():
def __init__(self,name,gender):
self.name=name
self.__gender=gender#self.__gender是私有的实例属性
#使用@property修改方法,将方法转换成属性使用
@property
def gender(self):
return self.__gender
stu=Student('qqq','nan')
print(stu.name,stu.gender)
继承
继承是面向对象编程中的一个重要概念,允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以重用父类的代码,并且可以在其基础上添加新功能或修改已有的功能。
在python中一个子类可以继承N多个父类;一个父类也可以拥有N多个子类;如果一个类没有继承任何类,那么这个类默认继承的是object类。
继承的语法结构为:
calss 类名(父类1,父类2,...父类N)
pass
class Person:#()可以省略,父类默认继承了object
def __init__(self,name,age):
self.name=name
self.age=age
def show(self):
print(f'大家好,我叫:{self.name},今年{self.age}岁')
#Student类继承Person类
class Student(Person):
#编写初始化方法
def __init__(self,name,age,stuno):
super().__init__(name,age)#调用父类的初始化方法,给实例属性赋值
self.stuno=stuno
#Doctor类继承Person类
class Doctor(Person):
def __init__(self,name,age,deparment):
super().__init__(name,age)
self.deparment=deparment
#创建第一个子类的对象
stu=Student('qwe',12,1222,)
stu.show()
#创建第二个子类的对象
doctor=Doctor('qwa',22,'ww')
doctor.show()
一个子类若继承多个父类,多个父类之间使用英文的逗号进行分割。它继承多个父类,就拥有了多个父类的公有的成员和受保护的成员:
class FatherA():
def __init__(self,name):
self.name=name
def showA(self):
print('父类A中的方法')
class FatherB():
def __init__(self,age):
self.age=age
def showB(self):
print('父类B中的方法')
#多继承
class Son(FatherA,FatherB):
def __init__(self,name,age,gender):
#需要调用两个父类的初始化方法
FatherA.__init__(self,name)
FatherB.__init__(self,age)
self.gender=gender
son=Son('qqq',122,'nan')
son.showA()
son.showB()
方法重写
子类继承了父类就拥有了父类当中共有的成员和受保护的成员,父类的方法并不能完全适应子类的需求的时候子类就可以重写父类的方法。子类在重写父类的方法时,要求方法的名称必须与父类方法的名称相同,在子类重写后的方法中,可以通过super().xxx()调用父类中的方法。
class Person:#()可以省略,父类默认继承了object
def __init__(self,name,age):
self.name=name
self.age=age
def show(self):
print(f'大家好,我叫:{self.name},今年{self.age}岁')
#Student类继承Person类
class Student(Person):
#编写初始化方法
def __init__(self,name,age,stuno):
super().__init__(name,age)#调用父类的初始化方法,给实例属性赋值
self.stuno=stuno
def show(self):
#调用父类中的show方法
super().show()
print(f'我的学号是:{self.stuno}')
#Doctor类继承Person类
class Doctor(Person):
def __init__(self,name,age,deparment):
super().__init__(name,age)
self.deparment=deparment
def show(self):
#super().show()#此处没有调用show方法,完全重写了方法体
print(f'大家好,我叫{self.name},今年{self.age},来自{self.deparment}')
#创建第一个子类的对象
stu=Student('qwe',12,1222,)
stu.show()
#创建第二个子类的对象
doctor=Doctor('qwa',22,'ww')
doctor.show()
多态
多态指的就是“多种状态”,即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用对象的方法;在程序运行过程中,根据变量所引用对象的数据类型,动态决定调用哪个对象中的方法;python语言中的多态,根本不关心对象的数据类型,也不关心类之间是否存在继承关系,只关心对象的行为(方法)。只要不同的类中有同名的方法,即可实现多态。(可实现程序的可扩展性)
class Person():
#此处没有写初始化方法。初始化方法是有实例属性的时候,在初始化方法中赋值,如果没有实例属性,可以不写初始化方法
def eat(self):
print('人,吃五谷杂粮')
class Cat():
def eat(self):
print('猫,吃猫粮')
class Dog():
def eat(self):
print('狗,啃骨头')
#编写函数
def fun(obj):#obj是函数的形式参数,在定义处不知道这个形参的数据类型
obj.eat()#通过变量obj(对象)调用eat方法
#创建三个类的对象
per=Person()
cat=Cat()
dog=Dog()
#调用fun函数
fun(per)#python中的多态不关心对象的数据类型,只关心对象是否具有同名方法
fun(cat)
fun(dog)
object类
是所有类直接或间接的父类,所有类都具有object类的属性和方法。
object类中的特殊方法及其功能描述:
__new__():由系统调用,用于创建对象
__init__():创建对象时自动调用,用于初始化对象属性值
(执行顺序:先执行父类当中的__new__()方法去创建对象,开辟内存空间,创建完成之后再由__init__()方法给实例属性赋值)
__str__():对象的描述,返回值是str类型,默认输出对象的内存地址