• Python 全栈开发:python面向对象三大特征


    一、继承

    1.什么继承

    继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。

    2.为什么要有继承

    子类会“”遗传”父类的属性,从而解决代码重用问题,减少代码的冗余

    3.怎么应用继承

    eg:

    class ParentClass1: # 定义父类1
        pass
    
    class ParentClass2: # 定义父类2
        pass
    
    class Subclass1(ParentClass1): # 单继承 父类1
        pass
    
    class Subclass2(ParentClass1,ParentClass2):  # 多继承多个父类  父类1 父类2
        pass
    
    print(Subclass1.__bases__)  # 查看所有父类信息
    print(Subclass2.__bases__)  # 查看所有父类信息

    结果:以元组的形式返回
    (<class '__main__.ParentClass1'>,)
    (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

    4.继承与抽象(先抽象再继承)

    先抽象:抽取对象之间相似之处得到了类,在总结类与类之间的相似得到父类

    再继承:(子类继承父类,子类可以遗传父类属性)是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

    5.派生与重用

    派生:子类定义自己新的属性,如果与父类同名,以子类自己的为准

    # 父类
    class People:
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        def func1(self):
            print('People.func1')
    
    
    # 子类 派生自己的属性
    class Teacher(People):
        def __init__(self, name, age, sex, level, salary):
            self.name = name
            self.age = age
            self.sex = sex
    
            self.level = level
            self.salary = salary
    
        def func1(self):
            print('Teacher.func1')
    
    
    # 实例化
    tea1 = Teacher('fixd', 18, 'male', 9, 3.1)
    print(tea1.name, tea1.age, tea1.sex, tea1.level, tea1.salary)
    
    # 结果
    fixd 18 male 9 3.1
    示例代码

    重用:在子类派生出的新方法中重用父类的功能

    方式一:指名道姓地调用(其实与继承没有什么关系的)

    # 父类
    class People:
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    
    # 子类 "指名道姓" 调用父类的属性
    class Teacher(People):
        def __init__(self, name, age, sex, level, salary):
            People.__init__(self, name, age, sex)
            self.level = level
            self.salary = salary

    方式二、super()调用(严格依赖于继承)

      ps:super()的返回值是一个特殊的对象,该对象专门用来调用父类中的属性

      了解:在python2中,需要super(自己的类名,self)

    # 父类
    class People:
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    
    # 子类 super() 调用父类的属性
    class Teacher(People):
        def __init__(self, name, age, sex, level, salary):       
            super().__init__(name, age, sex)
            self.level = level
            self.salary = salary 

    注意:以上两种方式都可以使用,在实际的编码工作中,推荐使用统一的一种方式

    #super()会严格按照mro列表从当前查找到的位置继续往后查找
    class A:
        def test(self):
            print('A.test')
            super().f1()
    class B:
        def f1(self):
            print('from B')
    class C(A,B):
        pass
    
    c=C()
    print(C.mro()) #C->A->B->object
    
    
    c.test()
    mro列表

    5.属性查找

    '''
    1、新式类:
        继承object的类,以及该类的子类,都是新式类
    
        在python3中,如果一个类没有指定继承的父类,默认就继承object
        所以说python3中所有的类都是新式类
    
    2、经典类(只有在python2才区分经典类与新式类):
        没有继承object的类,以及该类的子类,都是经典类
    '''

    单继承名称空间的查找顺序:

    对象自身-------->>当前类-------->>父类-------->>object      # 查找不到,报错

    多继承名称空间的查找顺序:

    在菱形继承的背景下,查找属性
    1、经典类:深度优先
    2、新式类:广度优先

    二、封装

    1.什么是封装

    字面上的意思就是,把东西隐藏起来了。

    在python中的封装就是把 类中的属性(变量、函数)隐藏起来

    2.为什么要用封装

    封装的真谛在于明确内外

    对外隐藏(类的外部只能通过我们提供的接口对类内部的隐藏属性就行访问)

    对内开放(在类的内部可以直接使用隐藏属性)

    封装数据(变量)将数据隐藏并不是我们的目的,可以通过接口的方式将数据暴露给类外面使用,在接口中我们可以对数据进行限制,完成对数据的控制

    class Teacher:
        def __init__(self,name,age):
            # self.__name=name
            # self.__age=age
            self.set_info(name,age)
    
        def tell_info(self):
            print('姓名:%s,年龄:%s' %(self.__name,self.__age))
        def set_info(self,name,age):
            if not isinstance(name,str):
                raise TypeError('姓名必须是字符串类型')
            if not isinstance(age,int):
                raise TypeError('年龄必须是整型')
            self.__name=name
            self.__age=age
    
    
    t=Teacher('egon',18)
    t.tell_info()
    
    t.set_info('egon',19)
    t.tell_info()
    示例代码

    封装方法(函数)主要目的隔离复杂度,将类中多个函数组合,提供一个对外封装好的接口,供使用者调用,而调用者无需考虑接口内复杂的实现过程,简化调用

    #取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    #隔离了复杂度,同时也提升了安全性
    
    class ATM:
        def __card(self):
            print('插卡')
        def __auth(self):
            print('用户认证')
        def __input(self):
            print('输入取款金额')
        def __print_bill(self):
            print('打印账单')
        def __take_money(self):
            print('取款')
    
        def withdraw(self):
            self.__card()
            self.__auth()
            self.__input()
            self.__print_bill()
            self.__take_money()
    
    a=ATM()
    a.withdraw()
    
    隔离复杂度的例子
    示例代码

    3.怎么用封装

    在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

    #其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
    #类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式:
    
    class A:
        __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
        def __init__(self):
            self.__X=10 #变形为self._A__X
        def __foo(self): #变形为_A__foo
            print('from A')
        def bar(self):
            self.__foo() #只有在类内部才可以通过__foo的形式访问到.
    
    #A._A__N是可以访问到的,
    #这种,在外部是无法通过__x这个名字访问到。

    PS:

    1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形,主要用来限制外部的直接访问。

    2.变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形

    3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

    #正常情况
    >>> class A:
    ...     def fa(self):
    ...         print('from A')
    ...     def test(self):
    ...         self.fa()
    ... 
    >>> class B(A):
    ...     def fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from B
     
    
    #把fa定义成私有的,即__fa
    >>> class A:
    ...     def __fa(self): #在定义时就变形为_A__fa
    ...         print('from A')
    ...     def test(self):
    ...         self.__fa() #只会与自己所在的类为准,即调用_A__fa
    ... 
    >>> class B(A):
    ...     def __fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from A
    示例代码

    4.特性(property)

    什么是特性

    property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

    eg:

    BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)
    
    成人的BMI数值:
    过轻:低于18.5
    正常:18.5-23.9
    过重:24-27
    肥胖:28-32
    非常肥胖, 高于32
      体质指数(BMI)=体重(kg)÷身高^2(m)
      EX:70kg÷(1.75×1.75)=22.86
    功能描述
    class People:
        def __init__(self,name,weight,height):
            self.name=name
            self.weight=weight
            self.height=height
    
        @property
        def bmi(self):
            return self.weight / (self.height * self.height)
    
    # egon=People('egon',75,1.80)
    #
    # egon.bmi=egon.weight / (egon.height * egon.height)
    # print(egon.bmi)
    #
    # yl=People('yangli',85,1.74)
    # yl.bmi=yl.weight / (yl.height * yl.height)
    # print(yl.bmi)
    
    
    # 首先需要明确。bmi是算出来的,不是一个固定死的值,也就说我们必须编写一个功能,每次调用该功能
    #都会立即计算一个值
    egon=People('egon',75,1.80)
    yl=People('yangli',85,1.74)
    
    # 但很明显人的bmi值听起来更像一个名词而非动词
    # print(egon.bmi())
    # print(yl.bmi())
    
    
    # 于是我们需要为bmi这个函数添加装饰器,将其伪装成一个数据属性
    # egon.weight=70
    # print(egon.bmi) #21.604938271604937,调用egon.bmi本质就是触发函数bmi的执行,从而拿到其返回值
    # print(yl.bmi)
    示例代码

    为什么要用特性

    将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

    PS:

    【public】
    这种其实就是不封装,是对外公开的
    【protected】
    这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,
    但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开 【private】 这种封装对谁都不公开

    python并没有在语法上把它们三个内建到自己的class机制中,可以通过property方法实现

    class Foo:
        def __init__(self,val):
            self.__NAME=val #将所有的数据属性都隐藏起来
    
        @property
        def name(self):
            return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
    
        @name.setter
        def name(self,value):
            if not isinstance(value,str):  #在设定值之前进行类型检查
                raise TypeError('%s must be str' %value)
            self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
    
        @name.deleter
        def name(self):
            raise TypeError('Can not delete')
    
    f=Foo('egon')
    print(f.name)
    # f.name=10 #抛出异常'TypeError: 10 must be str'
    del f.name #抛出异常'TypeError: Can not delete'
    示例代码

    三、多态

    1、什么是多态

     多态指的是同一种事物多种形态

    2、为什么要用多态   

    用基类创建一套统一的规则,强制子类去遵循(使用抽象类实现),这样便可以
    在不用考虑对象具体类型的前提下而直接使用对象下的方法

    3、如何用多态

    动物有多种形态:人,狗,猪

    import abc
    class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
        @abc.abstractmethod
        def talk(self):
            pass
    
    class People(Animal): #动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal): #动物的形态之二:狗
        def talk(self):
            print('say wangwang')
    
    class Pig(Animal): #动物的形态之三:猪
        def talk(self):
            print('say aoao')
    Animal

    文件有多种形态:文本文件,可执行文件

    import abc
    class File(metaclass=abc.ABCMeta): #同一类事物:文件
        @abc.abstractmethod
        def click(self):
            pass
    
    class Text(File): #文件的形态之一:文本文件
        def click(self):
            print('open file')
    
    class ExeFile(File): #文件的形态之二:可执行文件
        def click(self):
            print('execute file')
    File

    PS:

    1.抽象基类是不能实例化的

    2.继承抽象基类的类,必须重写其抽象父类中的抽象方法

    4、多态性

    1.什么是多态动态绑定(在继承的背景下使用时,又称为多态性)

    多态性是指在不考虑实力类型的情况下使用实例

    在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
    
    比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
    解释

    2.为什么要用多态性(多态性的好处)

    • 增加了程序的灵活性
    以不变应万变,不论对象千变万化,使用者都是同一种形式去调用
    • 增加了程序的可扩展性
    通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用 
    >>> class Cat(Animal): #属于动物的另外一种形态:猫
    ...     def talk(self):
    ...         print('say miao')
    ... 
    >>> def func(animal): #对于使用者来说,自己的代码根本无需改动
    ...     animal.talk()
    ... 
    >>> cat1=Cat() #实例出一只猫
    >>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
    say miao
    
    '''
    这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)
    '''
    示例代码

    3.鸭子类型

    Python中崇尚鸭子类型(如果看起来像,叫声像而且走起路像鸭子,那么它就是鸭子)

    通过继承实现的多态性,具有强耦合性

    鸭子类型,通过创建一个外观和行为像,但与原类型毫无关系的全新对象,具有松耦合性

    #其实大家一直在享受着多态性带来的好处,
    #比如Python的序列类型有多种形态:字符串,列表,元组,多态性的体现
    #
    #str,list,tuple都是序列类型
    s=str('hello')
    l=list([1,2,3])
    t=tuple((4,5,6))
    
    #我们可以在不考虑三者类型的前提下使用s,l,t
    s.__len__()
    l.__len__()
    t.__len__()
    
    len(s)
    len(l)
    len(t)
    示例代码
  • 相关阅读:
    用JavaScript实现div的鼠标拖拽效果
    JavaScript插件制作-tab选项卡
    javascript插件制作学习-制作步骤
    JavaScript实现页面滚动到div区域div以动画方式出现
    RabbitMQ生产者消费者
    基于TCP/IP协议的socket通讯client
    基于TCP/IP协议的socket通讯server
    MQTT 发布者订阅者
    linux实用命令
    RabbitMQ CentOS6.5 安装
  • 原文地址:https://www.cnblogs.com/fixdq/p/8807853.html
Copyright © 2020-2023  润新知