美文网首页Python回忆录
基础篇: 11. Python类继承

基础篇: 11. Python类继承

作者: 后视镜 | 来源:发表于2019-12-01 09:51 被阅读0次

Python类继承是一个很复杂的体系,但说简单其实就是父类和子类关于成员属性和方法的继承与派生.继承就是子继承父类的方法和属性,谈理论比较空洞,实际上继承就是为了把相同的东西放到一起,每个人都会吃东西,用双腿走路等行为,这些就是相同的东西,每次写代码去描写一个人的时候,当第二次写一个人走路的动作代码的时候就会觉得能不能把这些东西放到父类呢,这样其实就是继承的用法.这些抽象派的理论名词多数都需要我们遇到真实的场景才能感受到它们的魅力,特别是那些专门编写类库的给别人用的人,但实际工作中,大部分的人都是调用别人写好的类库,继承某个类可以调用某个方法,得到某个行为之类的.如果没有这种需求的时候,可以自己创造一下,每次复制粘贴的时候是否能用上继承.

1. 继承

1.1. 定义

下面例子就是一个继承最简单的例子,Person继承了People,其实People也继承了object类(是所有类的基类),类名后面加括号注明父类是谁就继承了,如果父类的构造方法是有参构造函数,必须在构造函数中显式调用.主要用super()来调用,例子就是传进了arg作为父类的构造函数的参数.


# 父类
class People(object):
    def __init__(self, name):
        super(People, self).__init__()
        self.name = name
     
class Person(People):
       def __init__(self, name):
           super(Person, self).__init__(name)

print(Person.__base__)
# 输出People
<class '__main__.People'>

可以看到Person的父类是People.

1.2. 属性继承

父类定义的属性都会被继承下来,但私有属性却不能访问.私有属性一般用双下划线前缀(如__private_data).

class People(object):
    def __init__(self, name):
        super(People, self).__init__()
        self.name = name
        self.__fahter_name = "object"
     
class Person(People):
    def __init__(self, name):
        super(Person, self).__init__(name)
        print(self.name)
        # print(self.__fahter_name)

p = Person("Tom")
# 输出
Tom

可以看到name是被继承下来了,并被赋值了Tom.

1.3. 方法继承

父类定义的方法也可以被继承下来,同样私有方法不能访问.数据和逻辑都被继承主要可以复用,而不是每个写都一遍.但Python不支持重载方法,这种方式会让语言更简单,扬长避短发挥语言优势才能更灵活地实现自己的需求.


class People(object):
    def __init__(self):
        super(People, self).__init__()
        self.name = "People"
        self.__fahter_name = "object"

    def hello(self):
        print("{} hello".format(self.name))

    def run(self):
        print("People run")
     
class Person(People):
    def __init__(self):
        super(Person, self).__init__()
        self.name = "Tom"

    def run(self):
        print("Tom run")

    # 如果这样不是重载方法,和Java有区分的,会覆盖方法,虽然可以这样定义,再传参调用,但强烈不建议,这样很容易出问题的.
    # def run(self, distance):
    #     print("People run")


p = Person()
p.hello()
p.run()
# 输出
Tom hello
Tom run

上面的两个方法,hello方法是从People继承下来的,调用也不会报错,但为什么显示是Tom hello,而不是People hello,因为虽然调用hello方法是父类定义,里面使用self.name其实已经变成了Tom,这就是继承的魅力.假如每个人都要重写一遍hello方法这是多麻烦,只需要继承就可以重用了,可以大幅减少代码量.同时也涉及到一个议题,多处一样的代码块修改的问题,很多时候我们都会遇到的,逻辑一样却没有想好怎么调用就多处复制粘贴,然后当这块代码需要修改的时候,就会陷入一个困境,是否所有地方都修改了?还有没有漏的?所以封装方法和继承重用非常重要.

重写同名方法会覆盖,但不建议修改参数列表,有些从其他语言转过来的时候很容易会出问题,以为有重载.但实际上不同参数只会覆盖.所以建议就是尽量覆盖方法的时候不要修改参数,避免产生错觉.

2. 例子: 模板方法

继承的最大用处在于实际公司的编码上面,主要是屏蔽一些复杂的细节,把逻辑分了好多步,其中大部分人只需要重写其中的一步便可,不需要了解整个逻辑的细节.例如接收网络字节包,然后再保存数据到数据库再返回响应这一过程中,很多时候公司已经帮我们把接收和响应的步骤都写好了,我们一般只需要编写怎么处理数据的逻辑,其实就是业务逻辑.一般的做法就是老大写了个父类,把这些细节都处理好,让组员继承父类,然后覆盖方法(如do_handle),专注于写业务逻辑便行.但父类的其实就是用到了模板方法,具体例子如下:


class Task(object):

    def __init__(self):
        super(Task, self).__init__()
        

    def receive_data(self):
        print("receive_data...")
        return ""

    def response_data(self, resp):
        print("response_data...")

    # need to override
    # 需要覆盖
    def do_handle(self, req_data):
        pass

    def run(self):
        req_data = self.receive_data()
        print("start to handle")
        resp = self.do_handle(req_data)
        print("end to handle")
        self.response_data(resp)

class LoginTask(Task):
    def __init__(self):
        super(LoginTask, self).__init__()

    def do_handle(self, req_data):
        # 例如 判断传进来的参数是否正确
        if "username" not in req_data:
            return "username not exists"

        # ...

        # save to db
        return "success"


task = LoginTask()
task.run()
# 输出
receive_data...
start to handle
end to handle
response_data...

一般都定义个Task类,然后把那些复杂的细节都处理好了,让其他人继承Task,如登录任务LoginTask,复写do_handle就可以处理逻辑,好处是不需要关心其他的细节,而且也可以更快地上手不需要研究怎么处理网络包接收数据等等.当然也有其坏处,就是编写do_handle的人员如果不去了解底层,是无法知道整个运作的全貌,一直都是流水线上的一个环节而已.

3. 总结

Python类系统其实很复杂,先把80%重要的东西吸收了,其他东西在特殊环境和业务才能用到,这些情况其实少之又少,可以后面遇到或者有时间再去深入了解,把这些肉眼可见都有价值的摸清摸透,其实已经算是在Python的使用上面超过80%的人了,在大公司也适用.类和继承用得非常多,但为什么不提接口呢?接口这个在刚才模板方法上面已经有涉及,说不要复写方法的时候更改参数列表,也是因为很多时候留了接口的定义给子类去覆盖,因为父类有调用到并传入对应的参数,如果修改了参数列表就会导致报错,但在脚本语言没有强类型的情况下,我觉得更多依靠的是约定,这样在团队协作上面就不会出现太多风格不一样的坑.类的理解其实非常深奥,随着遇到的业务场景和适用场景的不一样,可以考虑在不出问题的情况下,多去尝试,理论与实践相结合.类和继承是非常理论的东西,所以在适用上面更多的靠经验去判断和封装,怎么样方便扩展.多去试试编写实际的例子来检验自己对类和继承的理解.如把当前遇到的业务如果硬要使用类来实现该如何使用呢?多用Python试试,创建自己的工具包和类库.

这次废话多了好多,因为我见到很多人用Python单纯只是写业务,刚好这次说起了类和继承这种特别需要靠经验使用的议题就想起实践问题.还是建议多去使用这种方便的语言去实现自己更多的想法,而不是把它当做一个完成业务的工具.

后视镜 20191123

相关文章

网友评论

    本文标题:基础篇: 11. Python类继承

    本文链接:https://www.haomeiwen.com/subject/enczwctx.html