资讯专栏INFORMATION COLUMN

python基础教程:类的继承

XFLY / 2786人阅读

摘要:类的继承,说明了不同类直接的关系,派生类复用了基类的代码同时也继承了基类的属性和方法。派生类的实例化会创建该类的一个新实例。派生类既可以多带带继承一个基类,也可以多重继承多个基类。

面向对象语言的一个特性就是类的继承。继承的关系跟人类繁衍的关系相似,被继承的类称为基类(也叫做父类),继承而得的类叫派生类(也叫子类),这种关系就像人类的父子关系。

类的继承,说明了不同类直接的关系,派生类复用了基类的代码同时也继承了基类的属性和方法。派生类定义的语法如下:

派生类的定义
class DerivedClassName(BaseClassName):
    语句1
    ...
    语句n

下面我们根据这个语法来写一个继承的例子:

class Person:
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height

    def look(self):
        print(self.name, "is looking")

    def walk(self):
        print(self.name, "is walking")

class Teacher(Person):
    def __init__(self, name, age, height):
        super().__init__(name, age, height)

    def teach(self):
        print(self.name, "is teaching")

class Student(Person):
    def __init__(self, name, age, height):
        super().__init__(name, age, height)

    def learn(self):
        print(self.name, "is learning")

if __name__ == "__main__":
    teacher = Teacher("Tom", 31, 178)
    s1 = Student("Jim", 12, 160)
    s2 = Student("Kim", 13, 162)

    teacher.look()
    teacher.walk()
    teacher.teach()
    print("==="*5)

    s1.look()
    s1.walk()
    s1.learn()
    print("==="*5)

    s2.look()
    s2.walk()
    s2.learn()

我们定义了一个基类Person,这个人有名字(name)、年龄(age)和身高(height)三个属性,还有look(), walk()两个方法。由它派生出两个类:TeacherStudent,这两个派生类继承了name, age, height属性,也继承了look(), walk()方法。但它们又有自己独有的方法,Teacher可以teach()Student可以learn()

运行这个示例代码,可以得到下面的信息:

Tom is looking
Tom is walking
Tom is teaching
===============
Jim is looking
Jim is walking
Jim is learning
===============
Kim is looking
Kim is walking
Kim is learning

派生类定义的执行过程与基类相同。 当构造类对象时,基类会被记住。 此信息将被用来解析属性引用:如果请求的属性在类中找不到,搜索将转往基类中进行查找。 如果基类本身也派生自其他某个类,则此规则将被递归地应用。

派生类的实例化会创建该类的一个新实例。方法引用将按以下方式解析:搜索相应的类属性,如果搜索的方法在派生类中找不到,就去基类中进行查找,如果基类本身也派生自其它类,则此规则将被递归地应用。如果搜索到了一个函数对象则方法引用就生效。

比如,Teacher中并没有定义look(),当我们调用方法teacher.look()的时候会在它的基类Person中找到并调用look()

重载基类方法

派生类可以重载基类的方法。重载,就是重新定义。如果派生类中重新定义了基类的某方法,那么派生类的重载的这个方法就会覆盖基类中的同名方法。

例如,我们定义Student时可以重载walk()方法,让它拥有Student特征:

class Student(Person):
    def __init__(self, name, age, height):
        super().__init__(name, age, height)

    def learn(self):
        print(self.name, "is learning")

    def walk(self):
        print("Student:", self.name, "is walking")

再次运行s1.walk()时,就会打印下面的信息:

Student: Jim is walking

Python有两个内置函数可被用于检查继承机制:

isinstance()来检查一个实例的类型:isinstance(obj, int)仅仅会在obj.__class__int或某个派生自int的类时为True。

issubclass()来检查类的继承关系:issubclass(bool, int)True,因为boolint的子类。但是,issubclass(float, int)False,因为float不是int的子类。

多重继承

多重继承的意思就是,一个派生类同时派生自多个基类,继承它们全部属性和方法。它的定义形式是:

class DerivedClassName(Base1, Base2, Base3):
    语句1
    ...
    语句1

对于多数应用来说,在最简单的情况下,你可以认为搜索从父类所继承属性的操作是深度优先、从左至右的,当层次结构中存在重叠时不会在同一个类中搜索两次。 因此,如果某一属性在 DerivedClassName 中未找到,则会到 Base1 中搜索它,然后(递归地)到 Base1 的基类中搜索,如果在那里未找到,再到 Base2 中搜索,依此类推。

真实情况比这个更复杂一些;方法解析顺序会动态改变以支持对 super() 的协同调用。 这种方式在某些其他多重继承型语言中被称为后续方法调用,它比单继承型语言中的 super 调用更强大。

比如,我们要定义一个“助教”类,助教是帮助老师教学的高年级同学。他兼具老师和学生的特点,我们可以让这个类多重继承“老师类”和“学生类”。

私有变量

