zoukankan      html  css  js  c++  java
  • python之继承、抽象类、新式类和经典类

    一、上节补充
    1、静态属性
    静态属性 : 类的属性,所有的对象共享这个变量
      如果用对象名去修改类的静态属性:在对象的空间中又创建了一个属性,而不能修改类中属性的值
      操作静态属性应该用类名来操作

    例1:请你写一个类,能够统计一共实例化了多少个对象?

    复制代码
    class Foo:
        count = 0
        def __init__(self):
            Foo.count += 1
    
    f1 = Foo()
    print(f1.count)       # 1
    f2 = Foo()
    f3 = Foo()
    f4 = Foo()
    f5 = Foo()
    print(f1.count)       # 5
    print(f5.count)       # 5
    print(Foo.count)    # 5
    复制代码

    结论:当类中的属性发生改变的时候,对象中没有同名的属性、方法的时候,对象使用属性名会跟着类中的变量走

    例二:(类的静态属性为可变数据类型,对象也能引用并修改)

    复制代码
    class Foo:
        count = [0]
    
    f1 = Foo()
    f1.count[0] += 1   # 静态属性的修改  1
    print(f1.count[0]) # 对象引用类的静态属性 1
    print(Foo.count[0]) # 类修改后 也是 1
    f1.count = [2]    # 对象新增 2
    print(f1.count)   # 2
    print(Foo.count)  # 类不变 1
    复制代码

    结论:
    1,类的静态属性是可变数据类型时,对象可以引用并修改
    2,只要对象的某个属性被直接赋值,那么一定是对象的命名空间发生变化
    3,只要是静态变量,就用类名操作

     


    (面向对象的三大特性:继承、多态、封装,这里先说一下继承)
    二、继承(提高代码的重用性,规范性)

    (C语言没有面向对象,而java和C#只有单继承没有多继承(但是有接口),
    python和C++有单继承也有多继承(python原本并没有接口,但是有些人为python开发了接口模块,所以在python中,
    需要用接口的时候要安装特定的模块))


    2-1单继承:
    1,语法:
    class A:
      pass

    class B(A):
      pass

    这就是单继承:
      A就叫:父类/超类/基类
      B就叫:子类/派生类


    2,继承与重用:子类可以使用父类中的变量和方法
    下面看个例子了解一下:
    我们都知道猫有自己的名字和吃的粮食,还有猫会喝水吃东西,抓老鼠等,
    狗也有自己的名字和吃的粮食,狗也会喝水吃东西,狗还能看家等。
    因此构建出来的类如下:

    复制代码
    class Cat():
        def __init__(self,name,food):
            self.name = name
            self.food = food
    
        def eat(self):
            print('吃猫粮')
    
        def drink(self):
            print('喝水')
            
        def catch_mouse(self):
            print('抓老鼠')
    
    
    
    class Dog():
        def __init__(self,name,food):
            self.name = name
            self.food = food
    
        def eat(self):
            print('吃狗粮')
    
        def drink(self):
            print('喝水')
        
        def look_after_house(self):
            print('看家')
    
    xiaomao = Cat('啊猫','猫粮')
    print(xiaomao.name)         #啊猫
    xiaomao.eat()                     #吃猫粮
    xiaomao.catch_mouse()   #抓老鼠
    
    xiaogou = Dog('啊狗','狗粮')
    print(xiaogou.name)       #啊狗
    xiaogou.eat()                  #吃狗粮
    xiaogou.look_after_house()   #看家
    复制代码

    这么一看是不是感觉代码有很多重复的地方,比如初始化名字和食物、吃东西和喝水的方法,
    那么怎么样可以节省代码呢?这就要用到继承。
    我们先定义一个公共的类Animal,用来存储猫和狗相同的方法和属性。猫类和狗类分别继承Animal类,
    就可以继承Animal的属性和方法了。

    复制代码
    class Animal:
        def __init__(self,name,food):
            self.name = name
            self.food = food
    
        def eat(self):
            print('吃%s'%(self.food))
    
        def drink(self):
            print('喝水')
    
    
    class Cat(Animal):
    
        def catch_mouse(self):
            print('抓老鼠')
    
    
    class Dog(Animal):
    
        def look_after_house(self):
            print('看家')
    
    xiaomao = Cat('啊猫','猫粮')
    print(xiaomao.name)         #啊猫
    xiaomao.eat()                     #吃猫粮
    xiaomao.catch_mouse()   #抓老鼠
    
    xiaogou = Dog('啊狗','狗粮')
    print(xiaogou.name)       #啊狗
    xiaogou.eat()                  #吃狗粮
    xiaogou.look_after_house()   #看家
    复制代码

    这样一来是不是就简化了很多,而且再来一个猪类,只要它也是有这些公共方法,它也能继承Animal类,
    一下子就可以节省了很多代码。

    其中:
    父类/超类/基类 :Animal
    子类/派生类 :Cat、Dog

     

    3,继承与派生:
    (1)继承:提高代码的重用性,规范代码
    (2)派生:子类在父类的基础上又新创建了自己需要的方法和属性
    (3)父类有的而子类没有:子类对象直接调用 就会直接执行父类的方法
    (4)父类有的而子类也有:1、子类对象调用 直接执行子类中的方法
                2、想在子类中使用父类的名字:父类名、super()去调用

     例如:

    复制代码
    class Animal:
        def __init__(self, name, food):
            self.name = name
            self.food = food
    
        def eat(self):
            print('吃%s' % (self.food))
    
        def drink(self):
            print('喝水')
    
    
    class Cat(Animal):  # Animal的派生类
        def __init__(self, name, food, eye_color):
            self.eye_color = eye_color  # 派生属性
            super().__init__(name, food)
            # Animal.__init__(self,name,food) 跟上一句super()都是调用父类的方法
            # 不同的是super()不需要传self,而直接用父类名.方法(),需要传self
    
        def catch_mouse(self):  # 派生方法
            print('抓老鼠')
    
        def eat(self):   # 不仅执行了父类中的基础功能,还完成了特殊的功能
            Animal.eat(self)
            # super().eat()
            self.weight = 10
    
    
    class Dog(Animal):
        def look_after_house(self):
            print('看家')
    
        def eat(self):
            # Animal.eat(self)
            super().eat()
            self.drink()  # 吃完东西调用父类喝水的方法
    
    
    xiaomao = Cat('阿猫', '猫粮', '绿色')  
    print(xiaomao.eye_color)  # 绿色
    print(xiaomao.food)  #猫粮
    xiaomao.catch_mouse() #抓老鼠
    xiaomao.eat()  #吃猫粮
    print(xiaomao.weight)  # 10
    
    xiaogou = Dog('啊狗', '狗粮') 
    xiaogou.eat()  #吃狗粮  喝水
    复制代码

    2-2多继承:
    1,语法:
    class A:
      pass

    class B:
      pass

    class C(A,B):
      pass

    C既继承了A,又继承了B


    2,实例:

    复制代码
    # 天鹅:飞 游泳 走路
    # 老虎:走路 游泳
    # 鹦鹉:飞 说话 走路
    
    class Animal:
        def __init__(self,name):
            self.name = name
    
    class FlyAnimal(Animal):  #会飞的动物类
        def fly(self):
            print('%s在飞' % self.name)
    
    class WalkAnimal(Animal):  #会走路的动物类
        def walk(self): 
            print('%s在走路'%self.name)
    
    class SwimAnimal(Animal):  #会游泳的动物类
        def swim(self):
            print('%s在游泳'%self.name)
    
    class Tiger(SwimAnimal,WalkAnimal):  # 老虎
        pass
    
    class Swan(SwimAnimal,WalkAnimal,FlyAnimal):  # 天鹅
        pass
    
    class Parrot(FlyAnimal,WalkAnimal):  # 鹦鹉
        def talk(self):
            print('%s说话了'%self.name)
    
    swan = Swan('天鹅')
    swan.fly()  # 天鹅在飞
    swan.walk()  # 天鹅在走路
    复制代码

     

     

    三、抽象类:抽象类是一个规范,它基本不会实现什么具体的功能,只能被继承,不能被实例化
    1、抽象类的作用:规范编程模式
      多人开发、复杂的需求、后期的扩展
      是一种用来帮助我们完成规范化的手段

    2、如何定义抽象类
      1,from abc import ABCMeta,abstractmethod
      2,在这个类创建的时候指定 metaclass = ABCMeta
      3,在你希望子类要实现的方法的上一行加上一个 @abstractmethod装饰器

    3、使用抽象类
      1,继承这个类
      2,必须实现这个类中被@abstractmethod装饰器装饰的方法

    4、实例

    复制代码
    # 支付功能
    
    from abc import ABCMeta, abstractmethod
    class Payment(metaclass=ABCMeta):  # 模板的功能
        @abstractmethod                # abstractmethod是一个装饰器,装饰器怎么用?放在函数或者类的上一行
        def pay(self): pass
    
    # 这样就构建了一个抽象类Payment,并声明了子类必须要实现的方法是 pay(),若子类没有定义pay(),则实例化时会报错
    
    
    class Alipay(Payment):  # 继承了抽象类,就必须实现抽象类中被@abstractmethod装饰器装饰的方法 pay()
        def pay(self, money):
            print('使用支付宝支付了%s元' % money)
    
    
    class Wechatpay(Payment):
        def pay(self, money):
            print('使用微信支付了%s元' % money)
    
    
    class My_pay(Payment):  # 这里没有定义pay()方法,那么在实例化的时候机会报错
        def fuqian(self,money):
            print('你支付了%s元' % money)
    
    
    def pay(obj, money):
        obj.pay(money)
    
    
    # p = Payment()  # 报错 抽象类不能被实例化
    
    a = Alipay()
    # a.pay(100)
    pay(a,100)  # 使用支付宝支付了100元
    
    we = Wechatpay()
    # we.pay(200)
    pay(we,200)  # 使用微信支付了200元
    
    my = My_pay()  # 报错:类中没有定义抽象类的pay方法
    pay(my,300)
    复制代码

     

     

    四、新式类
    1、新式类和经典类:
    继承了object的类就是新式类
    在py3中默认都继承了object因此所有的类都是新式类
    在py2中既有新式类又有经典类


    python3.x:
    在python3.x版本中所有的类都是新式类
    所有的新式类都有一个默认的父类 : object

    class Person1:pass
    class Person2():pass
    class Person3(object):pass
    # __bases__方法是查看某个类继承的所有父类
    print(Person1.__bases__)  # (<class 'object'>,)
    print(Person2.__bases__)  # (<class 'object'>,)
    print(Person3.__bases__)  # (<class 'object'>,)

     

    python 2.7:
    经典类和新式类并存
    class Student:pass # 经典类
    class Student(object):pass # 新式类

    class Person1:pass # 经典类
    class Person2():pass #经典类
    class Person3(object):pass #新式类


    2、多继承的顺序(在新式类和经典类之间的区别)
    新式类
      所有的多继承关系寻找方法的顺序 :遵循广度优先算法
      继承object
      类名.mro() # (返回一个类的寻找顺序(继承顺序)的列表)
      super : super不是单纯的找父类,而是遵循mro顺序的

    经典类
      python2.x
      不主动继承object
      经典类在找父类中方法的过程中遵循:深度优先算法
      不提供mro方法和super

    2-1、新式类中

    复制代码
    class A:
        def func(self):
            print('A')
    
    class B(A):
        def func(self):
            super().func()
            print('B')
    
    class C(A):
        def func(self):
            super().func()
            print('C')
    
    class D(B,C):
        def func(self):
            super().func()
            print('D')
    print(D.mro())     # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    D().func()         # A C B D
    复制代码

     

    复制代码
    class A:
        pass
    
    class B(A):
        pass
    
    class C(A):
        pass
    
    class D(B):
        pass
    
    class E(C):
        pass
    
    class F(D,E):
        pass
    
    print(F.mro())
    结果:
    # [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>,
    <class '__main__.A'>, <class 'object'>]
    复制代码

     

     

     

  • 相关阅读:
    第三章 操作符
    exit函数
    详解C++ friend关键字
    放假了,暂告一段落,迎接研究生
    使用const 提高函数的健壮性
    使用断言assert
    对return 语句的正确性和效率进行检查
    函数堆栈
    somethings about QSplitter
    引用和引用参数
  • 原文地址:https://www.cnblogs.com/yidashi110/p/10092257.html
Copyright © 2011-2022 走看看