04-python中的面向对象编程-03

04-python中的面向对象编程-03

上一篇 文章介绍 python 中面向对象的 几种常见方法, 类方法,实例方法,静态方法等,知道每一种方法的区别 .

今天 我们继续介绍 python面向对象的编程 中一些 概念的东西, 面向对象 都必须要知道的 封装 ,继承,多态 这些基本特性.

三大特性

所有 面向对象 编程 几乎 都要说一下 封装,继承 多态 这三大特性.

首先 python语言 是一种动态的语言 ,很多时候 就比较灵活.

封装 隐藏内部实现

继承 复用现有的代码

多态 改变对象的行为

封装性

比较好理解, 就是把数据和行为绑定在一起,暴露一些 公开的方法 ,隐藏一些实现的细节.

以 Flask 框架为例:

# -*- coding: utf-8 -*- 

"""
@Time   : 2020/6/13 17:38
@File   : hello2.py
@Author : 15769162764@163.com

"""

from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
    return 'this is index'


if __name__ == '__main__':
    pass
    app.run()

这里 Flask 这个类 就封装了 很多的操作,就这么的代码,就可以在本地启动一个 server 服务,看起来是不是很简单.

在这里插入图片描述
image-20200613190335924.png

点击链接 就可以看到 index 页面.

image-20200613190352691

这里 就 简单几行代码 就实现了一个 server 服务.

venv/lib/python3.7/site-packages/flask/app.py

看下 源代码 就可以发现 这个类 是不是特别丰富, 写了 很多实例方法,

image-20200613190836745

看下 这里 就有很多的成员变量,这里就有很多方法 , 这里我不打算 将每个参数的作用, 只是 这里 其实在真正的开发中 一个类 可能有很多 方法, 很多成员变量,来组成这个类.

class Flask:
    def __init__(
            self,
            import_name,
            static_url_path=None,
            static_folder="static",
            static_host=None,
            host_matching=False,
            subdomain_matching=False,
            template_folder="templates",
            instance_path=None,
            instance_relative_config=False,
            root_path=None,
    ):
        pass
      
      
      
      
   def wsgi_app(self, environ, start_response):
    	pass 

而真正暴露给 使用者 其实就是 这个 方法 是不是很简单,一个方法,其他的方法 都封装在这个类中.

这个就是 类的封装性.

这里 补充一个 在python 的里面一个命名规范

有时候 你可能会遇到 这样的方法名 _xxxx,__xxx

class Person:

    def __init__(self, name, height, weight):
        self.name = name
        self.height = height
        self.weight = weight

    def __cook(self):
        print(f"{self.name} is cooking")

    def _eat(self):
        print(f"{self.name} is eating")
        self.weight = self.weight + 2

    def swim(self):
        print(f"{self.name} is  swimming")
        self.weight = self.weight - 1

这种 以下划线命名, 单下划线, 双下划线来命名的方法, 这里就是说 我不需要 外界 关心我的这些方法, 内部的方法,只给 自己使用 ,你只能调用 公共方法 swim ,

当然 这是一种约定, 如果 强行去调用 __cook _eat,谁也挡不住你 去这样调用的.

按照 其他语言 Java,

__cook 对应私有方法 , _eat protected 方法,保护方法 , 受保护的方法 允许子类调用 ,私有方法 只能自己调用,其他类 不能调用.

继承

继承 是面向对象 使用最多的一种方式. 什么是继承呢? 这个 可以对比现实世界, 子承父业 的例子.

python 如何继承的呢 ?

class Father:

    def __init__(self, name):
        self.name = name
        pass

    def cook(self):
        print(f"{self.name} is cooking")


# 继承语法 
class Son(Father):

    def __init__(self, name):
        super().__init__(name=name)


if __name__ == '__main__':
    son = Son(name='frank')
    son.cook()  # frank is cooking


class Son(Father) 这句话 就是说继承 一个类 Father , 这里 继承里面 一般要继承的类 称为父类,自己称为子类,

这是一些基本的概念.

son 这个类里面本身没有实现任何方法, 但是我却可以 使用son.cook() 为啥呢?

这就是继承的作用, 就是 Son 可以从父类中获取一些方法 . 这样子类就不需要重新写 cook 这个方法了,是不是实现了代码的复用呢.

画了一个 继承图.

image-20200613225021603

