• 面向对象的三大特性


    面向对象的三大特性:继承、多态、封装

    一、继承

    1、什么是继承?

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

    python中类的继承分为:单继承和多继承。

    class A:pass   # 父类,基类,超类
    class B:pass   # 父类,基类,超类
    class A_son(A,B):pass # 子类,派生类
    class AB_son(A,B):pass # 子类,派生类

    注:一个类可以被多个类继承;

           一个类可以继承多个类。

    2、查看继承

    class A:pass   
    class B:pass   
    class A_son(A,B):pass 
    class AB_son(A,B):pass 
    
    print(A_son.__base__)  #__base__只查看从左到右继承的第一个子类,
    #(<class '__main__.A'>, <class '__main__.B'>)
    print(AB_son.__bases__)  #__bases__则是查看所有继承的父类
    #(<class '__main__.A'>, <class '__main__.B'>)
    print(A.__bases__)  #python的类会默认继承object类
    #(<class 'object'>,)

    注:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

    3、继承与抽象(先抽象再继承)

     抽象即抽取类似或者说比较像的部分。

    继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

    抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类。

    4、继承与重用性

    有两个类,类B的内容与类A大部分相同时,可以用到类的继承,让B继承A,B会继承A的所有属性(数据属性和函数属性),实现代码的重用。

    class Animal:
        '''
        人和狗都是动物,所以创造一个Animal基类
        '''
        def __init__(self, name, aggressivity, life_value):
            self.name = name  # 人和狗都有自己的昵称;
            self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
            self.life_value = life_value  # 人和狗都有自己的生命值;
    
        def eat(self):
            print('%s is eating'%self.name)
    
    class Dog(Animal):
        pass
    
    class Person(Animal):
        pass
    
    ya = Person('yaya',10,1000)
    ha2 = Dog('二愣子',50,1000)
    ya.eat()
    ha2.eat()
    重用性

    5、派生

    在继承中,子类也可以添加自己新的属性,也可以在不影响父类的情况下重新定义这些属性。

    #人狗大战
    class Animal:
        def __init__(self,name,aggr,hp):
            self.name = name
            self.aggr = aggr
            self.hp = hp
    
        def eat(self):
            print('吃药回血')
            self.hp+=100
    
    class Dog(Animal):
        def __init__(self,name,aggr,hp,kind):
            Animal.__init__(self,name,aggr,hp)  #
            self.kind = kind       # 派生属性
        def eat(self):
            Animal.eat(self)   # 如果既想实现新的功能也想使用父类原本的功能,还需要在子类中再调用父类
            self.teeth = 2
        def bite(self,person):   # 派生方法
            person.hp -= self.aggr
    
    jin = Dog('金老板',100,500,'吉娃娃')
    jin.eat()
    print(jin.hp)
    
    class Person(Animal):
        def __init__(self,name,aggr,hp,sex):
            Animal.__init__(self,name,aggr,hp)
            self.sex = sex       # 派生属性
            self.money = 0       # 派生属性
    
        def attack(self,dog):
            dog.hp -= self.aggr
    
        def get_weapon(self,weapon):
            if self.money >= weapon.price:
                self.money -= weapon.price
                self.weapon = weapon
                self.aggr += weapon.aggr
            else:
                print("余额不足,请先充值")
    alex = Person('alex',1,2,None)
    alex.eat()
    print(alex.hp)
    
    jin.bite(alex)
    print(alex.hp)
    派生

    注意:

    1)、父类中没有的属性 在子类中出现 叫做派生属性;

    2)、父类中没有的方法 在子类中出现 叫做派生方法;

    3)、只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,如果父类也没有报错

    4)、如果父类 子类都有 用子类的

                  如果还想用父类的,单独调用父类的:   

                              父类名.方法名 需要自己传self参数   

                              super().方法名 不需要自己传self(只在新式类中有,python3中所有类都是新式类)

    5)、正常的代码中 单继承 === 减少了代码的重复

    6)、继承表达的是一种 子类是父类的关系

    7)、如果子类中实现了调用父类的方法

        在类内:super(子类,self).方法名()  super().__init__(参数)

        在类外:super(子类名,对象名).方法名()

    通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

    当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好。

    继承的实例

    class Animal:      #父类  基类  超类
     2     def __init__(self,name,life_value,aggr):
     3         self.name= name
     4         self.life_value = life_value
     5         self.aggr = aggr  #攻击力
     6     def eat(self):
     7         self.life_value += 10  #谁调谁的血量就增加
     8 
     9 class Person(Animal):  #子类  派生类
    10     def __init__(self, money, name, life_value, aggr):
    11         super().__init__(name, life_value, aggr)
    12         self.money = money  #派生出来的一个属性
    13     def attack(self,enemy):    #人的派生方法
    14         #enemy.life_value = enemy.life_value - self.aggr
    15         enemy.life_value -= self.aggr
    16 
    17 class Dog(Animal): #子类  派生类
    18     def __init__(self,name,breed, life_value,aggr):
    19         # Animal.__init__(self,name,breed, life_value,aggr)#让子类执行父类的方法 就是父类名.方法名(参数),连self都得传
    20         super().__init__(name,life_value,aggr) #super关键字  ,都不用传self了,在新式类里的
    21         # super(Dog,self).__init__(name,life_value,aggr)  #上面super是简写
    22         self.breed = breed
    23     def bite(self,person):   #狗的派生方法
    24         person.life_value -= self.aggr
    25     def eat(self):  #父类方法的重写
    26         super().eat()
    27         print('dog is eating')
    28 
    29 # ha2 = Dog('旺财','哈士奇',20000,100)
    30 # egg = Person('egon',500,1000,50)
    31 # print(egg.aggr)
    32 # print(ha2.aggr)
    33 # egg.eat()
    34 # print(egg.life_value)
    35 #
    36 # egg.eat()
    37 # print(egg.life_value)
    38 #
    39 # ha2.eat()
    40 # print(ha2.life_value)
    41 #
    42 # print(egg.life_value)
    43 # ha2.bite(egg)
    44 # print(egg.life_value)
    45 #
    46 
    47 ha2 = Dog('牛头梗','旺财',20000,100)
    48 print(ha2.life_value)
    49 ha2.eat()  #如果父类有的方法子类里面也有,那么就叫做方法的重写,就不执行父类的了,去执行子类了
    50 print(ha2.life_value)
    51 
    52 
    53 super(Dog,ha2).eat() #生掉父类的方法,但是不推荐这样用
    54 print(ha2.life_value)
    55 
    56 #在继承中,如果子类有的方法就执行子类的
    57 # 如果子类没有的方法就执行父类的
    实现调用父类的方法

    6、抽象类与接口类

    6.1、接口类

    接口类:python原生不支持

    本质:是做代码规范用的,希望在子类中实现和父类方法名字完全一样的方法。

    from abc import abstractmethod,ABCMeta
    class Payment(metaclass=ABCMeta):  # 元类 默认的元类 type
        @abstractmethod
        def pay(self,money):pass   # 没有实现这个方法
    # 规范 :接口类或者抽象类都可以
    # 接口类 支持多继承,接口类中的所有的方法都必须不能实现 —— java
    # 抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 —— java
    class Wechat(Payment):
        def pay(self,money):
            print('已经用微信支付了%s元'%money)
    
    class Alipay(Payment):
        def pay(self,money):
            print('已经用支付宝支付了%s元' % money)
    
    class Applepay(Payment):
        def pay(self,money):
            print('已经用applepay支付了%s元' % money)
    
    def pay(pay_obj,money):  # 统一支付入口
        pay_obj.pay(money)
    
    wechat = Wechat()
    ali = Alipay()
    app = Applepay()
    wechat.pay(100)
    ali.pay(200)
    接口类

    接口类的多继承:

    # tiger 走路 游泳
    # swan 走路 游泳 飞
    # oldying 走路 飞
    from abc import abstractmethod,ABCMeta
    class Swim_Animal(metaclass=ABCMeta):
        @abstractmethod
        def swim(self):pass
    
    class Walk_Animal(metaclass=ABCMeta):
        @abstractmethod
        def walk(self):pass
    
    class Fly_Animal(metaclass=ABCMeta):
        @abstractmethod
        def fly(self):pass
    
    class Tiger(Walk_Animal,Swim_Animal):
        def walk(self):
            pass
        def swim(self):
            pass
    class OldYing(Fly_Animal,Walk_Animal):pass
    class Swan(Swim_Animal,Walk_Animal,Fly_Animal):pass
    View Code

    接口类,  刚好满足接口隔离原则 面向对象开发的思想 、规范。

    6.2、抽象类

    抽象类:规范

    一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
    多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中

    利用abc模块实现抽象类

    import abc #利用abc模块实现抽象类
    class All_file(metaclass=abc.ABCMeta):
        all_type='file'
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def read(self):
            '子类必须定义读功能'
            with open('filaname') as f:
                pass
    
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def write(self):
            '子类必须定义写功能'
            pass
    
    class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('文本数据的读取方法')
        def write(self):
            print('文本数据的读取方法')
    
    class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('硬盘数据的读取方法')
    
        def write(self):
            print('硬盘数据的读取方法')
    
    class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('进程数据的读取方法')
    
        def write(self):
            print('进程数据的读取方法')
    
    wenbenwenjian=Txt()    #实例化
    
    yingpanwenjian=Sata()
    
    jinchengwenjian=Process()
    
    #这样大家都是被归一化了,也就是一切皆文件的思想
    wenbenwenjian.read()   #文本数据的读取方法
    yingpanwenjian.write()  #硬盘数据的读取方法
    jinchengwenjian.read()  #进程数据的读取方法
    
    print(wenbenwenjian.all_type)  #file
    print(yingpanwenjian.all_type)  #file
    print(jinchengwenjian.all_type)   #file
    抽象类

    补充:

    抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化

    java :

    java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题

    但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题

    python:
    python中没有接口类 :
    python中自带多继承 所以我们直接用class来实现了接口类
    python中支持抽象类 : 一般情况下 单继承 不能实例化
    且可以实现python代码

    6.3、接口类和抽象类的区别:

    在java的角度上看 是有区别的
    java本来就支持单继承 所以就有了抽象类
    java没有多继承 所以为了接口隔离原则,设计了接口这个概念,支持多继承了
    python既支持单继承也支持多继承,所以对于接口类和抽象类的区别就不那么明显了
    甚至在python中没有内置接口类

    7、继承顺序

    单继承:子类有的用子类,子类没有用父类;

    多继承:子类的对象调用一个方法,默认是就近原则;

    经典类中:深度优先

    新式类中:广度优先

    python2.7中:新式类与经典类共存,新式类要继承object

    python3:只有新式类,默认继承object

    经典类和新式类中还有一个区别:

    mro方法只在新式类中存在;

    super方法只在python3中新式类中存在;

    super的本质:不是直接找父类,而是根据调用者的节点位置的广度优先顺序来的。

    二、多态

    1、什么是多态?

    多态指的是一类事物有多种形态。

    #动物有多种形态:人,狗,猪
    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')
    动物的多种形态
    #文件有多种形态:文本文件,可执行文件
    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')
    文件有多种形态

    2、多态性

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

    peo=People()
    dog=Dog()
    pig=Pig()
    
    #peo、dog、pig都是动物,只要是动物肯定有talk方法
    #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
    peo.talk()
    dog.talk()
    pig.talk()
    
    #更进一步,我们可以定义一个统一的接口来使用
    def func(obj):
        obj.talk()
    多态性

    3、鸭子类型

    鸭子类型是指不依赖父类的情况下实现两个相似类中的同名方法。

    #二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
    class TxtFile:
        def read(self):
            pass
    
        def write(self):
            pass
    
    class DiskFile:
        def read(self):
            pass
        def write(self):
            pass

    三、封装

    1、什么是封装?

    隐藏对象的属性的实现细节,仅对外提供公共访问方式。

    2、封装的好处?

    1)、将变化隔离;

    2)、便于使用;

    3)、提高复用性;

    4)、提高安全性。

    3、封装原则?

    1)、 将不需要对外提供的内容都隐藏起来;

    2)、把属性都隐藏,提供公共方法对其访问。

    4、私有变量和私有方法

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

    4.1、私有变量

    #其实这仅仅这是一种变形操作
    #类中所有双下划线开头的名称如__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是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形

    自动变形的特点:

    1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果

    2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

    3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

    注意:

    1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

    2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形

    4.2私有方法

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

    #正常情况
    >>> 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
    私有方法

    5、会用到私有的这个概念的场景:

    1)、隐藏起一个属性 不想让类的外部调用

    2)、我想保护这个属性,不想让属性随意被改变

    3)、我想保护这个属性,不被子类继承

  • 相关阅读:
    软件工程实践2019第一次作业
    SDN第三次作业
    SDN第二次作业
    SDN第一次作业
    软工第二次结对
    #软件工程第三次作业
    2019软件工程第一次作业
    php-长文章分页函数
    Sqlilab靶机配置
    目录遍历(复现及修复)
  • 原文地址:https://www.cnblogs.com/gaoya666/p/8313191.html
Copyright © 2020-2023  润新知