18 面向对象 - 三大特性
封装 继承 多态
18.1 封装
封装是使用特殊的语法,对成员属性和成员方法进行包装,达到保护和隐藏的目的
封装是为了限制一些访问和操作,但是不能全部都限制
被封装的成员只是限制了访问的权限,并不是不让访问
通常情况下,被封装的成员主要是供类的内部使用
18.1.1 封装的级别
被特殊语法封装的成员,会有不同的访问权限
'''
公有的 public 受保护的 protected 私有的 private
在类的内部 ok ok ok
在类的外部 ok no no
'''
在成员前面加一个_,受保护的成员
在成员前面加两个__,私有的
了解即可:
python中,对成员进行私有化,其实就是改了成员的名字,并不是真正的私有化
受保护 _成员
私有化 _类名__成员 (可以据此访问)
class Person():
name = 'one'
_age = 'one'
__sex = 'None'
def __init__(self,n,a,s):
self.name = n
self._age = a
self.__sex = s
def func(self):
# 在类的内部,可以操作任一成员
print(self._age)
print(self.__sex)
self._sing()
def __talk(self):
print('gossip and chatting')
def _sing(self):
print('sing ambitiously and loudly')
one = Person('yangmi','30','female')
# print(one.age) # 不可访问
# print(one.sex) # 不可访问
# one.sing()
one.func()
print(one._age)
# 据此访问 protected private
print(one._age)
print(one._Person__sex)
one._sing()
18.1.2 查看对象的成员
# 查看对象的所有成员
print(one.__dict__) # 可以获取对象自己的所有成员信息
# {'name': 'yangmi', 'age': '30', 'sex': 'female'}
print(Person.__dict__) # 可以获取当前类的所有成员信息
# {'__module__': '__main__', 'name': 'one', 'age': 'one', 'sex': 'None', '__init__': <function Person.__init__ at 0x0000000001E69318>, 'talk': <function Person.talk at 0x0000000001E693A8>, 'sing': <function Person.sing at 0x0000000001E69438>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# print(one.__dir__())
# ['name', 'age', 'sex', '__module__', '__init__', 'talk', 'sing', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
18.2 继承
一个类继承父类,这个类就拥有了父类中的所有成员,除了私有成员
18.2.1 继承的概念
被其他类继承的类,称为 父类 ,也叫 基类 或者 超类
继承其他类的类,称为 子类,也叫 派生类
继承的意义:
提高代码的重用性,建立新的类与类的关系,方便其他逻辑的操作
18.2.2 继承的语法格式
class 父类():
pass
class 子类(父类):
pass
特征:
- 在不指定继承的父类时,所有类都继承自系统提供的object类
- 子类继承父类,拥有了父类中的所有成员,包括魔术方法(除了私有成员)
- 子类继承父类,不会把父类复制给子类,而是引用父类
- 子类继承父类,可以重写父类中的方法,也可扩展父类没有的方法
- 子类重写父类方法,之后依然可以调用父类的方法,使用super().父类方法()的方式
子类可以有自己独立的成员,也可以没有
若子类继承父类后,重新定义了父类的方法,这种情况称为对父类方法的 重写
若子类继承父类后,定义了父类没有的方法,这种情况称为对父类方法的 扩展
子类调用方法时,若该方法有参数要求,需要传递参数
- 一个父类可以被多个子类继承,还可以存在 链式继承
链式继承:A类继承B类,B类继承C类,C类继承D类。。。
class Felid():
color = 'white'
variety = ''
age = ''
def run(self):
print('better run run')
def crawl(self):
print('climb up the tree')
class Cat(Felid):
def crawl(self):
# 子类中调用父类方法
super().crawl()
print('climb the shelf')
class Predator():
def feature1(self):
print('prey the prey')
class Puma(Felid,Predator):
pass
# 实例化
h = Cat()
# 调用成员时,先查对象自己的属性,==》 对象的类的属性,==》 父类的属性
h.run()
print(h.__dict__) # {}
# 继承关系 是继承引用,没有复制一份给子类
h.crawl()
b = Puma()
b.run()
b.feature1()
18.2.3 多继承
单继承:一个类只能继承一个父类的方式
多继承:一个类key继承多个父类的方式
注意!
多继承中,在子类调用父类方法时,super().父类方法() 调用的是第一个父类!
18.2.4 菱形继承 (钻石继承)
D类继承了B类和C类,然后B类和C类又分别继承了A类,这种继承关系称为 菱形继承
A
B C
D
class Human():
num = 44
def eat(self):
print(self.num)
print(self)
print('can eat raw meat')
class F(Human):
num = 33
def eat(self):
super().eat()
print(super().num)
print('eat meat with excellent appetite')
class M(Human):
num = 22
def eat(self):
super().eat()
print(super().num)
print('eat with grace manner')
class Son(F,M):
num = 11
def eat(self):
super().eat()
print(super().num)
print('eat as he likes')
a = Son()
a.eat()
# 每个子类FMS都有super().eat(),所有的方法都输出
# 子类S和第一个副子类F有,除了父类,其他都输出
# 子类S,M 有,输出F和S的方法
# 只有子类有,输出F和S的方法
# print(a.num)
'''
11
<__main__.Son object at 0x00000000021FD508>
can eat raw meat
44
eat with grace manner
22
eat meat with excellent appetite
33
eat as he likes
'''
mro( ) 获取mro列表,就是类的继承关系
print(Son.mro())
super( )
使用super去调用父级的方法时,实际上就是用super调用MRO列表中的上一级中的方法;
使用super去调用父级的属性时,实际上就是用super调用MRO列表中的上一级中的属性;
super()本身调用父级方法时,传递的self对象,就是这个方法中的那个self对象自己:
如,用a实例化的对象Son,方法中的self,就是a,之后super传递到上一级,self=a也一起传递,以此类推,整个调用过程self都只有a
18.2.5 继承关系检测
在实现继承的语法后,程序会自动生成一个继承的列表MRO(Method Relation Order)方法关系列表
MRO列表生成原则:
1.子类永远在父类的前面
2.同一等级的类,按照子类中的继承顺序摆放
2.先子类,后父类的顺序原则,最终的类为系统提供的object类
MRO 调用方法:类名.mro()
- super()调用时,并不是查找父类,而是去MRO列表找上一个类
- super()调用时,会自动把当前self传入到上一级的类的方法中
类关系检测 issubclass()
检测一个类是否是另一个类的子类
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B,C):
pass
# 获取类的MRO列表
print(D.mro())
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
# 检测类是否是另一个类的子类
res = issubclass(D,A)
print(res)
18.3 多态
对于同一个方法,由于调用对象(或者传入对象)的不同,产生了不同形态的结果
简单方法:
# 定义电脑类
class Computer():
# 在电脑类中定义一个usd 的规范的接口的方法
def usb(self,obj):
obj.start() # 注意!
# 定义鼠标类
class Mouse():
def start(self):
print('active mouse successfully, u can double click')
# 定义键盘类
class KeyBoard():
def start(self):
print('u can use keyboard now !')
# 定义U盘类
class Udisk():
def start(self):
print('active usb ')
# 实例化对象
c = Computer()
m = Mouse()
k = KeyBoard()
u = Udisk()
# 把不同的设备插入电脑的usb接口中
c.usb(m)
c.usb(k)
18.4 多态 - 继承版
'''
定义一个接口规范类,其它类都继承这个类,并实现(重写)父类中的方法
由于每个对象实现父类方法的方式或者过程都不相同,最后的结果是不一样的形态
'''
# 定义 USB 类
class USB():
'''
当前类的说明:
这个类是一个接口规范类,需要子类继承并实现start方法
start方法不作任何具体功能的实现
'''
# 在USB中定一个规范的借口方法,但是不实现任何功能
def start(self):
pass
# 定义鼠标类
class Mouse(USB):
def start(self):
print('active mouse, u can double click')
# 定义键盘类
class KeyBoard(USB):
def start(self):
print('u can use keyboard now !')
# 定义U盘类
class Udisk(USB):
def start(self):
print('active usb ')
r = issubclass(Mouse,USB)
# 实例化对象
m = Mouse()
k = KeyBoard()
u = Udisk()
m.start()