面向对象(3)
面向对象
1、面向对象三大特性
# 面向对象的三大特性
'''
1、封装:
什么是封装:
所谓的封装就是将重复的代码封装到一起,
封装的目的:
封装的目的是每个对象在使用的时候可以减少冗余的代码量
如何封装:
对象封装之前说了就是把一个个对象封装成一个类,
在产生对象的时候只需要调用类就可以了,而在类里定义了每个对象都共有的属性和方法,
调用的时候简单了很多
2、继承:
什么是继承:
所谓的继承就是另外一种定义类的方式,继承的思想就是一种什么是什么的概念,
被继承的类称为父类或基类,继承类称为父类的子类或者派生类
继承的目的:
参照封装的目的,继承则是将拥有同一功能的类都继承于或者说是遗传于一个父类,
其目的也是为了减少每个子类里重复的代码,继承就是子类拥有了父类的一些功能和属性,
使得每个子类在使用这种功能或属性的时候,可以直接从父类中找到
如何继承:
继承的基本结构
class School:
pass
class Student(School):
pass
class Teacher(School):
pass
继承的语法结构就是类名()括号里写上父类的名字,即该类是括号里类的子类或者派生类
3、多态性:
多态性又是什么?????
'''
2、继承
在python中,继承又可分为两种:
1、单继承
单继承是指,只有一个父类或基类,即只有一个爹
2、多继承
多继承只有在python里面有,是指有多个父类或基类,即有多个爹
继承的核心思想:什么'是'什么的思维习惯
在提到类时,就必然会产生属性的查找顺序这一概念,那么在继承里,属性的查找顺序又是什么样的?
# 单继承里的属性查找
# 示例一:
class Foo:
def f(self):
print('from Foo')
def f3(self):
print('from Foo')
class F1(Foo):
def f1(self):
print('from F1')
class F2(F1):
def f3(self):
print('from F2')
class F(F2):
def f3(self):
print('from F')
obj1 = F()
obj1.f3() # from F
obj1.f1() # from F1
obj1.f() # from Foo from F2
# 示例二:
class Foo:
def __F(self): # _Foo__F
print('from Foo')
def F(self):
self.__F() # _Foo__F
class F1(Foo):
def f1(self):
print('from F1')
class F2(F1):
def f2(self):
print('from F2')
class F(F2):
def __F(self): # _F__F
print('from F')
obj1 = F()
obj1.F() # from Foo
'''
从以上两个实例中,我们可以得出在单继承中属性查找的顺序为:
对象本身>>>产生对象的类>>>父类>>>父类的父类>>>>.....
其实我们可以通过类的内置方法__base__方法来查看当前类的父类是谁
'''
class F1():
def f1(self):
print('from F1')
class F2(F1):
def f2(self):
print('from F2')
print(F2.__base__) # <class '__main__.F1'>
# 多继承里的属性查找
'''
在讲多继承之前,先引进两个额外的概念:新生类与经典类
经典类:没有继承object类或者object子类等
新式类:继承了object类或者object的子类的类等
在python2中,有经典类和新式类之分,在python3中一律为新式类,这是因为,在python3中,默认所有的类都是继承于object类
示例:
python2中:
class F: # 经典类
pass
class F(object): # 新式类
pass
python3中:
表现方式一:class F: # 新式类 ==>> 表现方式二: class F(object):
pass pass
在python3中是默认所有的类皆继承于object类,至于object类是什么类,这是python内部定义的一个类,不必深究,
但是python3里的新生类如果想要在python3里正常显示,必须要指明继承于object类,
在python2里如果不指明,则默认为经典类,无继承的父类
'''
# 以python3为例
# 示例一:
class A:
def text(self):
print('from A')
class B(A):
# def text(self):
# print('from B')
pass
class C(B):
# def text(self):
# print('from C')
pass
class D(A):
# def text(self):
# print('from D')
pass
class E(D):
def text(self):
print('from E')
class F(C, E):
pass
print(F.__bases__) # (<class '__main__.C'>, <class '__main__.E'>)
obj = F()
obj.text() # from E
# 示例二:
class B:
# def text(self):
# print('from B')
pass
class C(B):
# def text(self):
# print('from C')
pass
class D:
def text(self):
print('from D')
class E(D):
# def text(self):
# print('from E')
pass
class F(C, E):
pass
print(F.__bases__) # (<class '__main__.C'>, <class '__main__.E'>)
obj = F()
obj.text() # from D
'''
对于多继承属性的查找顺序,要引入另外两个概念:
多属性查找遵循mro列表,mro列表就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表
非菱形查找:只适用于经典类里的多继承属性查找,先从最左边的父类开始查找,找不到再从后面的开始找,
所谓的非菱形也就是说,所有的父类或者父类的父类皆没有一个共同的父类,而python3中所有的类都默认是object的子类,
因此,python3里的多继承查找皆不是非菱形查找
菱形查找:适用于新式类里的多继承属性查找,所谓的菱形查找,恰恰相反,即要查找的属性的父类皆有一个共同的父类,且在python2和python3中
菱形查找所遵循的原则也有说不同,这里就先下个结论:
python2中:左边第一个开始查找,第一次就直接一条道走到黑,找到父类的共同父类,
如果找不到,则在下面的查找中,不再从共同的父类中查找,这种方法又叫深度优先查找
python3中:从左边第一个开始查找,不会从共同的父类中查找,
当找到最后一个父类还没找到时,再从父类共同的类里面查找,这种方法又叫广度优先查找
'''
'''
牵扯到继承时,就引出了另外一个概念:
在子类派生的新方法中如何重用父类的功能????
'''
# 先从单继承开始说
# 方式一:指名道姓的找 ==>> 不依赖于继承
class School:
School = 'old_boy'
def __init__(self, name, age):
self.name = name
self.age = age
class Student(School):
def __init__(self, name, age):
School.__init__(self, name, age) # 指名道姓的调用类里的方法
class Teacher(School):
def __init__(self, name, age, salary, level):
School.__init__(self, name, age) # 指名道姓的调用类里的方法
self.salary = salary
self.level = level
obj1 = School('jason', 18)
print(obj1.name) # jason
obj2 = Teacher('json', 20, 3000, 10)
print(obj2.name) # json
'''
这种指名道姓调用的方法严格来说,并不属于继承里独有的属性查找方式,
下面介绍另外一种调用的方式:这里再调用两个概念:
super()方法
mro列表
先从实例中慢慢摸索
'''
# 方式二:使用super()方法 ==>>严格依赖于继承关系
class School:
School = 'old_boy'
def __init__(self, name, age):
self.name = name
self.age = age
class Student(School):
def __init__(self, name, age):
# 在python2中:super(当前类,对象).__init__(self, name, age)
# 在python3中,super不需要填参数
super().__init__(name, age)
class Teacher(School):
def __init__(self, name, age, salary, level):
# 在python2中:super(当前类,对象).__init__(self, name, age)
# 在python3中,super不需要填参数
super().__init__(name, age)
self.salary = salary
self.level = level
obj = Student('jason', 18)
print(obj.__dict__) # {'name': 'jason', 'age': 18}
obj2 = Teacher('egon', 20, 3000, 10)
print(obj2.__dict__) # {'name': 'egon', 'age': 20, 'salary': 3000, 'level': 10}
'''
在单继承里使用super()方法调用类里的方法时,super()方法会返回一个特殊的对象,
该对象参照发起属性查找的类的mro列表,从当前类的下一个类(父类)里进行查找
'''
# 多继承下如何调用父类里的方法
'''
在利用单继承来调用父类里的方法时,我们可以用指名道姓的方式,也可以用super()方法直接调用,
但是在多继承里调用时,指名道姓这种查找顺序就不再试用了,因为会涉及到父类的查找顺序,而在
多继承里,我们使用super()这种方法,遵循mro列表里的类查找顺序
'''
# 示例一:
class B(object):
# def text(self):
# pass
pass
class C(B):
# def text(self):
# print('from C')
pass
class D(object):
def text(self):
print('from D')
class E(D):
# def text(self):
# print('from E')
pass
class F(C, E):
pass
#
obj = F()
print(F.mro()) # [<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class 'object'>]
obj.text() # from D
# 示例二:
class A(object):
def text(self):
print('from A')
class B(A):
def text(self):
super().text()
class C(B):
# def text(self):
# print('from C')
pass
class D(A):
# def text(self):
# print('from D')
pass
class E(D):
# def text(self):
# print('from E')
pass
class F(C, E):
pass
obj = F()
print(F.mro()) # [<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
obj.text() # from A
'''
这就提到了我们之前说的,在python3中,多继承查找顺序,采用广度优先查找顺序,
而类的mro列表,其实就是多继承在查找属性时的顺序
而super()方法我们也可以在这里定义:
super()方法会产生一个特殊的对象,该对象会参照发起属性查找的类(发起者)的mro列表,
从当前类的后面的类里查找,不能再次返回找,找不到即报错
'''
'''
在讲多继承之前,我们提到了只有python中才存在有多继承的问题,那么多继承是不是就是完美的?
答案当然不是,没有任何一种方法是完美的,那么多继承的利与弊又是什么?
利:可以继承多个父类的属性与功能,最大限度的重用父类的功能
弊:1、打破了人正常的思维逻辑:什么'是'什么
2、在进行属性查找和子类里派生的新方法重用父类里的方法时,查找起来很麻烦,且很费时间,
使得代码的可读性变差,进而导致扩展性变差,那么如何避免这种情况?
下面就讲述一下多继承正确打开的方式之:Mixins机制
'''
# Mixin机制核心:在多继承的背景下,尽可能的提升多继承的可读性
# 父类就是一个标识,即子类是什么,让子类有一个归属
# 示例:
class Vehicle:
pass
class FlyableMixin:
def fly(self):
pass
class Plane(FlyableMixin, Vehicle): # 意思就是混合功能,即给飞机混合进去飞行的功能,实际上飞机的父类只有一个,还是交通工具类
pass
class Car(Vehicle):
pass
'''
Mixin方法实际上只是混入了一些功能,意即临时混入的一些功能,并不是指是谁的父类
使用Mixins多继承时要注意以下几点:
1、只能存放某种功能,而非物品,python对于mixin类的一般定义为mixin结尾或者able结尾
2、责任必须单一,如果有多个功能,则写多个mixin类,一个类可以继承多个mixin类,为了遵循
继承里什么什么'是'什么的思维逻辑,只能继承一个标识其归属的父类
3、不依赖于子类的实现,是一个独立的类,和子类是没有关系的,只是附加给子类的一种功能
4、子类即便没有mixin类,依旧可以正常实现,只是不具备了mixin类里的功能而已
但是Mixins的本质还是多继承,写的越多,依旧会造成代码的可读性变差
'''
3、多态
- 什么是多态:
多态指的是同一种事物有多种形态
现实生活中:
eg:人,狗,猪皆属于动物类,这里就可以说,动物的形态可以是人,狗或者猪,这就是我们所说的多态
编程里:
在程序里,如何表现出这一概念?
# 多态实际上就是在继承的背景之下衍生出的一种表现形式
class Animal:
pass
class People(Animal):
pass
class Dog(Animal):
pass
class Pig(Animal):
pass
- 为什么要有多态:多态会带来什么样的特性,==>>多态性
多态性指的是在不考虑对象的类型的情况下直接使用对象
class Animal: # 统一所有子类的方法
def say(self):
print('动物基本的发声频率...',end=' ')
class People(Animal):
def say(self):
super().say()
print('嘤嘤嘤嘤嘤嘤')
class Dog(Animal):
def say(self):
super().say()
print('汪汪汪')
class Pig(Animal):
def say(self):
super().say()
print('哼哼哼')
obj1 = People()
obj2 = Dog()
obj3 = Pig()
obj1.say() # 动物基本的发声频率... 嘤嘤嘤嘤嘤嘤
obj2.say() # 动物基本的发声频率... 汪汪汪
obj3.say() # 动物基本的发声频率... 哼哼哼
def animal_say(item):
return item.say()
animal_say(obj1)
'''
在使用多态性时,我们可以不考虑对象的类型的情况下,直接使用对象,
这完全归功于父类统一了所有子类的方法,在定义完以后,只需要将方法封装成一个接口,
只要在调用的时候传入相应的对象就可以实现想要的功能,实际上是方便了使用,
而在我们平常就一直在用多态
'''
# 以len方法为例
# 其实len方法就是每一个数据类型都具有一个__len__方法,然后将该方法封装成一个统一的接口
# 只需要填入相应的数据类型就可以计算出长度
def my_len(item):
return item.__len__()
print(my_len('jason')) # 5
'''
但是python不推崇这种写法,python所推崇的是一种叫做鸭子类型(只要会跳,会鸭子叫,长得像鸭子,就是鸭子)的写法,
即,只要不同的类都具有相似的功能,就可以把他们归为一个类,而不再使用继承父类的方式
'''
# 示例
class People:
def say(self):
print('嘤嘤嘤嘤嘤嘤')
class Dog:
def say(self):
print('汪汪汪')
class Pig:
def say(self):
print('哼哼哼')
obj1 = People()
obj2 = Dog()
obj3 = Pig()
# 将这几个类共同的方法封装成一个接口
def say(item):
return item.say()
say(obj1) # 嘤嘤嘤嘤嘤嘤
say(obj2) # 汪汪汪
say(obj3) # 哼哼哼
'''
如果非要用父类来规范子类所具有的一些方法时,可以通过导入一个模块来进行规范
'''
import abc # ==>> abstract抽象
# 抽象类只能被继承,不能被实例化
class Animal(metaclass=abc.ABCMeta ): # 统一所有子类的方法
@abc.abstractmethod # 抽象方法,子类在继承时,子类里必须要有这种方法,且方法名必须一样,不然就会报错
def say(self):
print('动物基本的发声频率...',end=' ')
class People(Animal):
def shuo(self):
super().say()
print('嘤嘤嘤嘤嘤嘤')
class Dog(Animal):
# def say(self):
# super().say()
# print('汪汪汪')
pass
class Pig(Animal):
def say(self):
super().say()
print('哼哼哼')
obj1 = People()
obj2 = Dog()
obj2.say() # 报错
obj1.say() # 报错