通过继承 就可以获得父类的方法,以及属性. 继承 你可以认为 在父类的基础上,拓展 更加新的功能,或者 获取更多的方法.

继承可以这样理解 , 在子类中包含了 父类,同时自己有一些自己的方法.

image-20200613225927204

举个例子

# -*- coding: utf-8 -*- 

"""
@Time   : 2020/6/13 23:06
@File   : oriented_object.py
@Author : 15769162764@163.com

"""


class Father:


    wealth=100

    def __init__(self, name):
        self.name = name
        pass

    def cook(self):
        print(f"{self.name} is cooking")


class Son(Father):

    def __init__(self, name, weight):
        super().__init__(name=name)
        self.weight = weight

    def swim(self):
        print(f"{self.name} is  swimming ")
        self.weight = self.weight - 1


if __name__ == '__main__':
    son = Son(name='frank', weight=55)

    son.cook()  # frank is cooking
    son.swim() # frank is  swimming 
    print(son.wealth) # 100

mage-20200613231904753

这里 就可以看到继承的好处, 子类 可以完全使用父类所有公开的方法 ,属性等. 这样 子类 就不需要重新写一遍 父类的方法, 只要写一点 父类没有的方法,或者 父类的实现 和子类不同的方法 就可以了.

这样 父类的代码 就可以被复用了. 这里 复用 意思是说源代码没有被更改的情况下, 直接使用了源代码.

这里 的复用不是 复制黏贴代码, 注意 这是两个概念. 面向对象编程 在一定程度在实现的代码的复用性.

何时使用继承呢?

你可能 不会提出这个问题, 但是我 觉得还是要提醒一下.

如果刚刚接触继承的时候,你会觉得代码复用非常好, 然后 各种继承 ,至少在我学习面向对象的时候,考虑都不考虑 就直接继承 我需要的方法 .

继承关系 是一种耦合性极强 的一种设计.如果 不加思考 就使用继承, 会让系统变得耦合性极高.

一般情况 当满足一下 条件的时候 才能继承.

问一下 A 是B 吗? 如果是 , 就可以是继承关系. A 可以继承B

车子 是交通工具吗? 是, 那么 可以使用继承

class Vehicle:
  pass


class Car(Vehicle):
  pass

手是人吗? 否 ,所以不能 使用继承关系.

手是人的一部分,所以 这里应该使用组合关系,而非继承.

我举例 很明显, 所以你会觉得 这样 很显而易见. 当你实际写代码的时候, 可能没有去认真思考,而写出了 使用面向对象语法(而非 面向对象思考的代码) 的代码. 所以这里我还是 多啰嗦几句. 你现在 对这个并没有什么感觉. 当你的代码 有一定积累时候,你就慢慢能够理解了.

从python 内置库里面寻找一些例子

python 所有的类默认都是从 object 继承的,这点 需要知道一下.

看下 异常类

class BaseException(object):
  	pass
  

class Exception(BaseException):
		pass
  
  
class ArithmeticError(Exception):
  	pass

  
class AttributeError(Exception):
  	pass
  

20200613235533660

之前 写 异常处理的时候, 写了一些异常,那时候可能没有解释那么多,现在回去看下, 哈哈.

02-python 基础语法知识-04-异常与错误处理

lib/python3.7/http/server.py

HTTPServer 的类图

mage-20200614132333857

继承的特点

继承链 越往上 即父类 越来越抽象, 子类会越来越具体, 子类会越来越接近应用.

image-20200614132925405

多继承问题

继承还有 多继承的问题 ,这个呢 尽量少用, 多继承 不建议使用, 虽然python 中 有对多继承的支持, 多继承 容易参数的问题 就是菱形继承 的问题 .

比如 下面的情况 :

D从 B,C两个类继承, 但是 B, C 都有一个swim 这个方法, 那么 D对象 执行的时候, 是调用 B, 还是调用C 的swim 方法呢?

image-20200614134705916

# -*- coding: utf-8 -*- 

"""
@Time   : 2020/6/13 23:06
@File   : oriented_object.py
@Author : 15769162764@163.com

"""


class A:
    wealth = 100

    def __init__(self, name):
        self.name = name

    def cook(self):
        print(f"A  {self.name} is cooking")


class B(A):

    def __init__(self, name, weight):
        A.__init__(self, name=name)

        self.weight = weight

    def swim(self):
        print(f"B {self.name} is  swimming ")
        self.weight = self.weight - 1


