zoukankan      html  css  js  c++  java
  • python-----面向对象三大特性

    一、继承

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

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

    class A:   #定义父类
        pass
    class B:   #定义父类
        pass
    class C(A):  #单继承,基类是A,派生类是C
        pass
    class D(A,B):   #python支持多继承,用逗号分隔开多个继承的类
        pass
    print(C.__bases__)   # (<class '__main__.A'>,)
    #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
    继承

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

    class Animal:
        def __init__(self,name):
            self.name = name
        def eat(self):
            print('')
        def drink(self):
            print('')
    class Dog(Animal):
        def guard(self):
            print('看门')
    class Bird(Animal):
        def lay(self):
            print('下蛋')
    
    dog = Dog()
    bird = Bird()
    dog.drink()
    View Code

      用已有的类建立一个新的类,这样就重用了已经有的那个类中的属性跟方法,大大省了编程工作量。当然,也可以在自己的类中重新定义与父类相同的属性和方法,那么在调用属性和方法时,就先以自己的为准了。

    class Animal:
        '''
        人和狗都是动物,所以创造一个Animal基类
        '''
        def __init__(self, name, aggr, hp):
            self.name = name  # 人和狗都有自己的昵称;
            self.aggr = aggr # 人和狗都有自己的攻击力;
            self.hp = hp   # 人和狗都有自己的生命值;
    
        def eat(self):
            print('%s is eating'%self.name)
    
    class Dog(Animal):
        '''
        狗类,继承Animal类
        '''
        def bite(self, people):
            '''
            派生:狗有咬人的技能
            :param people:
            '''
            people.hp  -= self.aggr
    
    class Person(Animal):
        '''
        人类,继承Animal
        '''
        def attack(self, dog):
            '''
            派生:人有攻击的技能
            :param dog:
            '''
            dog.hp  -= self.aggr
    
    p1 = Person('jim',10,1000)
    ha2 = Dog('二愣子',50,1000)
    print(ha2.hp )  # 1000
    p1.attack(ha2)
    print(ha2.hp )  #990
    View Code

    在python3中,子类执行父类的方法时也可以直接用super方法

    class A:
        def func(self):
            print('A')
    
    class B(A):
        def func(self):
            super().func()
            # super(B,self).func()
            # A.func(self)
            print('B')
    
    a = A()
    b = B()
    b.func()  # A  B
    super(B,b).func()  # A
    super
    class Animal:
        '''
        人和狗都是动物,所以创造一个Animal基类
        '''
        def __init__(self, name, aggr, hp):
            self.name = name  # 人和狗都有自己的昵称;
            self.aggr = aggr  # 人和狗都有自己的攻击力;
            self.hp = hp  # 人和狗都有自己的生命值;
    
        def eat(self):
            print('%s is eating'%self.name)
    
    class Dog(Animal):
        '''
        狗类,继承Animal类
        '''
        def __init__(self,name,breed,aggr,hp):
            super().__init__(name, aggr, hp) #执行父类Animal的init方法
            self.breed = breed  #派生出了新的属性
    
        def bite(self, people):
            '''
            派生出了新的技能:狗有咬人的技能
            :param people:
            '''
            people.hp -= self.aggr
    
        def eat(self):
            # Animal.eat(self)
            #super().eat()
            print('from Dog')
    
    class Person(Animal):
        '''
        人类,继承Animal
        '''
        def __init__(self,name,aggr, hp,money):
            #Animal.__init__(self, name, aggr, hp)
            #super(Person, self).__init__(name, aggr, hp)
            super().__init__(name,aggr, hp)  #执行父类的init方法
            self.money = money   #派生出了新的属性
    
        def attack(self, dog):
            '''
            派生出了新的技能:人有攻击的技能
            :param dog:
            '''
            dog.hp -= self.aggr
    
        def eat(self):
            #super().eat()
            Animal.eat(self)
            print('from Person')
    
    jim = Person('jim',10,1000,600)
    ha2 = Dog('二愣子','哈士奇',10,1000)
    print(jim.name)
    print(ha2.name)
    jim.eat()
    派生新的属性

     接口类:

    1:继承基类的方法,并且做出自己的改变或者扩展(代码重用)  

    2:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

    class Alipay:
        def pay(self,money):
            print('支付宝支付了%s元'%money)
    
    class Applepay:
        def pay(self,money):
            print('apple pay支付了%s元'%money)
    
    def pay(pay_obj,money):
        pay_obj.pay(money)
    p = Alipay()
    pay(p,200)
    View Code

    上述代码在开发中容易出问题,如下面出现错误情况代码

    class Alipay:
        def pay(self,money):
            print('支付宝支付了%s元'%money)
    
    class Applepay:
        def pay(self,money):
            print('apple pay支付了%s元'%money)
    
    class Wechatpay:
        def fuqian(self,money):
            '''
            实现了pay的功能,但是名字不一样
            '''
            print('微信支付了%s元'%money)
    
    def pay(payment,money):
        payment.pay(money)
    p = Wechatpay()
    pay(p,200)   #执行会报错  AttributeError: 'Wechatpay' object has no attribute 'pay'
    错误情况

    借用abc模块来实现接口

    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)
    
    p = Alipay()
    pay(p,200)
    abc模块
    依赖倒置原则:
    高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程。


    接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。
    这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
    归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

    抽象类:
    与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化.
    为什么要有抽象类?
    如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
    从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
      从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的
    # 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
    
    # 接口类  刚好满足接口隔离原则 面向对象开发的思想 规范
    在python中实现抽象类
    # 抽象类的本质还是类,指的是一组类的相似性,包括数据属性和函数属性,而接口只强调函数属性的相似性。
    # 抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计 。
    # 在python中,并没有接口类这种东西。

    1.多继承问题
    # 在继承抽象类的过程中,我们应该尽量避免多继承;
    # 而在继承接口的时候,我们反而鼓励你来多继承接口
    # 接口隔离原则:
    # 使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
    # 2.方法的实现
    # 在抽象类中,我们可以对一些抽象方法做出基础实现;
    # 而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现

    钻石继承
    继承顺序:
      1、python的类可以继承多个类,Java和C#则只能继承一个类
    2、python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
        a:当类是经典类时,多继承情况下,按按照深度优先方式查找。
        b:当类是新式类时,多继承情况下,按按照广度优先方式查找。
       python3中只有广度优先
    class A(object):
        def test(self):
            print('from A')
    
    class B(A):
        def test(self):
            print('from B')
    
    class C(A):
        def test(self):
            print('from C')
    
    class D(B):
        def test(self):
            print('from D')
    
    class E(C):
        def test(self):
            print('from E')
    
    class F(D,E):
        # def test(self):
        #     print('from F')
        pass
    f1=F()
    f1.test()
    print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
    
    #新式类继承顺序:F->D->B->E->C->A
    #经典类继承顺序:F->D->B->A->E->C
    #python3中统一都是新式类
    #pyhon2中才分新式类与经典类
    继承顺序

    继承的作用

    减少代码的重用
    提高代码可读性
    规范编程模式
    抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。
    继承:子类继承了父类的方法和属性
    派生:子类在父类方法和属性的基础上产生了新的方法和属性

    二、多态

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

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

    鸭子类型

    # list tuple
    # 不崇尚根据继承所得来的相似
    # 我只是自己实现我自己的代码就可以了。
    # 如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型
    # list tuple 这种相似,是自己写代码的时候约束的,而不是通过父类约束的
    # 优点 : 松耦合 每个相似的类之间都没有影响
    # 缺点 : 太随意了,只能靠自觉
    #二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
    class TxtFile:
        def read(self):
            pass
    
        def write(self):
            pass
    
    class DiskFile:
        def read(self):
            pass
        def write(self):
            pass
    例子

    接口类、抽象类的总结:

    python中没有接口类,但有抽象类;abc模块中的metaclass = ABCMeta, @abstructmethod

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

    从java的角度身上看,接口类与抽象类是有区别的:

      1:java本来就支持单继承,所以就有了抽象类

      2:java没有多继承,所以为了接口隔离原则,设计了接口这个概念,支持多继承了。

    python既支持单继承也支持多继承,所以对于接口类和抽象类的区别就不那么明显了。甚至在python中没有设置接口类。

    三、封装

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

     封装的好处:1.将变化隔离;2.便于使用;3.提高复用性;4.提高安全性

     封装原则:1.将不需要对外提供的内容都隐藏起来;2.把属性都隐藏,提供公共方法对其访问。

    在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是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
            
    # 这种自动变形的特点:
    #     1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
    #     2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
    #     3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x, 而父类中变形成了:_父类名__x,
    #       即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
    # 这种变形需要注意的问题是:
    #     1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然
    #       后就可以访问了,如a._A__N
    #     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
    View Code

    封装与扩展性

    封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),
    只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约
    定不变,则代码改变不足为虑。
    class Room:
        def __init__(self,name,length,width):
            self.__name = name
            self.__length = length
            self.__width = width
        def get_name(self):
            return self.__name
        def set_name(self,newName):
            if type(newName) is str and newName.isdigit() == False:
                self.__name = newName
            else:
                print('不合法的姓名')
        def area(self):  #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
            return self.__length * self.__width
    #使用者
    jin = Room('金老板',2,1)
    print(jin.area())  #使用者调用接口area
    jin.set_name('2')
    print(jin.get_name())
    View Code
    
    
     
  • 相关阅读:
    残疾流浪歌手的令人陶醉的声音
    关于浏览器参数的获取
    项目开发文档模板
    scoped_ptr源码
    从逗号分隔的字符串中删除某个子串的js函数
    合并两个数组的js函数
    comutil.h移值(_com_error,_bstr_t,_variant_t类的移值)
    判断两个对象是否相等的js函数
    托管可执行文件的结构(The Structure of a Managed Executable File)
    访问cookie的js函数
  • 原文地址:https://www.cnblogs.com/huangjm263/p/8305013.html
Copyright © 2011-2022 走看看