《老鸟python 系列》视频上线了,全网稀缺资源,涵盖python人工智能教程,爬虫教程,web教程,数据分析教程以及界面库和服务器教程,以及各个方向的主流实用项目,手把手带你从零开始进阶高手之路!点击 链接 查看详情




继承和多态

阅读:227568359    分享到

面向对象的三大特征(封装,继承和多态),我们通过前面两节学习了封装的各种知识,本节课我们来学习继承和多态。

继承

在面向对象程序设计中,当我们定义一个类的时候, 可以从某个现有的类继承,该类称为子类,而被继承的类称为基类,父类或超类, 我们只需要在定义类的括号内加入父类的类名称就实现了继承。

class Animal():
    pass

class Cat(Animal):   # 继承 Animal
    pass

class Bird(Animal):  # 继承 Animal
    pass

Python 语言的哲学是:一切皆对象。Python 给我们提供一个万物之源的类,该类类名叫 object,Python 想让我们写的任何类都要继承这个 object 类,在 Python 3 中继承和不继承 object 的类都是一样的,但在 Python 2 中,继承和不继承 object 的类,对有继承关系的类是有区别的,大家有兴趣可以参考 Python 2 教程部分。所以我们以后写类要养成一个好习惯,特别是想要写出 Python 2 和 Python 3 通用的代码,我们自己写的类都要继承 object 类,在此我们只需要在被继承的父类 Animal 中继承 object 类即可,因为继承关系是往下传递的。

class Animal(object):  # 一切皆对象
    pass

class Cat(Animal):     # 继承 Animal
    pass

class Bird(Animal):    # 继承 Animal
    pass

子类拥有父类所有的属性和方法,比如我们有一个 Animal 类,该类有一个变量 name 和一个函数 talk,我们另定义类 Cat 和类 Bird 来继承 Animal 类。

class Animal(object):
    name = "Animal"  # Animal 类的属性

    def talk(self):  # Animal 类的方法
        print("Animal talking")

class Cat(Animal):
    pass

class Bird(Animal):
    pass


onecat = Cat()
onebird = Bird()

print(onecat.name)
onecat.talk()

print(onebird.name)
onebird.talk()

当然我们也可以在父类的构造函数中定义变量,子类的对象如果想拥有该变量,子类必须把子类对象本身传给父类, 用来替代父类对象本身,我们可以通过调用父类的构造函数把子类对象传过去。

class Animal(object):
    def __init__(self):
        self.name = "Animal"  # self 具体是什么,一切由调用者决定

    def talk(self):  # Animal 类的方法
        print("Animal talking")

class Cat(Animal):
    def __init__(self):
        Animal.__init__(self)  # 把子类对象本身传给父类的构造函数

class Bird(Animal):
    def __init__(self):
        Animal.__init__(self)  # 把子类对象本身传给父类的构造函数


onecat = Cat()    # 调用 Cat 类的构造函数,在 Cat 类的构造函数中,调用了 Animal 类的构造函数(参数为 onecat 对象本身)
onebird = Bird()  # 调用 Bird 类的构造函数,在 Bird 类的构造函数中,调用了 Animal 类的构造函数(参数为 onebird 对象本身)

print(onecat.name)
onecat.talk()

print(onebird.name)
onebird.talk()

当然,我们只要能把子类的对象传给父类,并且在父类中给该对象定义属性,子类对象都会拥有该属性。注意:我们一般习惯于在子类的构造函数中调用父类的构造函数来初始化属性,在此我们只是举个例子让大家明白这个机制。

class Animal(object):
    def __init__(self):
        self.name = "Animal"   # self 具体是什么,一切由调用者决定

    def what(self):            # self 具体是什么,一切由调用者决定
        self.what = 250

class Cat(Animal):
    def __init__(self):
        Animal.what(self)      # 把子类对象本身传给父类的 what 函数

    def haha(self):
        Animal.__init__(self)  # 把子类对象本身传给父类的构造函数


onecat = Cat()     # 调用 Cat 类的构造函数,在 Cat 类的构造函数中,调用了 Animal 类的 what 函数(参数为 onecat 对象本身)

print(onecat.name)  # 错误
print(onecat.what)  # 正确

onecat.haha()
print(onecat.name)  # 正确

如果我们在子类中不写构造函数,Python 解释器会自动给我们添加上构造函数,子类构造函数里面的代码和我们上面子类中写的一样。

class Animal(object):
    def __init__(self):
        self.name = "Animal"

    def talk(self):
        print("Animal talking")


'''
1.Cat 类中没有构造函数,Python 解释器会自动给 Cat 类添加构造函数。
2.添加的内容为:
def __init__(self):
    Animal.__init__(self)
'''
class Cat(Animal):
    pass



'''
1.Bird 类中有构造函数,Python 解释器不会再创建构造函数。
2.我们在子类中的构造函数中,并没有把 self 传给父类,
所以,Bird 类的对象没法拥有父类构造函数中定义的属性 name
'''
class Bird(Animal):
    def __init__(self):
        pass

onecat = Cat()
onebird = Bird()

print(onecat.name)  # 正确
onecat.talk()

print(onebird.name)  # 错误
onebird.talk()

再有继承关系的类中,我们也可以在子类中加入新的属性和函数。

class Animal(object):
    def __init__(self):
        self.name = "Animal"

    def talk(self):
        print("Animal talking")

class Cat(Animal):
    def __init__(self):
    self.beard = "Cat beard"  # 猫有胡须

    def getbeard(self):
    return "This is" + self.beard

