zoukankan      html  css  js  c++  java
  • Python()- 面向对象三大特性----继承

    继承:

    继承是一种创建新类的方式,
    在python中,新建的类可以继承一个或多个父类(基类或超类),新建的类是所继承的类的(派生类或子类)


    人类和狗 有相同的属性, 提取了一个__init__方法,在这个方法里放一些共有的属性
    人类和狗 在相同的方法,提取了一个def func():方法,在这个方法里放一些共有的方法

    单继承和多继承

    class Par1:
        pass
    class Par2:
        pass
    class Sub1(Par1):
        pass
    class Sub2(Par1,Par2):
        pass
    #__base__只查看从左到右的第一个父类,而__bases__查看所有父类
    print(Sub1.__bases__)
    #(<class '__main__.Par1'>,)
    print(Sub2.__bases__)
    #(<class '__main__.Par1'>, <class '__main__.Par2'>)
    ------------------------------------
    print(Par1.__base__)
    #<class 'object'>
    

      

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

     

    class Par1:
        def __init__(self,name):
            self.name = name
        def __str__(self):
            return self.name
    p = Par1('kitty')
    print(p)
    # kitty
    

      

    class Foo1:
      def __init__(self,name):     # self是f对象
        self.name = name
      def __str__(self):
        return self.name
    class Foo2(Foo1):
      a = 1
    f = Foo2('kitty')     #Foo2中没有__init__函数,就去基类去找,然后传入参数
    print(f.name)     # kitty
    

      

    继承与抽象:

    世间本来没有人类,只是很多个具体的人对象,后来把所有的人抽象成(划规成)一类,才有了人类的概念
    so:   抽象是从下往上
      继承从上往下


    继承与重用性:

    继承的目的是为了减少代码的重复性


    比如:

      想创建一个b类, 但是发现b类的属性大部分和a类的相同, 就让b去继承a的属性,节省代码

    提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.

    经典类 和 新式类:

    python3都是新式类 (新式类默认继承object class Foo(object): == class Foo: )

    python2中经典类和新式类共存

    • class Foo: 是经典类
    • class Foo(object): 是新式类

    钻石继承:

    • 经典类: 深度优先,就是单线走到头,再去其它线
    • 新式类: 就是单线走到剩余最后一个父类停止,再去找其它线,最后一条线走到尾

    经典类继承顺序:

    深度优先

     新式类继承顺序:

      广度优先:

    如果找到b就去找c c再去找e e在去找f f没有就报错了 因为f找不到d 单线
    =====================================================
    python2 中: 最原始的加上object,往后的都会继承object
    ======================================

    再来看:

    class D:
      def __init__(self):
        print('d')
    class C(D):
      def __init__(self):
        print('c')
        super().__init__()
    class B(D):
      def __init__(self):
        print('b')
        super().__init__()
    class A(B,C):
      def __init__(self):
        print('a')
        super().__init__()
    #mro
    a = A()
    print(A.mro())
    ##结果如下:
    a
    b
    c
    d
    [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
    和mro的顺序一样 广度优先 (super只有新式类中有)
    在多继承中,super不只是寻找当前类的父类,而是依据mro顺序,
    从A节点出发,根据广度优先排序查找下一个类
    
     

    Mixin

    在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为Mixin。
    为了更好地看出继承关系,我们把Runnable和Flyable改为RunnableMixin和FlyableMixin。类似的,你还可以定义出肉食动物CarnivorousMixin和植食动物HerbivoresMixin,让某个动物同时拥有好几个Mixin:

    class Dog(Mammal, RunnableMixin, CarnivorousMixin):
        pass

    Mixin的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个Mixin的功能,而不是设计多层次的复杂的继承关系。
    Python自带的很多库也使用了Mixin。举个例子,Python自带了TCPServer和UDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixin和ThreadingMixin提供。通过组合,我们就可以创造出合适的服务来。
    比如,编写一个多进程模式的TCP服务,定义如下:

    class MyTCPServer(TCPServer, ForkingMixin):
        pass

    编写一个多线程模式的UDP服务,定义如下:

    class MyUDPServer(UDPServer, ThreadingMixin):
        pass

    如果你打算搞一个更先进的协程模型,可以编写一个CoroutineMixin:

    class MyTCPServer(TCPServer, CoroutineMixin):
        pass

    这样一来,我们不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。

    总结:

    由于Python允许使用多重继承,因此,Mixin就是一种常见的设计。只允许单一继承的语言(如Java)不能使用Mixin的设计。

    派生


    派生:

    class A:       # 基类(提取了狗和人的共同属性)
        def __init__(self,name, aggressivity, life_value):     #里面的self是对象
            self.name = name
            self.aggressivity = aggressivity
            self.life_value = life_value
        def eat(self):
            print('A  eating')
    class Dog(A):  # 定义一个狗类
        def __init__(self,name, aggressivity, life_value,breed,):   #派生一个__init__方法
            #A.__init__(self,name, aggressivity, life_value)        : 类名.__init__(参数)   
            super().__init__(name, aggressivity, life_value)        #调用基类__init__方法:super().__init__()
            self.breed = breed  # 每一只狗都有自己的品种;
        def bite(self,people):                                  #派生bite方法    父类中没有的属性&方法
            people.life_value -= self.aggressivity
        def eat(self):                          #重新定义eat   (派生)
            A.eat(self)                         #如果想加上父类的方法: A.eat(对象)
            print('dog eating')            
    class Person(A):  # 定义一个人类
        def __init__(self,name, aggressivity, life_value,money):    #派生
            super().__init__(name, aggressivity, life_value)        #调用基类__init__方法
            self.money = money
        def attack(self,dog):
            dog.life_value -= self.aggressivity
        def get_weapon(self,weapon_obj):
            if self.money > weapon_obj.price:
                self.money -= weapon_obj.price  # 金老板花钱买武器
                self.weapon = weapon_obj  # 金老板装备打狗棒
                self.aggressivity += weapon_obj.aggr  # 金老板的攻击力增加了
    jin = Person('jin',250,250,100)
    dog = Dog('酷狗',33,50,'藏獒')
    print(jin.name)     # jin
    print(jin.money)    # 100
    print(dog.name)     # 酷狗
    print(dog.breed)    # 藏獒
    dog.eat()
    # A  eating
    # dog eating
    -------------------
    A.eat(dog)      #A  eating          #类名.方法名(对象名)
    --------------------------------
    super(Dog,dog).eat()    #A  eating      super(类名,对象名)   super在外面用,必须传参数

    派生方法和属性:
    1.在子类中增加父类没有的
    2.如果再调用父类的的方法和属性

       父类.__init__(self,参数...) ⇒ 指名道姓调用

       super()__init__(参数...) 

      在类外面用super的时候, super(子类名,对象).属性
      

    小结:

    子类有,用自己的,没有用父类的
    子类有,父类有,仍然想用父类的:
    经典类: 父类.__init__(self,参数...) 父类.方法名(对象)
    新式类: super().__init__(参数...) 父类.方法名(对象)

    一般情况下super都满足了
    指名道姓调用父类的方法 父类.方法(self) self是子类的对象


    例题:打印结果

    class Foo:
        def __init__(self):     (self是对象,对象自己有func())
            self.func()
        def func(self):
            print('父类')
    class Son(Foo):
        def func(self):
            print('子类')
    s = Son()
    #子类
    ----------------
    print(Son.mro())        新式类才有的查看继承顺序的方法:    print(类名.mro())
    [<class '__main__.Son'>, <class '__main__.Foo'>, <class 'object'>]

    接口类:



    接口类:多继承,父类不实现
    (模板:都按我的来,这是约定好的)
    =================================
    格式:

    from abc import ABCMeta, abstractmethod
    class Foo(metaclass=ABCMeta)
      @abstractmethod
      def func(self,money):pass

    =============================


    # 是
    规范子类的一个模板,只要接口类中定义的,就应该在子类中必须实现

    #
    接口类不能被实例化,它只能被继承

    # 支持
    多继承

    from abc import ABCMeta, abstractmethod
    
    
    class Payment(metaclass=ABCMeta):  # 模板,接口类               metaclass  元类  先不用深究
        @abstractmethod  # 装饰接口类中方法,     加上这个装饰器,自动检测子类中的方法名
        def pay(self, money): pass
    
        # def get(self):pass           接口类有,下面子类必须有,如果包含没有@abstractmethod的方法,就不是接口类,而是抽象类
    
    
    class Apple_Pay(Payment):
        def pay(self, money):
            print('您使用苹果支付支付了%s元' % money)
    
        def func(self):
            print('类自己的方法')
    
    
    class Ali_Pay(Payment):
        def pay(self, money):
            print('您使用支付宝支付了%s元' % money)
    
    
    class WeChat_Pay(Payment):
        def pay(self, money):
            print('您使用微信支付了%s元' % money)
    
    
    def pay(obj, money):
        return obj.pay(money)  # 这个pay是和类里面的一一对应的
    
    
    apple = Apple_Pay()
    ali = Ali_Pay()
    wechat = WeChat_Pay()
    
    pay(apple, 100)  # apple.pay(100)
    pay(wechat, 200)

    ===========

    def pay(obj,money):
      return obj.pay(money) 
    
    类似:
    def next(obj):
      return obj.__next_()
    
    __next__() next() 内置函数
    

    =======================================

    接口类: 主动触发异常

    class Payment(Payment):
      def pay(self,money):raise NotImplementedError 
    (这个方法有个缺点,子类只有调用那个方法时才会抛错,推荐@abstractmethod,可以在类被实例化后触发它)
    ..................下面代码省略...........
    报错
      def pay(self,money):raise NotImplementedError
      NotImplementedError
    

    =========================================


    ===============

    接口的多继承:

    接口隔离原则:使用多个专门的接口,而不使用单一的总接口.(客户端不应该依赖那些不需要的接口)

    (模板不能放到一个里面)
    (接口类鼓励多继承)                           # (抽象类避免多继承)

    ===========================================

    python 本来没有接口类, 接口类的@abstractmethod 是借用的 抽象类的模块(装饰器)来实现接口类

    接口类概念来自java

    抽象类:

    模板 规范
    抽象类可以实现一些子类共有的功能和属性抽象类避免多继承

    python 没有接口的概念
    只能借助抽象类的模块 来实现接口类
    ---------------------------------------------------------
    接口 —— 来自java,但是java没有多继承 , java有个Interface关键字,
    ---------------------------------------------------------

    例子:
    文件操作 :打开文件 关闭文件 写文件 读文件
    硬盘操作:打开,关闭,写 读
    进程文件:打开 关闭,读 写
    都有打开,关闭,读取,写入的功能

    from abc import ABCMeta,abstractmethod          #格式参考接口类格式
    class Base(metaclass=ABCMeta):
        def __init__(self,filename):                #抽象类可以有__init_(),  接口类不能有   其它的没感觉两个有有啥区别
            self.filename = filename
        @abstractmethod #抽象方法
        def open(self):
            return 'file_handler'
    
        @abstractmethod
        def close(self):pass
    
        @abstractmethod
        def read(self):pass
    
        @abstractmethod
        def write(self):pass
    
    class File(Base):
        def open(self):pass
        def close(self):pass
        def read(self):pass
        def write(self):pass

      

    抽象类不能被实例化
    这个抽象类可以规范子类必须实现抽象类中的抽象方法 (没有强制子类必须有父类的所有方法)

    例二: 原博客地址: https://www.cnblogs.com/bjdxy/archive/2012/11/15/2772119.html
    由于python 没有抽象类、接口的概念,所以要实现这种功能得abc.py 这个类库,具体方式如下

    from abc import ABCMeta, abstractmethod
     
    #抽象类
    class Headers(object):
        __metaclass__ = ABCMeta
    
        def __init__(self):
            self.headers = ' '
    
        @abstractmethod
        def _getBaiduHeaders(self):pass       
    
        def __str__(self):
            return str(self.headers)             
    
        def __repr__(self):
            return repr(self.headers)
    
    #实现类
    class BaiduHeaders(Headers):
        def __init__(self, url, username, password):
            self.url = url
            self.headers = self._getBaiduHeaders(username, password)
    
        def _getBaiduHeaders(self, username, password):
            client = GLOBAL_SUDS_CLIENT.Client(self.url)
            headers = client.factory.create('ns0:AuthHeader')
            headers.username = username
            headers.password = password
            headers.token = _baidu_headers['token']
            return headers

    如果子类不实现父类的_getBaiduHeaders方法,则抛出TypeError: Can't instantiate abstract class BaiduHeaders with abstract methods  异常

    ===============================================

    抽象类和接口类的区别

    python的语法角度上来说,是没有区别的,(因为没有接口类的概念,接口类借用抽象类的概念和模块)

    小区别:接口类不能实现内部的代码,
    抽象类能实现司内部的代码(__init__())

    应用场景的不同:
    抽象类不鼓励多实现(少继承)
    接口类鼓励多实现(多继承)

    所以一般情况下,没有法子通过代码上的不同去区分它们,只能通过应用场景去区分它们.(一种思想上的改变)

    抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),
    而接口只强调函数属性的相似性。
    抽象类是一个介于类和接口之间的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计 


    ================================================

  • 相关阅读:
    UVA 562 Dividing coins --01背包的变形
    HDU 2602 Bone Collector --01背包
    Topcoder SRM 618 Div2 --900
    【转】Python 列表排序
    线段树之区间最大连续和问题
    【转】Python中string的strip,lstrip,rstrip用法
    《Python核心编程》部分代码习题实践(持续更新)
    《Python核心编程》部分错误纠正(勘误表)(持续更新)
    UVA 103 Stacking Boxes --LIS
    POJ 1836 Alignment --LIS&LDS
  • 原文地址:https://www.cnblogs.com/zhzhlong/p/8531590.html
Copyright © 2011-2022 走看看