面向对象的三大特性:继承、封装、多态

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)
输出:
动物都能吃东西。。。
阿狸
呼吸
View Code

#子类实例化的所有对象也可以访问父类的所有内容

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)
输出:
动物都能吃东西。。。
呼吸
View Code

变量、方法查询顺序:先从本类中查询,如果没有,再从父类中查询

例:写三个类:狗、猫、鸡,每个类中都有吃、喝、自己的方法,最后定义一个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喝东西
喵喵喵
豆豆吃东西
汪汪汪
View Code

总结:

         ①若只执行父类的方法:在子类中不要定义与父类同名的方法

         ②只执行子类的方法:在子类中创建这个方法

         ③即执行本类方法,又执行父类的方法【父类名.方法名(参数)】【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': ''}
View Code

②利用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': '鹦鹉'}
View Code

例:实现既要执行本类的方法,又要执行父类的方法;如动物吃肉,猫吃肉同时还要吃鱼

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吃肉
猫吃鱼
View Code

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()
View Code

广度优先查询顺序

总结:广度优先,即每个节点有且只走一次(先沿着第一条路走,然后判断最后的基类是否还有其他路可以到达,如果还有其他路可以达到基类,就不再沿着当前路走)

#若代码中类过多,无法判断类的继承顺序,可使用方法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'>]
View Code

#多继承的经典类:深度优先(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
View Code

注:对于此处的深度优先和广度优先的顺序,只能是继承两个类的情况,继承两个以上的类,顺序会出现问题

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': '黄皮肤'}
View Code

②: 实例化一个人类的对象,让其封装姓名,性别,年龄,皮肤四个属性。

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}
View Code

③: 实例化一个狗类的对象,让其只封装毛色属性。

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': '黑毛'}
View Code

④: 实例化一个狗类的对象,让其封装姓名,性别,年龄,毛色四个属性。

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}
View Code

⑤: 实例化一个人类的对象,让其只执行父类的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()
输出:
劫正在吃饭
View Code

⑥: 实例化一个狗类的对象,让其既执行父类的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()
输出:
豆豆正在吃饭
狗狗正在在吃饭
View Code

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
View Code

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
View Code

#对于变量是可变的数据类型

当程序运行看到的变量和值对应的关系存在全局名称空间中;如果这个变量对应的值是个可变的数据类型,这个名称空间存的是变量和这个可变数据类型(如列表)的内存地址

         对象.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]
View Code