class C(A):

    def __init__(self, name, weight):
        A.__init__(self, name=name)
        self.weight = weight

    def swim(self):
        print(f"C  {self.name} is  swimming ")
        self.weight = self.weight - 1


class D(C, B):

    def __init__(self, name='frank', weight=55):
        super().__init__(name=name, weight=weight)


if __name__ == '__main__':
    d = D()
    d.swim()  # C frank is  swimming 

如果修改 D 的继承顺序

class D(B, C):
  
    def __init__(self, name='frank', weight=55):
        super().__init__(name=name, weight=weight)
  

这样调用 结果 C frank is swimming 看到这里的区别了, 所以这就是多继承 带来的问题 . 继承多个父类的时候, 如果父类有相同的方法, 这样 结果是不可预测的.

当然 其实背后是 MRO 顺序 来去查找方法的, 这个 还是比较复杂的,就不展开了. 所以 如果 自己写代码的时候 注意尽量不要有多继承出现, Mixin 这种方式 是可以的. 很多优秀的语言Java 也意识到多继承的问题, 慢慢地推荐 实现接口 ,之继承一个父类 来完成.

总结 继承 其实就是 子类 可以完全继承 父类所有公开的方法和属性,可以被子类 无缝调用, 这里 就提现 面向对象的编程的复用性.

多态性

在python中多态性 就比较灵活了.

多态性 在 其他面向对象语言中 如Java 语言中.

多态存在的三个必要条件
一、要有继承;
二、要有重写;
三、父类引用指向子类对象

在python 中 其实说多态性, 就是 一个 对于不同的对象, 实现 同的接口, 不管什么类型 只要接口实现 就可以调用.

这样说 有点抽象 ,看下写的一个例子

# -*- coding: utf-8 -*- 

"""
@Time   : 2020/6/13 23:06
@File   : oriented_object_03.py
@Author : 15769162764@163.com

"""

import abc


class Animal(abc.ABC):

    @abc.abstractmethod
    def sound(self):
        """
        定义 发声音 的方法
        这是一个抽象方法, 有子类进行实现

        :return:

        """
        pass


class Dog(Animal):

    def sound(self):
        print(" wang wang wang !")


class Cat(Animal):

    def sound(self):
        print("miao miao miao")


def run(obj):
    obj.sound()


if __name__ == '__main__':
    dog = Dog()
    cat = Cat()

这里我定义一个Animal 的抽象类, 定义了一个 抽象方法, 发声的方法. 然后 Dog , Cat 继承这个 Animal 类型.

>>> dog = Dog()
>>> cat = Cat()

>>> cat.sound()
miao miao miao
>>> dog.sound()
 wang wang wang !
 

>>> # 注意看这里 把 dog , cat 传入 run 函数里面 ,发现都可以正常调用 自己的sound 方法
>>> run(cat)
miao miao miao
>>> run(dog)
 wang wang wang !

无论我们给obj传递的是狗,猫 都能正确的调用相应的方法,打印对应的信息。这就是多态性的体现.

实际上 我完全 不从animal 继承 也是 可以调用.只要实现 sound方法即可.


class Bird:

    def sound(self):
        print("jiji  zhazha 叽叽喳喳 ")

        
        
>>> bird = Bird()
>>> run(bird)
jiji  zhazha 叽叽喳喳 

python 是 动态性语言, 动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

这里 run 的这个函数 不论你传入什么类型,只要你传入的类型 ,有 sound 方法的实现,就可以正常被调用.

来一个鸭子 就是鸭子叫, 来一个鸡就是鸡叫. 来一个不知道什么类型, 只要实现了 sound ,它就可以叫.

这就是多态性.

总结

​ 这篇文章 介绍 python 语言中面向对象编程的 三大特性, 继承, 封装, 多态.有些东西 可能没有办法 让你 那么快的理解, 但是 随着编程经验的增加,相信 你会有更深的体会.

这篇文章介绍了python中如何使用面向对象语言 ,如何定义父类(基类), 如果定义抽象方法. 有了这些基础知识, 你就可以用面向对象思想 来描述 这个斑斓多彩的 现实世界了. 世界那么大, 让我们用面向对象方式 来去描述这个世界吧. 好了,好好学习,加油!

分享快乐,留住感动.2020-06-14 17:42:58 --frank
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值