面向对象的三大特性:继承、封装、多态
1.继承
1)定义:子类以及子类实例化的对象,可以访问父类的任何方法和变量;类名括号中的类名为父类,也叫基类,超类;括号外面的类为字类,也叫派生类
2)优点:
①节省代码
②规范代码
#通过子类的类名可以访问父类的所有内容
class Animal: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def eat(self): print("动物都能吃东西。。。") class Person(Animal): pass Person.eat(11) Person.name = "阿狸" print(Person.name) print(Person.breath) 输出: 动物都能吃东西。。。 阿狸 呼吸
#子类实例化的所有对象也可以访问父类的所有内容
class Animal: breath = "呼吸" def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def eat(self): print("动物都能吃东西。。。") class Person(Animal): pass p = Person("阿狸",18,"女") p.eat() print(p.breath) 输出: 动物都能吃东西。。。 呼吸
变量、方法查询顺序:先从本类中查询,如果没有,再从父类中查询
例:写三个类:狗、猫、鸡,每个类中都有吃、喝、自己的方法,最后定义一个Animal类
class Animal: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def eat(self): print("%s吃东西" % self.name) def dring(self): print("%s喝东西" %self.name) class Cat(Animal): def bark(self): print("喵喵喵") class Dog(Animal): def bark(self): print("汪汪汪") class Chook(Animal): def bark(self): print("咕咕咕") c1 = Cat("Tom",3,"公") c1.eat() c1.dring() c1.bark() d1 = Dog("豆豆",5,"公") d1.eat() d1.bark() 输出: Tom吃东西 Tom喝东西 喵喵喵 豆豆吃东西 汪汪汪
总结:
①若只执行父类的方法:在子类中不要定义与父类同名的方法
②只执行子类的方法:在子类中创建这个方法
③即执行本类方法,又执行父类的方法【父类名.方法名(参数)】【super().方法名(参数)】
3)若既要执行子类的方法,又要执行父类的方法(两种方式)
①则需要在子类中__init__方法中调用父类的__init__方法,然后再添加子类自己的属性
class Animal: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def eat(self): print("%s吃东西" % self.name) def dring(self): print("%s喝东西" %self.name) class Bird(Animal): def __init__(self,name,age,sex,wing): Animal.__init__(self,name,age,sex) self.wing = wing class Cat(Animal): def bark(self): print("喵喵喵") b1 = Bird("鹦鹉",10,"雌","绿翅膀") print(b1.__dict__) 输出: {'age': 10, 'name': '鹦鹉', 'wing': '绿翅膀', 'sex': '雌'}
②利用super关键字,起到的功效和法一一模一样,只是写法不一样
super(本类类名,本类实例化对象).父类的__init__(参数)方法;可把本类类名和本类实例化对象去掉
class Animal: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def eat(self): print("%s吃东西" % self.name) def dring(self): print("%s喝东西" %self.name) class Bird(Animal): def __init__(self,name,age,sex,wing): #Animal.__init__(self,name,age,sex) super(Bird,self).__init__(name,age,sex) #super().__init__(name,age,sex) #写一种即可 self.wing = wing class Cat(Animal): def bark(self): print("喵喵喵") b1 = Bird("鹦鹉",10,"雌","绿翅膀") print(b1.__dict__) 输出: {'sex': '雌', 'age': 10, 'wing': '绿翅膀', 'name': '鹦鹉'}
例:实现既要执行本类的方法,又要执行父类的方法;如动物吃肉,猫吃肉同时还要吃鱼
class Animal: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def eat(self,argv): print("%s吃%s" % (self.name,argv)) def dring(self): print("%s喝东西" %self.name) class Cat(Animal): def bark(self): print("喵喵喵") def eat(self,argv): super().eat(argv) print("猫吃鱼") c1 = Cat("Tom",3,"公") c1.eat("肉") 输出: Tom吃肉 猫吃鱼
3)补充:继承和类的分类
①继承继承分为单继承和多继承
②类分为经典类和新式类
1'新式类:凡是继承object类都是新式类;在python3.x中,所有的类都是新式类,所有类默认继承object类;
2'经典类:不继承object类都是经典类;python2.x中既有新式类,又有经典类,所有的类都默认不继承object类,所有的类默认都是经典类,但可以手动让其继承object类,成为新式类
4)查询顺序区别
①在单继承中,新式类和经典类的查询顺序一样
②在多继承中,新式类查询遵循广度优先;经典类查询遵循深度优先
#多继承的新式类:广度优先
class A: def func(self): print('IN A') class B(A): def func(self): print('IN B') class C(A): def func(self): print('IN C') class D(B): def func(self): print('IN D') class E(C): def func(self): print('IN E') class F(D,E): def func(self): print('IN F') f1 = F() f1.func()
总结:广度优先,即每个节点有且只走一次(先沿着第一条路走,然后判断最后的基类是否还有其他路可以到达,如果还有其他路可以达到基类,就不再沿着当前路走)
#若代码中类过多,无法判断类的继承顺序,可使用方法mro()查看类的继承顺序,mro()方法只适用于新式类
class A: def func(self): print('IN A') class B(A): pass def func(self): print('IN B') class C(A): pass def func(self): print('IN C') class D(B): pass def func(self): print('IN D') class E(C): pass def func(self): print('IN E') class F(D,E): pass def func(self): print('IN F') f1 = F() f1.func() print(F.mro()) #打印类的继承顺序 输出: IN F [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
#多继承的经典类:深度优先(py3无法测试,需要py2)
对于深度优先,类的查询顺序是一条路走到底(即第一条路全部查询完,如果再找不到,才会走第二条路)
class A: def func(self): print('IN A') class B(A): def func(self): print('IN B') class C(A): def func(self): print('IN C') class D(B): pass def func(self): print('IN D') class E(C): pass def func(self): print('IN E') class F(D,E): pass def func(self): print('IN F') f1 = F() f1.func() 查询顺序为:D----B-----A-----E------C
注:对于此处的深度优先和广度优先的顺序,只能是继承两个类的情况,继承两个以上的类,顺序会出现问题
2.练习
1.通过具体代码完成如下要求
1)定义一个父类Animal,在构造方法中封装三个属性,姓名,性别,年龄,再给其添加一个eat的方法,方法中显示%s正在吃饭(%s是哪个对象调用此方法,显示哪个对象名字)。
2)定义两个子类Person,Dog,全部继承这个父类Animal.
3)Person类中,有构造方法,封装一个皮肤的属性,有eat方法,方法中显示人类正在吃饭。
4)Dog类中,有构造方法,封装一个毛色的属性,有eat方法,方法中显示狗狗正在吃饭。
上面这几个类创建完成之后,完成下列要求:
①: 实例化一个人类的对象,让其只封装皮肤属性。
class Animal: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def eat(self): print("%s正在吃饭" % self.name) class Person(Animal): def __init__(self,skin): self.skin = skin def eat(self): print("人类正在吃饭") class Dog(Animal): def __init_self(self,hair_color): self.hair_color = hair_color def eat(self): print("狗狗正在在吃饭") #实例化一个人类的对象,让其只封装皮肤属性。 p1 = Person("黄皮肤") print(p1.__dict__) 输出: {'skin': '黄皮肤'}
②: 实例化一个人类的对象,让其封装姓名,性别,年龄,皮肤四个属性。
class Animal: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def eat(self): print("%s正在吃饭" % self.name) class Person(Animal): def __init__(self,name,sex,age,skin): super().__init__(name,sex,age) self.skin = skin def eat(self): print("人类正在吃饭") class Dog(Animal): def __init_self(self,hair_color): self.hair_color = hair_color def eat(self): print("狗狗正在在吃饭") #实例化一个人类的对象,让其封装姓名,性别,年龄,皮肤四个属性。 p2 = Person("劫","男",18,"黄皮肤") print(p2.__dict__) 输出: {'name': '劫', 'skin': '黄皮肤', 'sex': '男', 'age': 18}
③: 实例化一个狗类的对象,让其只封装毛色属性。
class Animal: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def eat(self): print("%s正在吃饭" % self.name) class Person(Animal): def __init__(self,name,sex,age,skin): super().__init__(name,sex,age) self.skin = skin def eat(self): print("人类正在吃饭") class Dog(Animal): def __init__(self,hair_color): self.hair_color = hair_color def eat(self): print("狗狗正在在吃饭") d1 = Dog("黑毛") print(d1.__dict__) 输出: {'hair_color': '黑毛'}
④: 实例化一个狗类的对象,让其封装姓名,性别,年龄,毛色四个属性。
class Animal: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def eat(self): print("%s正在吃饭" % self.name) class Person(Animal): def __init__(self,name,sex,age,skin): super().__init__(name,sex,age) self.skin = skin def eat(self): print("人类正在吃饭") class Dog(Animal): def __init__(self,name,sex,age,hair_color): super().__init__(name,sex,age) self.hair_color = hair_color def eat(self): print("狗狗正在在吃饭") d2 = Dog("豆豆","公",3,"黑毛") print(d2.__dict__) 输出: {'sex': '公', 'name': '豆豆', 'hair_color': '黑毛', 'age': 3}
⑤: 实例化一个人类的对象,让其只执行父类的eat方法(可以对人类代码进行修改)。
class Animal: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def eat(self): print("%s正在吃饭" % self.name) class Person(Animal): def __init__(self,name,sex,age,skin): super().__init__(name,sex,age) self.skin = skin # def eat(self): # print("人类正在吃饭") class Dog(Animal): def __init__(self,name,sex,age,hair_color): super().__init__(name,sex,age) self.hair_color = hair_color def eat(self): print("狗狗正在在吃饭") p2 = Person("劫","男",18,"黄皮肤") p2.eat() 输出: 劫正在吃饭
⑥: 实例化一个狗类的对象,让其既执行父类的eat方法,又执行子类的eat方法。
class Animal: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def eat(self): print("%s正在吃饭" % self.name) class Person(Animal): def __init__(self,name,sex,age,skin): super().__init__(name,sex,age) self.skin = skin # def eat(self): # print("人类正在吃饭") class Dog(Animal): def __init__(self,name,sex,age,hair_color): super().__init__(name,sex,age) self.hair_color = hair_color def eat(self): super().eat() print("狗狗正在在吃饭") d2 = Dog("豆豆","公",3,"黑毛") d2.eat() 输出: 豆豆正在吃饭 狗狗正在在吃饭
2.写出下列代码的执行结果
class Parent: def func(self): print('in Parent func') def __init__(self): self.func() #此处的self还是子类对象,所以执行子类的func class Son(Parent): def func(self): print('in Son func') son1 = Son() 输出: in Son func
3.判断如下程序输出:
#对于类中变量是不可变的数据类,不能通过对象修改类变量,通过对象修改变量,只是修改本对象空间中的变量;
class A: age = 10 p1 = A() p2 = A() p1.age = 20 p2.age = 30 print(p1.age) print(p2.age) print(A.age) 输出: 20 30 10
#对于变量是可变的数据类型
当程序运行看到的变量和值对应的关系存在全局名称空间中;如果这个变量对应的值是个可变的数据类型,这个名称空间存的是变量和这个可变数据类型(如列表)的内存地址
对象.name是先从对象空间中查找name,若对象中没有,再从类中去找(即从类的名称空间中去找),即找到的是列表的内存地址
通过对象给变量添加元素,就相当于往这个列表中添加了元素,而变量的内存地址不变
class A: name = [] p1 = A() p2 = A() p1.name.append(1) p2.name.append(2) print(p1.name) print(p2.name) 输出: [1, 2] [1, 2] [1, 2]