class Bird(Animal):
    def __init__(self):
        self.wing = "Bird wing"    # 鸟有翅膀

    def fly(self):
        print("I'm flying")

onecat = Cat()
onebird = Bird()

print(onecat.beard)
print(onecat.getbeard())
print(onebird.wing)
onebird.fly()

在有继承关系的类设计中,我们一般习惯于把子类对象这些共有的属性定义在父类中, 而这些属性的值我们通过子类的对象来赋予。我们可以在子类的构造函数通过调用父类的构造函数传过去,调用父类构造函数有两种方法 (可以通过调用 Animal.__init__ 函数或者 super(类名, 对象).__init__ 函数)。

class Animal(object):
    def __init__(self, name):
        self.name = name

    def talk(self):
        print(self.name + "is talking")

class Cat(Animal):
    def __init__(self, name, beard):
        Animal.__init__(self, name) # 调用父类 Animal 的构造函数
        self.beard = beard

    def getbeard(self):
        return self.beard

class Bird(Animal):
    def __init__(self, name, wing):
        super(Bird, self).__init__(name)  # 调用父类 Animal 的构造函数
        self.wing = wing    # 鸟有翅膀

    def fly(self):
        print("I'm flying")

onecat = Cat(u"加菲猫", "Cat beard")
onebird = Bird(u"愤怒的小鸟", "Bird wing")

print(onecat.name)
onecat.talk()

print(onebird.name)
onebird.talk()

我们在此再次复习一个概念:类中的所有成员函数都是属于类的,因为成员函数(不包含里面的变量)是代码, 这些代码存放在代码段中,所有对象共用这份代码,所以不存在属于某个对象的成员函数; 而成员变量有属于对象的(在函数内部定义),也有属于类的(在类内非函数内部定义)。

class Animal(object):
    name = "Animal"                   # 属于类的成员变量
    def __init__(self):               # 成员函数都是属于类的,构造函数也是成员函数
        self.nameex = "Animalex"      # 属于 self 对象的成员变量

    def talk(self):                   # 成员函数都是属于类的
        self.nameexex = "Animalexex"  # 属于 self 对象的成员变量
        print("Animal talking")

如果我们子类中的属于类的变量或函数和父类中的属于类的变量或函数重名,通过子类的对象访问该重名变量或重名函数都是子类的。

class Animal(object):
    name = "Animal"

    def talk(self):
        print("Animal talking")

class Bird(Animal):
    name = "Bird"

    def talk(self):
        print("Bird talking")

onebird = Bird()
print(onebird.name)  # Bird 类的 talk 成员函数
onebird.talk()       # Bird 类的 name 成员变量

但是如果是子类属于对象的变量和父类对象中的变量重名,则根据对象本身赋值顺序而定(这是理所当然的正常语法)。

class Animal(object):
    def __init__(self):
        self.name = "Animal"

class Bird(Animal):
    def __init__(self):
        Animal.__init__(self)  # 在父类中先给 onebird 对象的 name 赋值为 "Animal"
        self.name = "Bird"     # 然后改写 onebird 对象的 name 值为 "Bird"

onebird = Bird()
print(onebird.name)  # "Bird"

#---------我是分割线---------
class Animal(object):
    def __init__(self):
        self.name = "Animal"

class Bird(Animal):
    def __init__(self):
        self.name = "Bird"     # 先给 onebird 对象的 name 赋值为 "Bird"
        Animal.__init__(self)  # 然后在父类中改写 onebird 对象的 name 值为 "Animal"

onebird = Bird()
print(onebird.name)  # "Animal"

多态

当子类和父类存在同名的成员函数时,通过某个子类的对象调用该函数,总是会调用相应子类的函数,该行为称为多态。

class Animal(object):
    def talk(self):
        print("Animal talking")

class Cat(Animal):
    def talk(self):
        print("Cat talking")

class Bird(Animal):
    def talk(self):
        print("Bird talking")

def func(animal):
    animal.talk()

onecat = Cat()
onebird = Bird()

func(onecat)     # "Cat talking"
func(onebird)    # "Bird talking"

Python中的多态和其它静态语言(比如 c++,java等)中多态的实现方法是不一样的, c++ 中是通过基类的指针或引用来实现运行时多态。由于 Python 是动态语言,并不需要提前给一个变量定义好类型,在 Python 中其实我们就是直接通过子类的对象调用子类中重新定义的该函数而已(这是理所当然的正常语法)。

本节重要知识点

理解 Python 继承中相应的语法。

弄明白 Python 是动态语言,多态是必然的。

作业

著名公司(某为)笔试题:标出下面四个成员变量 name 的调用顺序。

class Animal(object):
    name = "Animal one"
    def __init__(self):
        self.name = "Animal two"

class Bird(Animal):
    name = "Bird one"
    def __init__(self):
        self.name = "Bird two"
        Animal.__init__(self)

birdone = Bird()
print(birdone.name)

如果以上内容对您有帮助,请老板用微信扫一下赞赏码,赞赏后加微信号 birdpython 领取免费视频。


登录后评论

user_image
白如冰
2020年7月15日 05:57 回复

真棒!感谢作者![赞同]


user_image
王大喵
2019年7月2日 06:22 回复

谢谢 学习了


user_image
t123yh
2019年6月22日 17:33 回复

太难了,感觉这辈子都学不会


user_image
老鸟python
2020年2月18日 12:15

不会,耐心点就会


user_image
慕容腹黑
2019年5月11日 08:37 回复

目前大一

自学到python这里

表示能看懂


user_image
巩朋
2019年4月21日 10:38 回复

请问,看到代码,继承基类括号里写了一个变量(类实例)是什么意思?