面向对象三大特性 封装 继承 多态 鸭子类型
1 继承
1:定义
继承描叙的是两个类之间的关系,一个类可以直接使用另一个类中已定义的方法和属性; 被继承的称之为父类或基类,继承父类的类称之为子类;
1.减少代码重复 2.为多态提供必要的支持
3 继承的使用
1 先抽象在继承
# 抽取老师和学生的相同内容 形成一个新的类,作为它们的公共父类
class Person:
def __init__(self,name,gender,age):
self.name = name
self.gender = gender
self.age = age
def say_hi(self):
print("hi my name is %s age is %s gender is %s" % (self.name,self.age,self.gender))
class Teacher(Person): #指定Teacher类继承Person类
pass
class Student(Person): #指定Student类继承Person类
pass
#创建两个对象
t1 = Teacher("Jack","man",20)
t1.say_hi()
s1 = Student("Maria","woman",20)
s1.say_hi()
当父类提供的属性无法完全满足子类的需求时,子类可以增加自己的属性或非法,或者覆盖父类已经存在的属性,此时子类称之为父类的派生类;
覆盖
在子类中如果出现于父类相同的属性名称时,根据查找顺序,优先使用子类中的属性,这种行为也称为`覆盖`
# 抽取老师和学生的相同内容 形成一个新的类,作为它们的公共父类
class Person:
def __init__(self,name,gender,age):
self.name = name
self.gender = gender
self.age = age
def say_hi(self):
print("my name is %s age is %s gender is %s" % (self.name,self.age,self.gender))
class Teacher(Person): #指定Teacher类继承Person类
# Teacher类从Person类中继承到了say_hi方法 但是,老师打招呼时应当说出自己的职业是老师,所以需要
# 定义自己的不同的实现方式
def say_hi(self):
print("hi i am a Teacher")
#print("my name is %s age is %s gender is %s" % (self.name,self.age,self.gender))
#上一行代码与父类中完全相同,可以直接调用父类提供的方法
Person.say_hi(self)
# 创建Teacher对象
t1 = Teacher("Jack","man",20)
t1.say_hi()
#输出 hi i am a Teacher
# my name is Jack age is 20 gender is man
在子类中有两种方式可以重用父类中的代码
1.使用类名直接调用 ,该方式与继承没有关系,即时没有继承关系,也可以调用
2.使用super()
class Vehicle: #定义交通工具类
Country='China'
def __init__(self,name,speed,load,power):
self.name=name
self.speed=speed
self.load=load
self.power=power
def run(self):
print('开动啦...')
class Subway(Vehicle): #地铁
def __init__(self,name,speed,load,power,line):
#super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
super().__init__(name,speed,load,power)
self.line=line
def run(self):
print('地铁%s号线欢迎您' %self.line)
super(Subway,self).run()
class Mobike(Vehicle):#摩拜单车
pass
line13=Subway('中国地铁','180m/s','1000人/箱','电',13)
line13.run()
'''
地铁13号线欢迎您
开动啦...
'''
4 经典类与新式类
那什么是经典类,什么是新式类呢?
# Author:Zhang Zhao
class A(object):
def __init__(self):
print('A')
class B(A):
pass
# def __init__(self):
# print('B')
class C(A):
def __init__(self):
print('C')
class D(B,C):
pass
# def __init__(self):
# print('D')
r1 = D()
但是在经典类中,如果B中找不到,它会优先考虑B的父亲A,而不是C。
在python3中,都是遵循广度优先的规则,在python2.7以前,应该是遵循深度优先的的规则。两种规则没有优劣之分
#A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
def test(self):
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
c=C()
c.test() #打印结果:from B
print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
5 继承的顺序
对于你定义的每一个类,python通过一个算法算出一个查找顺序存放在(MRO)列表中,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如:
F.mro() #等同于F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
class Equip: #武器装备类
def fire(self):
print('release Fire skill')
class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
camp='Noxus'
def __init__(self,nickname):
self.nickname=nickname
self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性
r1=Riven('锐雯雯')
r1.equip.fire() #可以使用组合的类产生的对象所持有的方法
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
2.组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...
2 封装
1 定义
''' 什么是封装 :对外部隐藏属性和方法 给外部提供使用的接口 目的:限制外部对内部数据的访问 因为有些数据是机密的 不方便对外透露 如何封装: __开头的语法 分为封装属性 和 封装方法 封装的好处: 封装属性--提高安全性 封装方法:提高便利性 '''
2 封装属性 方法
一.
对于属性而言,封装就为了限制属性的访问和修改,其目的是为了保护数据安全
例如:
学生对象拥有,姓名,性别,年龄,和身份证号,分数;其中身份证是一个相对隐私的数据,不应该让外界访问到;
分数属性,是一个非常关键的数据,决定学员能不能正常毕业,不应被随意修改;
class Student:
def __init__(self,name,age,id_card):
self.name = name
self.age = age
self.__id_card = id_card # 外部就无法通过.id_card 和 .__id_card
def show_id_card(self):
print(self.__id_card)
stu = Student('tom',18,'xxxxxxxx')
print(stu.name) # tom
print(stu.age) # 18
# print(stu.__id_card) 是没有这个的
stu.show_id_card() # xxxxxxxx
对私有属性的访问以及修改
class Student:
def __init__(self,name,age,id_card):
self.name = name
self.age = age
self.__id_card = id_card # 外部就无法通过.id_card 和 .__id_card
def get_id_card(self,pwd): # 访问被封装的属性 称之为访问器 可以为访问添加条件
if pwd == '123':
return self.__id_card
else:
print('密码错误')
def set_id_card(self,new_id_card): # 修改被封装的属性称之为设置器
# 身份证必须是字符 且 是18位
if isinstance(new_id_card,str) and len(new_id_card) == 18:
self.__id_card = new_id_card
else:
print('新身份证不符合规定')
stu = Student('tom',18,'123456789012345678')
stu.get_id_card('wsx') # 密码错误
stu.set_id_card('123456789123456789') # 无结果
二.封装方法
说明这方法可以被内部访问 不应该被外界访问
一个大的功能很多情况下是由很多个小功能组合而成的,而这些内部的小功能对于用户而言是没有意义的,所以封装方法的目的是为了隔离复杂度;
例如:
电脑的开机功能,内部需要启动BIOS,读取系统配置,启动硬盘,载入操作系统,等等一系列复杂的操作,但是用户不需要关心这些实现逻辑,只要按下开机键等待开机即可;
class ATM:
def withdraw(self):
self.__user_auth()
self.__input_money()
self.__save_record()
# 输入账号和密码
# 显示余额
# 输入取款金额
# 保存记录
def __user_auth(self):
print('输入用户名和密码')
def __input_money(self):
print('余额为1000000,输入取款金额!')
def __save_record(self):
print('记录流水......')
atm = ATM()
atm.withdraw() # 提现只需要调用withdraw就可以了 不需要一步一步去操作其他三个步骤 简化了外部操作
三 封装特性property
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
@property封装时 特别要注意 封装的属性需要._
property的用途一 :将方法伪装成普通属性
''' 作用:将方法伪装成普通属性 为什么要用property 希望将私有属性的访问方式和普通属性一致 普通属性 点语法 与property相关的 两个装饰器 setter 用点语法 给属性赋值时触发 deleter 用点语法删除属性时触发 '''
class Teacher:
def __init__(self,name,age,salary):
self.name = name
self.age = age
self.__salary = salary # s实质是在名称空间里换名字为 _Teacher__salary
@property
def salary(self): # getter # 用于访问私有属性的值 也可以访问普通属性
return self.__salary
@salary.setter # 用来设置私有属性的值 也可以设置普通属性
def salary(self,new_salary):
self.__salary = new_salary
@salary.deleter
def salary(self):
del self.__dict__['_Teacher__salary'] # 删除老师薪水部分
a = Teacher('jeck',18,50) # 这是访问私有属性
print(a.salary) # 50
a.salary=10 # 赋值时运用 setter
print(a.salary) # 10
del a.salary # 已删除salary部分
# print(a.salary) 这时打印会报错的
class Foo:
def __init__(self,val):
self.__NAME=val #将所有的数据属性都隐藏起来
@property
def name(self):
return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
@name.setter
def name(self,value):
if not isinstance(value,str): #在设定值之前进行类型检查
raise TypeError('%s must be str' %value)
self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
@name.deleter
def name(self):
raise TypeError('Can not delete')
f=Foo('Jack')
print(f.name) # 访问property属性
#输出 Jack
f.name="Rose" # 修改property属性 抛出异常'TypeError: 10 must be str'
del f.name # 删除property属性 抛出异常'TypeError: Can not delete'
property的用途二 :计算属性
''' 有些属性不是固定的 是需要计算出来的 这是也可以用到property 例如计算BMI 体质指数(BMI)=体重(kg)÷身高^2(m) '''
class People:
def __init__(self,name,weight,height):
self.name=name
self.weight=weight
self.height=height
@property
def BMI(self):
return self.weight/(self.height**2)
@BMI.setter
def BMI(self,new_bmi):
print('bmi不支持自定义')
p = People('egon',80,1.7)
print(p.BMI) # 27.68166089965398
p.BMI = 10 # 运行这步 不会更改值 bmi不支持自定义
print(p.BMI) # 27.68166089965398 结果还是这个不会变的 计算属性时 setter 不受用
在属性名前添加两个下划线__,将其设置为私有的
class Student:
def __init__(self, name, gender, age, id, score): # 初始化函数
self.name = name
self.gender = gender
self.age = age
self.__id = id # 将id设置为私有的
self.__score = score # 将score设置为私有的
def test(self):
print(self.__id)
print(self.__score)
stu = Student("Jack", "man", 20, "320684198901010001", 780)
# 1.访问私有属性测试
# print(stu.id) # 直接访问到隐私数据
# print(stu.__id) # 换种写法
# 以上两行代码均输出相似的错误
# AttributeError: 'Student' object has no attribute 'id'
# 错误含义 在Student类的对象中没有一个id或__id属性
# 2.修改私有属性测试
stu.score = 1 # 直接修改私有属性 由于语法特点,相当于给stu对象增加score属性
stu.__score = 2 # 直接修改私有属性 由于语法特点,相当于给stu对象增加__score属性
print(stu.score,)
print(stu.__score)
# 输出 1
# 输出 2
# 看起来已经被修改了 调用函数来查看私有属性是否修改成功
stu.test()
# 输出 320684198901010001
# 输出 780
# 私有的数据没有被修改过
3 多态
1 定义
多态指的是一类事物有多种形态 例如: 动物有多种形态: 人,狗,猪 在程序中多态指的是,不同对象可以响应相同方法,并可以有自己不同的实现方式
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): #动物的形态之一:人
def talk(self):
print('say hello')
class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('say aoao')
peo=People()
dog=Dog()
pig=Pig()
#peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()
#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
obj.talk()
func(peo)
func(dog)
func(pig)
1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)*
2.增加了程序额可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用*
class Cat(Animal): #动物的另外一种形态:猫
def talk(self):
print('say miao')
def func(animal): #对于使用者来说,自己的代码根本无需改动
animal.talk()
cat1=Cat() #实例出一只猫
func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
say miao
'''
这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)
'''
4 鸭子类型
1 定义
如果一个对象叫声像鸭子,走路像鸭子,长得像鸭子,那它就是鸭子
class PC():
def conntent_device(self, usb_device):
usb_device.open()
usb_device.work()
usb_device.close()
class Mouse:
# 实现接口规定的所有功能
def open(self):
print("mouse opened")
def work(self):
print("mouse working...")
def close(self):
print("mouse closed")
mouse = Mouse()
pc = PC()
pc.conntent_device(mouse)
class KeyBoard:
def open(self):
print("KeyBoard opened")
def work(self):
print("KeyBoard working...")
def close(self):
print("KeyBoard closed")
key1 = KeyBoard()
# 如果key1的特征和行为都像USB设备 那就把它当做USB设备来使用
# 对于使用者而言可以不用关心这个对象是什么类,是如如何是实现,
pc.conntent_device(key1)
'''
mouse opened
mouse working...
mouse closed
KeyBoard opened
KeyBoard working...
KeyBoard closed
'''

浙公网安备 33010602011771号