我们上一节讲过,Python中没有类似C++中的“私有变量”。但是,大多数Python代码都遵循这样一个约定(只是约定但很重要):带有一个下划线的名称(例如:_name)应当被动作是API的非仅供部分(无论它是函数、方法或是数据成员)。 这应当被视为一个实现细节,可能不经通知即加以改变。

由于存在对于类私有成员的有效使用场景(例如避免名称与子类所定义的名称相冲突),因此存在对此种机制的有限支持,称为名称改写。 任何形式为__name的标识符(至少带有两个前缀下划线,至多一个后缀下划线)的文本将被替换为_classname__name,其中classname为去除了前缀下划线的当前类名称。这种改写不考虑标识符的句法位置,只要它出现在类定义内部就会进行。

名称改写有助于让子类重载方法而不破坏类内方法调用。例如:

class MyList:
    def __init__(self, iterable):
        self.items_list = []
        self.__update(iterable)

    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update   # 把update()拷贝给私有方法

class MyListSubclass(MyList):

    def update(self, keys, values):
        # 重载 update()
        # 但不会破坏 __init__()
        for item in zip(keys, values):
            self.items_list.append(item)

这个示例中即使正在MyListSubclass引入一个__update标识符的情况下也不会出错,因为它会在MyList类中被替换为_MyList__update,而在MyListSubclass类中被替换为_MyListSubclass__update

改写规则的设计主要是为了避免意外冲突;访问或修改被视为私有的变量仍然是可能的。这在特殊情况下甚至会很有用,例如在调试器中。

请注意传递给 exec() 或 eval() 的代码不会将发起调用类的类名视作当前类;这类似于 global 语句的效果,因此这种效果仅限于同时经过字节码编译的代码。 同样的限制也适用于 getattr(), setattr() 和 delattr(),以及对于 dict 的直接引用。

总结

类的继承体现了类的关系,基类的属性和方法可以被派生类继承,同时派生类又可以重载基类的方法。派生类既可以多带带继承一个基类,也可以多重继承多个基类。Python的类没有真正意义上的私有变量,通过约定和名称改写来有限支持私有变量。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/43843.html

相关文章

  • 【数据科学系统学习】Python # 编程基础[三]

    摘要:新的称为子类,而被继承的称为基类父类或超类。继承最大的好处是子类获得了父类的全部功能。在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。 在上一篇中我们介绍了模块和数据结构,这一篇将介绍面向对象编程。 面向对象编程 面向对象编程——Object Oriented Programming,简称 OOP,是一种程序设计思想。OOP 把对象作为程序的基本单元...

    molyzzx 评论0 收藏0
  • Python OOP 面向对象编程

    摘要:时代,如果需要手动继承,如多态多态是指,不同的子类对象调用相同的父类方法,会产生多态多样结果的编程特性。 参考:黑马程序员教程 - Python基础 面向对象 OOP三大特性,且三个特性是有顺序的: 封装 继承 多态 封装 指的就是把现实世界的事务,封装、抽象成编程里的对象,包括各种属性和方法。这个一般都很简单,不需要多讲。 唯一要注意的就是:推荐从小往大开始封装、开发类。比如手枪...

    weknow619 评论0 收藏0
  • Python基础教程

    摘要:函数内的变量被称为局部变量,这是与全局变量相反的概念。有一些进行函数式编程的机制。继承以通用的类为基础建立专门的类对象。 6.4.5 参数收集的逆过程 假设有如下函数: def add(x,y): return x+y 比如说有个包含由两个相加的数字组成的元组: params = (1,2) 使用*运算符对参数进行分配,不过是在调用而不是在定义时使用: >>> add(*params)...

    daydream 评论0 收藏0
  • [零基础python]类的细节

    摘要:在对象接口后包装其实现的细节,从而隔离了代码的修改对用户产生的影响。类提供了一个新的本地作用域,最小化了变量名冲突。类其实并没有结束,不过本讲座到此对类暂告一段。 前面对类的有关内容已经描述不少了,其实话题远远没有结束,不过对于初学者,掌握这些已经算是入门,在以后的实践中,还需要进行体会和感悟。 这几天和几个朋友以各种途径讨论过OOP的相关问题,他们是:令狐虫、Frank、晋剑、小冯...

    Fundebug 评论0 收藏0
  • [零基础python]关于类的初步认识

    摘要:反对者在某些领域对此予以否认。下面再引用一段来自维基百科中关于的历史。类的更严格的定义是由某种特定的元数据所组成的内聚的包。类还可以有运行时表示形式元对象,它为操作与类相关的元数据提供了运行时支持。 在开始部分,请看官非常非常耐心地阅读下面几个枯燥的术语解释,本来这不符合本教程的风格,但是,请看官谅解,因为列位将来一定要阅读枯燥的东西的。这些枯燥的属于解释,均来自维基百科。 1、问题...

    王岩威 评论0 收藏0

发表评论

0条评论

XFLY

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<