一、组合
自定义类的对象作为另一个类的属性。
class Teacher:
def __init__(self,name,age):
self.name = name
self.age = age
t1 = Teacher('Bob',20)
print(type(t1.name), type(t1.age))
class Student:
# 学生可以有 老师 属性
def __init__(self, name, age, teacher):
self.name = name
self.age = age
# 自定义类的对象作为类的属性:组合
self.teacher = teacher
# 创建一个学生
# 创建一个学生
stu = Student('Bob', 18, t1)
print(stu.__dict__)
# 学生的老师年龄和姓名
print(stu.name)
print(stu.teacher)
print(stu.teacher.name)
print(stu.teacher.age)
二、继承
将所有共同的属性与方法抽离出,形成父类
父类是多个有共同点的普通类抽离共有属性与方法形成的类
class People:
def __init__(self,name):
self.name = name
def eat(self):
print(self.name + '在吃饭')
class Student(People):
identify = '学生'
# def __init__(self, name):
# self.name = name
#
# def eat(self):
# print(self.name + '在吃饭')
student = Student('Bob')
student.eat()
class Teacher(People):
# def __init__(self, name):
# self.name = name
#
# def eat(self):
# print(self.name + '在吃饭')
pass
teacher = Teacher('Ben')
teacher.eat()
class Leader(People):
# def __init__(self, name):
# self.name = name
#
# def eat(self):
# print(self.name + '在吃饭')
pass
leader = Leader('Yang')
leader.eat()
print(Student.identify) # 只是自身类拥有
# print(Teacher.identify) # 父类没有,其他类就没有
print(Leader.__bases__) # (<class '__main__.People'>,)
继承的语法:
class 类名(父类名):
pass
class A:
pass
print(A.__bases__) # object
2.1、继承关系
1、父类所有未封装的属性和方法,子类都能访问
2、父类所有封装的属性和方法,子类都不能访问
-- 在外界通过子类和子类对象,都不能访问
-- 在子类内部也不能访问
class Sup:
__num = 10
def __init__(self, name):
self.__name = name
@property
def name(self):
print(self)
return self.__name
@classmethod
def __c_fn(cls):
print(cls,'c fn')
def __o_fn(self):
print(self.name, 'o fn')
class Sub(Sup):
def test(self):
print(self)
print(self.__name)
# Sub.__c_fn() # 无法访问父类的私有属性
sub = Sub('sss')
print(sub.name)
# sub.test() # 无法访问
2.2、有继承关系下的属性查找顺序
有继承关系下的属性查找顺序
1、优先找自身,自身没有再找父类
2、父类没有继续往父类的父类找
3、一直找到最顶级的父类,如果没有就报错
2.3、方法的重写
先写子类,抽离出父类
先写父类,派生出子类
class Sup:
num = 10
def test(self):
print('test sup')
class Sub(Sup):
num = 100
# 先写好父类的方法,由于父类方法的功能不满足子类需求,
# 子类可以重写父类方法:方法名与父类相同,自定义方法的实现体
def test(self):
print('test sub')
print(Sub.num) # 100
Sub().test() # test sub
2.4、方法的重用
有时,单纯的继承父类不能满足需求,但是重写又要用到父类,因此可以通过super()关键字进行方法重用
class Sup:
def test(self):
print('sup>>>',self)
print('test sup')
class Sub(Sup):
def test(self):
# python2中写法
# super(Sub, self).test()
# python3中简化写法
super().test()
print('sub>>>',self)
print('test sub')
Sub().test() # 既执行父类,又执行子类
__init__结合super()使用:
# 人类:只需要初始化 - name
# 老师: 要初始化 - name salary
# 学生: 要初始化 - name grade
class People:
def test(self):
print(self)
def __init__(self,name):
self.name = name
class Teacher(People):
# 有继承关系下,只要名字相同,即使参数不同,还是属于同一个方法
def test(self, num):
super().test()
print(num)
# 默认父级的__init__可以被继承过来,
# 但是会出现子类对象的属性比父类多
def __init__(self,name,salary):
super().__init__(name) # 父级有的共性功能通过super()交给父级做
self.salary = salary # 子类特有的自己来完成
Teacher('Bob',20000).test(10)
# 使用的时候,还是要使用自己的带参数的,不能使用父类不带参的
# (本质名字相同就是一个,优先查找自己的)
# Teacher('Bob',20000).test() # test() missing 1 required positional argument: 'num'
重点:super() 可以得到调用父级功能的对象,调用者还是子类对象
-- super()只能在子类的方法中使用
-- super()本质 super(子类类名, 当前对象)
-- super().父类普通方法 | super().__init__() | super()能调用父类所有可继承方法
了解:
-- java中存在真正的方法的重用
def fn():
pass
def fn(num):
pass
# fn()调用不传参时调用第一个fn
# fn(10)调用传入一个参数时调用第二个fn
2.5、多继承
2.5.1、简单的多继承
属性的查找顺序:优先找自己的,如果没有,按照继承先后查找父级
通过 类.mro()可以查看继承顺序
class A:
name = 'A'
num = 10
class B:
name = 'B'
count = 100
# 子类可以继承所有父类的所有可继承属性
class C(A, B): # 自己 => A => B
# name = 'C'
pass
print(C.num)
print(C.count)
print(C.name)
# 打印属性查找的顺序
print(C.mro())
2.5.2、复杂多继承
class A:
name = "A"
class B(A):
name = "B"
class C:
name = "C"
class D(C):
name = "D"
class E(B, D): # 先将B的所有父级们找完再找D的分支
name = "E"
print(E.mro()) # E => B => A => D => C
2.5.3、菱形继承
python2中深度优先,即A先找B>C>G>D>E>F,在查找第一个分支时就将菱形的头G查找了
python3中广度优先,即A>B>C>D>E>F>G,菱形的头在所有分支查找结束后再被查找
class G: name = "G"
class C(G): pass
class B(C): pass
class E(G): pass
class D(E): name = "D"
class F(G): pass
class A(B, D, F): pass
print(A.mro())
经典类:python2中才有,没有继承任何类的类
新式类:python2中直接或间接继承object的类,python3中所定义的所有类