zoukankan      html  css  js  c++  java
  • 第三周:Python抽象类

    Python抽象类

    在Python中抽象类只能被继承不能被实例化

    并且,抽象类中只有抽象方法普通方法


    定义抽象类和抽象方法

    Python的抽象类的定义需要abc模块。(= =...)

    # 导入抽象类需要用到的库
    from abc import ABCMeta, abstractmethod
    
    class Person(metaclass=ABCMeta):
        """使用元类(模板类)"""
        
        pname = "这是Person抽象类"  # 可以定义属性
        
        # 定义抽象方法,抽象方法不需要方法体
        # 需要在方法前加@abstractmethod装饰器
        @abstractmethod
        def run(self):
            pass  # 不需要写方法体
        
        @classmethod  # 可以定义类方法
        def eat(cls):
            print("在吃东西啊")
            
        @staticmethod
        def drink():  # 可以定义静态方法
            print("在喝东西啊")
            
        def sleep(self):  # 可以定义普通方法
            print("在睡觉啊")
    

    抽象方法不需要写方法体,但并不是不能写。可以写,但是没有用。

    抽象类中可以定义属性、类方法、静态方法


    使用抽象类和抽象方法

    class Student(Person):
        sno = 123  # 学号
        # 不会提示要要重写方法
        
        def run(self):
            print("跑")
        
        
    # 不重写抽象方法就无法实例化对象,并且抛异常
    s = Student()
    s.run()  # 跑
    s.eat()  # 在吃东西啊
    s.sleep()  # 在睡觉啊
    print(s.pname)  # 这是Person抽象类
    s.drink()  # 在喝东西啊
    
    # p = Person() 运行时抛异常	
    

    继承了抽象类,就必须重写抽象类中的抽象方法,否则无法实例化对象,并抛异常。


    应该尽量避免多继承

    如果出现多继承,那么对于同名抽象方法,哪个继承类靠前就是实现的哪个类的抽象方法。

    这对于抽象方法来说没什么意义。

    但是!

    由于抽象类不止能写抽象方法,还可以写属性、类方法、静态方法、普通方法,这些方法如果同名,就是按继承顺序来继承和重写的了。

    class A(metaclass=ABCMeta):
    
        num = 10
        
        def __init__(self):
            print("这是A的构造方法")
        
        @abstractmethod
        def show(self):
            pass
    
        @classmethod  # 可以定义类方法
        def eat(cls):
            print("吃 1")
    
        @staticmethod
        def drink():  # 可以定义静态方法
            print("喝 1")
    
        def sleep(self):  # 可以定义普通方法
            print("睡 1")
       
    
    class B(metaclass=ABCMeta):
    
        num = 20
        
        def __init__(self):
            print("这是B的构造方法")
            
        @abstractmethod
        def show2(self):
            pass
    
        @classmethod  # 可以定义类方法
        def eat(cls):
            print("吃 2")
    
        @staticmethod
        def drink():  # 可以定义静态方法
            print("喝 2")
    
        def sleep(self):  # 可以定义普通方法
            print("睡 2")
        
    class C(B, A):
        
        def show(self):
            print("showA")
            
        def show2(self):
            print("showB")
       
            
    c = C()
    # 在打印20前,先打印了 这是B的构造方法  但是没有打印 这是A的构造方法
    print(c.num)  # 20
    c.show()  # showA
    c.eat()  # 吃 2
    c.drink()  # 喝 2
    c.sleep()  # 睡 2
    c.show2() # showB
    

    抽象类中可以写构造方法,但是子类继承时只会调用第一个继承类的构造方法


    抽象类方法和抽象静态方法

    class A和class B中增添这两个方法

    @classmethod
    @abstractmethod
    def swim(cls):  # 抽象 类方法
        pass
    
    @staticmethod
    @abstractmethod
    def run():  # 抽象 静态方法
        pass
    

    与抽象方法一样,方法体可以不写,能写,但没必要。

    子类必须实现这两个方法才能实例化。

    不过!

    子类实现后,就是普通方法了。与普通抽象方法的实现没啥区别。

    class C(B, A):
        
        def show(self):
            print("show")
            
        def swim(self):
            print("swim")
            
        def run(self):
            print("run")
    
    # 不能这样永类名调用
    C.swim()
    C.run()
    

    所以还要在实现的方法上加对应的装饰器。

    @classmethod
    def swim(cls):
        print("swim")
        
    @staticmethod
    def run():
        print("run")
    

    然而这时写反装饰器(不按抽象类中的来),也不会有啥事。

    所以说抽象类只保证了抽象类本身不能实例化,和子类必须实现与抽象的方法(抽象方法、抽象类方法、抽象静态方法)同名的方法

    但是不保证子类的实现方法和原抽象的方法是同种类型

    简单来说,只要子类有同名的方法就行,管它是不是为了实现抽象的方法的,还是自己本身独有的。


    抽象“属性”

    Python中有个个特殊的装饰器@property。用了它,方法就可以当属性一样用(调用时不需要加(),直接.属性名

    @property
    @abstractmethod
    def age(self):  # 抽象属性
        pass
    

    但是在实现时也必须要加上@property,否则也就当成普通方法了。

    @property
    def age(self):
        print("age")
        return 22
    
    print(c.age)  # 先打印 22 再打印 age
    print(type(c.age))  # <class 'int'>
    c.age = 30  # 这样会抛异常,只读不能修改
    print(c.age)
    

    这个属性是只读的,不能修改。

    当然可以配合@X.setter@X.deleter来使得该“抽象”属性可读可写可删除。(其中X@property修饰的方法的方法名)

    这里不演示了,有兴趣的可以去查看@property/@X.setter/@X.deleter三个装饰器的使用情况和方法。

    也一定要注意子类中实现时,对应也要加上装饰器修饰,否则会被当成普通方法。


    使用抽象类的好处

    对于一些有相似属性和方法类,可以统一把这些属性和方法抽出来放在一个类中。

    这样可以很好的理清类直接的继承关系、方法属性的意义、做到了解耦合

    比如说,猫和狗都是动物,都有年龄、性别等属性、都有吃喝拉撒睡等动作(假设这些共有动作两者是不同的)。那么就可以把这些属性动作抽出来放在一个动物类中(class Animal)。让猫类(class Cat)和狗类(class Dog)都继承这个动物类,然后根据自身特点实现这些属性和动作即可。

    其实如果动作相同的话就没必要写成抽象方法,而是写成普通方法即可,这样不需要重写方法,直接调用就行。

    抽象就是一种规范,让继承了 抽象类 后的子类必须实现抽象方法才能够实例化。


    接口?

    Python中没有接口的概念,但是鉴于Java的接口理念,Python可以用abc模块像实现抽象类那样,来实现接口类。

    只要在类中只写抽象的方法、static和final属性(用@property等和@staticmethod配合着使用),那么就可以。

    当然肯定没有Java中那么严格的检验,不过也勉强能做到。

    但最终还是用看开发的需求来决定是否要这样去写,不能为了炫技而去写一些只能自己才能看懂的代码啊,那就失去了编程的初衷了。

  • 相关阅读:
    idea 控制到不能输出中文
    后台学习
    carthage和cocoapods
    如何优雅地调试
    从一次内存峰值说起
    多线程单线程,同步异步,并发并行,串行队列并行队列,看这里就对了
    iOS网络层设计感想
    iOS团队风格的统一
    AFNetworking二次封装的那些事
    UITextFiled,UITextView长度限制
  • 原文地址:https://www.cnblogs.com/jiyou/p/14024324.html
Copyright © 2011-2022 走看看