继承和派生
继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的行为;
派生类就是从一个已有的类中衍生出新的类,在新类的基本上添加新的属性和行为。
目的:
继承的目的是延续旧类的功能。
派生的目的是在旧类的基础上改变原有的功能。
单继承
语法:
class 类名(基类名):
语句块
说明:单继承是指派生类由一个基类衍生出来新类。
示例代码如下:
# 此示例示意单继承的定义方法和用法
class Human:
def say(self, what):
print("说:", what)
def walk(self, distance):
print("走了", distance, '公里')
class Student(Human):
def study(self, subject):
print("正在学习", subject)
class Teacher(Student):
def teach(self, subject):
print("正在教", subject)
h1 = Human()
h1.say('今天天气真好')
h1.walk(5)
s1 = Student()
s1.walk(4)
s1.say('感觉有点累')
s1.study('Python')
t1 = Teacher()
t1.teach("面向对象")
t1.walk(6)
t1.say('太累了,今天晚吃啥')
t1.study('英雄联盟')
继承派生机制的作用:
1.可以将一些共有功能加在基类中。实现代码的共享。
2.在不改变基类的基础上改变原有的功能。
继承说明:
python3 任何类都直接或间接的继承自object类;
object类是一切类的超类。
类的 base 属性
__base__属性用来记录此类的基类。
覆盖
覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类的实例调用该方法时,实际调用的是子类中的覆盖版本,这种现象叫覆盖。
示例代码如下:
# 此示例示意覆盖的含义及用法
class A:
def work(self):
print("A.work 被调用")
class B(A):
'''B类继承自A类'''
def work(self):
print("B.work被调用!!!")
b = B()
b.work() # B.work被调用!!! B.work(b)
A.work(b) # A.work 被调用
那么问题来了当覆盖发生时,子类对象如何调用父类中的被覆盖的方法?
python中有一个函数super函数可以解决这个问题。
super(cls, obj) 返回绑定超类的实例(要求obj必须为cls类型的实例)
super() 返回绑定超类的实例,等同于: super(class, 实例方法的第一个参数, 必须在方法内调用)
作用:借助super()返回的实例间接调用父类的覆盖方法.
示例代码如下:
# 此示例示意用super函数间接调用父类中覆盖版本的方法
class A:
def work(self):
print("A.work 被调用")
class B(A):
'''B类继承自A类'''
def work(self):
print("B.work被调用!!!")
def super_work(self):
# 调用B类自己的work方法怎么调用
self.work()
# 调用父类的work怎么调用
super(B, self).work()
super().work() # 此种调用方式只能在实例方式内调用
b = B()
# b.work() # B.work被调用!!!
# super(B, b).work() # A.work 被调用
b.super_work()
# super().work() # 出错
显式调用基类的初始化方法
当子类中实现了__init__方法,基类的构造方法并不会被调用,此时需要显式调用
# 此示例示意显式调用初始化方法
class Human:
def __init__(self, n, a):
self.name = n # 姓名
self.age = a # 年龄
print("Human类的初始化方法被调用 ...")
def infos(self):
print("姓名:", self.name)
print("年龄:", self.age)
class Student(Human):
def __init__(self, n, a, s=0):
# super(Student, self).__init__(n, a)
super().__init__(n, a)
self.score = s
print("Stduent的初始化方法被调用...")
def infos(self):
super().infos() # 显式调用父类的方法
print('成绩:', self.score)
s1 = Student('张飞', 15, 80)
s1.infos()
以下为钻石继承继承顺序
class Plant():
def __init__(self):
print("Enter plant")
print("Leave plant")
class Fruit(Plant):
def __init__(self):
print("Enter Fruit")
super().__init__()
print("Leave Fruit")
class Vegetable(Plant):
def __init__(self):
print("Enter vegetable")
super().__init__()
print("Leave vegetable")
class Tomato(Fruit, Vegetable):
def __init__(self):
print("Enter Tomato")
super().__init__()
print("Leave Tomato")
tomato = Tomato()
print(Tomato.__mro__)
注:__mro__属性
作用: 用来记录类的方法查找顺序。
# 输出
# Enter Tomato
# Enter Fruit
# Enter vegetable
# Enter plant
# Leave plant
# Leave vegetable
# Leave Fruit
# Leave Tomato
# (<class '__main__.Tomato'>, <class '__main__.Fruit'>, <class '__main__.Vegetable'>, <class '__main__.Plant'>, <class 'object'>)
A
/ \
B C
\ /
D
D --> B --> C --> A
遵循就近原则
封装
讲到封装,我们围绕三点展开,是什么,为什么,怎么样。
是什么
封装就是将类中的属性及方法隐藏起来,仅对外提供公共的访问方式,
可以想象为一个电视机,用户不需要知道里面具体有哪些零件,只提供给用户操作电视的按钮即可,这样将电视内部零件封装了起来,并且给外部提供了接口。
为什么
封装的目的是让使用者通过尽可能少的方法(或属性)操作对象,同时隔离复杂度。
示例代码如下:
#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做隔离了复杂度,同时也提升了安全性
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('用户认证')
def __input(self):
print('输入取款金额')
def __print_bill(self):
print('打印账单')
def __take_money(self):
print('取款')
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
a=ATM()
a.withdraw()
注:
python 的封装是假的封装(模拟的封装)
说明
私有属性和方法
python类中,以双下划线(’__’) 开头,不以双下划线结尾的标识符为私有成员,私有成员只能使用该类的方法来进行访问和修改。
1. 以__开头的属性为私有属性。
2. 以__开头的方法为私有方法。
封装特性property
@property装饰器就是负责把一个方法变成属性调用的。
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值。
目的: 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则。
示例代码如下:
class people:
def __init__(self,name,age,height,weight):
self.name=name
self.age=age
self.height=height
self.weight=weight
@property
def bodyindex(self):
return self.weight/(self.height**2)
p1=people('alex',18,1.76,74)
print(p1.bodyindex)
property属性的定义和调用要注意一下几点:
1.定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数。
2.调用时,无需括号。
3.经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法。
4.新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法。
5.Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类 ),python3中的类都是新式类。
多态
多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态。
示例代码如下:
class Shape:
def draw(self):
print("Shape的draw()被调用")
class Point(Shape):
def draw(self):
print('正在画一个点!')
class Circle(Point):
def draw(self):
print('正在画一个圆!!!')
def my_draw(s):
s.draw() # <<<--- 此处显示出多态中的"动态"
s1 = Circle() # 创建一个圆对象
s2 = Point() # 创建一个点对象
my_draw(s1) #正在画一个圆!!!
my_draw(s2) #正在画一个点!
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def whoAmI(self):
return 'I am a Person, my name is %s' % self.name
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
def whoAmI(self):
return 'I am a Student, my name is %s' % self.name
class Teacher(Person):
def __init__(self, name, gender, course):
super(Teacher, self).__init__(name, gender)
self.course = course
def whoAmI(self):
return 'I am a Teacher, my name is %s' % self.name
def who_am_i(x):
print(x.whoAmI())
p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')
who_am_i(p) #I am a Person, my name is Tim
who_am_i(s) #I am a Student, my name is Bob
who_am_i(t) #I am a Teacher, my name is Alice
有志者事竟成!!!