Python 培训(四)—— 继承、多态、虚函数

关于上次作业

  • 上次作业有一个通病,虽然说不是写法上的错误,但是感觉大家的思维不是很正确,用下面的代码为例:
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
    
    def getX(self):
        return self.x
    
    def getY(self):
        return self.y
  • 上面的代码问题就出现在 getX 上面。这里 x 和 y 是属性,我们用的时候直接 .x 就可以了,根本不需要 getX 这个函数,大家这样写,是因为学 C++ 的时候,x 和 y 是 private 所以必须要再写一个函数。
  • 个人经验,分开类属性和方法会更加方便,这也是为什么会有 property 这个东西的原因,就是为了把一些方法换成属性的样子,这样类的方法都是一些行为,而不会充满 get、set 这样的东西,这样会使代码质量更好,也更容易让别人看懂。

继承、多态

  • 继承具体是什么大家还是上网搜一下吧,实在不想讲概念,每个人理解不一样。基本上就是在基类的基础上添加属性、方法。比如现在有一个二维的点类,如果要新建一个三维空间的点类,就可以继承这个二维的点类,再增加一个坐标就可以了。
  • 继承一个类以后,子类就拥有基类的所有属性和方法,然后在此基础上增加属性和方法。这就是继承的基本思想。
  • 继承的基本写法如下,就是在基类的基础上继续添加属性和方法:
class Person:
    def __init__(self, name):
        self.name = name
        
    def talk(self):
        print('I am ', self.name)
        

class Chinese(Person):
    def __init__(self, name, province):
        #	构造基类函数
        super().__init__(name)
        self.province = province

    def talk(self):
        print('我是', self.name)        
  • 在定义的类后面加上括号表示继承,这时就继承了基类的属性和方法。
  • 需要注意的是,如果基类构造函数需要输入参数,就需要先构造基类,如上代码所示。
  • 多态: 重写基类的方法,可以将基类的方法覆盖掉。也可以用 super() 调用基类的方法,然后在此基础上继续处理。

多重继承

  • 虽然我们不建议直接继承多个基类,但是还是总结一下继承多个基类的语法。
class BaseOne:
    def __init__(self, numberOne):
        self.numberOne = numberOne
    
    def printData(self):
        print(self.numberOne)
        

class BaseTwo:
    def __init__(self, numberTwo):
        self.numberTwo = numberTwo
    
    def printData(self):
        print(self.numberTwo)
        

class Derived(BaseOne, BaseTwo):
    def __init__(self, numberOne, numberTwo):
        BaseOne.__init__(self, numberOne)
        BaseTwp.__init__(self.numberTwo)
        
    def printData(self):
        BaseOne.__init__(self)
        BaseTwo.__init__(self)
  • 上面这种调用基函数的方法,和之前的 super 相同,但是继承了多个类以后,就有多个基类,我试了一下 super 应该指向第一个基类,建议继承多个基类以后就用上面这种方法调用基函数。
  • 值得注意的是,这种调用基类方法的参数中要带 self,而 super 不需要

类的静态方法

  • 类的静态方法写发如下:
class myClass:
    @staticmethod
    def saySomething():
        print('Hello world')
        

A = myClass
#	用类访问
myClass.saySomething()
#	用对象访问
A.saySomething()
  • 如上,在类方法前加上 @staticmethod 表示方法为类的静态方法。

  • 静态方法中参数没有 self,因为这个方法是属于这个类的,而不是属于某个具体的对象。

  • 通常静态方法就是不需要 self 输入的方法,在 Pycharm 中,如果不加 @staticmethod 会有提示,建议你单独提出来放在类外面。

虚函数、抽象基类

  • 之前我们已经知道了,派生类可以覆盖基类的方法,但是通常有这么一种情况,需要我们用到抽象基类的思想。我们举一个例子
  • 现在我们定义一个基类 Shape,表示所有的二维图形,比如长方形,正方形等。这些图形有一个函数,用来计算面积 S。这时候你就会发现,基类 Shape 根本没法写这个函数,而每一个派生类都需要这个函数。
  • 我们当然可以直接继承 Shape,然后增加一个函数 getS,但是这样有可能忘记写了这个函数,导致出现逻辑问题,于是我们想,如果有一种机制,在基类里面写一个空函数,如果子类不重写这个方法,就会报错,这就是抽象基类,这个方法就是虚函数。
  • C++、Java 等都可以直接定义虚函数,但是 Python 不一样,需要导入一个模块,然后继承一个类,如下所示:
from abc import ABC, abstractmethod


class Shape(ABC):
    @abstractmethod
    def getS(self):
        pass
	
    def process(self):
        self.getS()
        #	更多操作
  • 如上,抽象基类是用来继承的,不能用抽象基类生成对象(就是不能 A = Shape 这样创建类的对象)
  • 还有一个好处,就是基类可以整合一些虚函数,比如我虽然不知道面积怎么算,但是我需要这个结果来计算一些东西,在 process 里面调用了 getS,如果我们创建了一个子类 Circle,重写了 getS 方法,那么可以不用重写 process,因为在 process 里面 getS 会调用重写后的函数。

作业

  • 大家自己发挥吧,把上面的各种机制都尝试一下,以后写程序就尽量用这种面向对象的思维。
  • 每种机制大家最好尝试一遍,加深一下印象,后面写大程序就需要这些基本功了。

本文章使用limfx的vsocde插件快速发布