单继承和多继承:
单继承:每个类只能继承一个类的方式称为单继承。
多继承:每个类可以同时继承多个类的方式称为多继承。
多继承格式:
class 父类1:
pass
class 父类2:
pass
class 子类(父类1,父类2):
pass
注意:当子类继承类多个类的时候,当多个父类的成员方法名不相同时候,则子类都可以进行访问调用,如果多个父类的成员方法的方法名是相同的时候,则当子类调用时候,只按照继承顺序的先后进行调用,调用一次后,不会在调用。
多继承之后,子类就具备了所有父类的成员(私有成员除外)
多个父类具有相同的成员时,子类继承[继承列表]中第一个类的方法
EG1:多重继承
class Base1:
def fool(self):
print("我是Bool1")
class Base2:
def fool(self):
print("我是Bool2")
class C(Base1, Base2):
pass
c = C()
c.fool()
实行结果:
我是Bool1
菱形继承/钻石继承
如果BC类同时继承了A类,D类又继承了BC两个类的情况下(菱形继承),
在调用BC中某个同名方法(该方法都继承自A类)时会导致继承自A类的该方法被多次调用。产生问题
super() 类来解决了多继承的菱形继承问题
补充:
1.uper不是一个关键字,也是不是有函数,他是一个类
2.super()的作用不是查找父类,而是找MRO列表的上一个类
3.super()和父类没有任何实质性的关系,只是有时候能调用到父类而已。
4.在单继承的情况下,super()永远调用的是父类/父对象
类的组合
把类的实例化放到新类里面,一般是把几个不是继承关系的类放到一起(没有纵向关系(继承关系),有横向关系)
补充:一个类的成员变量可以是另一个类的实例化对象(两个类之间没有继承关系)
class Turtle:
def __init__(self, x):
self.num = x
class Fish:
def __init__(self, x):
self.num = x
class Pool:
def __init__(self, x, y):
self.turtle = Turtle(x)
self.fish = Fish(y)
def display(self):
print(self.turtle.num, self.fish.num, sep="\t")
pool = Pool(2,9)
pool.display()
实行结果:2 9
注解:Pool类中的成员变量分别是Turtle和Fish的实例化对象
程序调用顺序:
1.Pool实例化对象,调用Pool的构造函数,将两个参数传入,在构造函数中,对Turtle和Fish分别实例化对象,将传入Pool构造函数的两个参数传入到Turtle和Fish实例化的构造函数中,实例化turtle和fish作为Pool的实例化对象pool的成员变量.pool调用display()函数时,又分别访问了turtle和fish的成员变量num。
补充:
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
EG:类,类对象,实例对象
class C:
count = 0
a = C()
b = C()
d = C()
print(a.count, b.count, d.count) # 1
d.count += 10
print(a.count, b.count, d.count) # 2 修改的是d的实例化对象属性,发生变化的是d的count属性,a和b是分别实例化的它们的值不会修改
C.count = 100
print(a.count, b.count, d.count) # 3 修改的是类C的静态属性值,a和c没对其实例化属性修改过,而d之前对实例化对象的属性赋值,覆盖了类的静态属性
实行结果:
0 0 0
0 0 10
100 100 10
EG:2实例化对象的属性和方法相同,则属性覆盖方法
class A:
def display(self):
print("AAAAA")
a = A()
a.display()
a.display = 1 #python中对象不需要声明,直接赋值就是定义。给实例化对象a的display属性直接赋值,该属性和方法名相同,则属性名直接将方法名覆盖掉
a.display()
实行结果:
AAAAA
Python中约定俗成的规定:
1.不要试图在一个类里定义所有能想到的特性和方法,应该利用继承和组合机制进行拓展
2.用不同的词性命名,如属性名用名词,方法名用动词
绑定
定义:python严格要求方法需要有实例才能被调用,这种限制就是绑定
EG1:
class A:
def display():
print("AAAAA")
A.display() # 类对象A直接调用displa方法不会报错
a = A()
a.display() # 实例化对象a直接调用display方法会报错,因为调用display时把a当作参数传进去了,完整的调用方法a.display(a)
实行结果:
AAAAA
EG2:
class CC:
def setXY(self, x, y):
self.x = x
self.y = y
def printXY(self):
print(self.x, self.y)
dd = CC()
print("dd:dict", dd.__dict__) # 查看对象所有的属性,返回一个字典的类型。字典中只有实例对象的属性,不显示类属性和特殊属性(魔术方法)
print("CC:dict", CC.__dict__) # 键表示属性名,值就是属性的值
dd.setXY(4, 6)
print("dd:dict", dd.__dict__) # 这x,y只属于dd这个实例化对象
print("CC:dict", CC.__dict__) # 类属性中,没有4和6,这归功于绑定,实例化对象dd调用setxy(4,5)实际上将自身当作参数传入进去,完整的函数是dd.setxy(dd,4,6),在函数中赋值是dd.x = 4,dd.y = 6,所以4,6只属于dd
del CC
# ee = CC() # 出错,类对象已经删除
dd.printXY()
实行结果:
dd:dict {}
CC:dict {'__module__': '__main__', 'setXY': <function CC.setXY at 0x00000000029D7598>, 'printXY': <function CC.printXY at 0x00000000029D7620>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
dd:dict {'x': 4, 'y': 6}
CC:dict {'__module__': '__main__', 'setXY': <function CC.setXY at 0x00000000029D7598>, 'printXY': <function CC.printXY at 0x00000000029D7620>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
4 6
注意:类中定义的属性和方法是静态变量,就算类对象被删除了,它们还是存放在内存中,只有在程序退出时候,它们才会被释放。(建议编程时使用实例属性,不要使用类属性)