zoukankan      html  css  js  c++  java
  • Python中的abc模块

    前言
    在《抽象基类(ABC)》中,基于C++讲述抽象基类。尽管Python设计上以鸭子类型为主,但仍有抽象基类(ABC)的一席之地,它被封装在了abc模块中供程序员使用。

    abc模块有以下两个主要功能:

    某种情况下,判定某个对象的类型,如:isinstance(a, Sized)
    强制子类必须实现某些方法,即ABC类的派生类

    判断类型
    当我们判断一个对象是否存在某个方法时,可以使用内置方法hasattr()。

    class A(object):
    def __len__(self):
    pass

    if __name__ == "__main__":
    a = A()
    print("存在__len__方法" if hasattr(a, "__len__") else "没有__len__方法")

    # 输出:
    存在__len__方法

    但在Python中,使用hasattr()并非优雅解法,这里建议isinstance()。

    abc模块中定义了Sized类,利用Sized可以判断一个对象里是否存在__len__方法,即:可否对这个对象使用len()函数。

    from collections.abc import Sized

    class A(object):
    def __len__(self):
    pass

    if __name__ == "__main__":
    a = A()
    print("存在__len__方法" if isinstance(a, Sized) else "没有__len__方法")

    # 输出:
    存在__len__方法

    isinstance实现原理
    让我们看看Sized类的源码:

    class Sized(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __len__(self):
    return 0

    @classmethod
    def __subclasshook__(cls, C):
    if cls is Sized:
    return _check_methods(C, "__len__")
    return NotImplemented

    Sized类改写了__subclasshook__魔法方法,使其可以通过isinstance()判断对象是否含有__len__方法。同时,这个类必须基于元类abc.ABCMeta。我们也可以依葫芦画瓢,实现一个用来判断对象是否存在greet()函数的类,尽管并不严谨:

    import abc

    class A(metaclass=abc.ABCMeta):
    @classmethod
    def __subclasscheck__(cls, subclass):
    # 存在greet()返回True,不存在返回False
    if hasattr(subclass, "greet"):
    return True
    return False

    class B(object):
    def greet(self): # 定义了greet()方法
    pass

    class C(object): # 没有greet()方法
    pass

    class D(B): # 继承自B类,因此继承了greet()方法
    pass

    if __name__ == "__main__":
    b = B()
    c = C()
    d = D()

    print(isinstance(b, A)) # True
    print(isinstance(c, A)) # False
    print(isinstance(d, A)) # True

    注意,此时A类可以被实例化,因为它还不是抽象基类。


    实现ABC类
    C++中利用纯虚函数实现抽象基类,Python中写法如下:

    import abc

    class A(metaclass=abc.ABCMeta):
    # 利用装饰器修饰greet()
    @abc.abstractmethod
    def greet(self):
    print("hell world")

    if __name__ == "__main__":
    a = A()

    解释器如期抛错:

    TypeError: Can't instantiate abstract class A with abstract methods greet
    1
    这是因为A类现在就是一个抽象基类了,不可以被实例化,同时,它的子类还必须实现greet()方法,否则实例化子类时解释器也要报错:

    import abc

    class A(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def greet(self):
    pass

    class B(A):
    def greet(self):
    pass

    class C(A):
    pass

    if __name__ == "__main__":
    b = B() # 正常实例化
    c = C() # 解释器抛错

    # 输出:
    # C类中没有定义greet()方法导致的报错
    Traceback (most recent call last):
    File "xxx", line xxx, in <module>
    c = C()
    TypeError: Can't instantiate abstract class C with abstract methods greet

    其他基类
    abc模块中还有实现了其他抽象基类,可以用来判断类型或是继承方法,这里不做详述了:

    __all__ = ["Awaitable", "Coroutine",
    "AsyncIterable", "AsyncIterator", "AsyncGenerator",
    "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
    "Sized", "Container", "Callable", "Collection",
    "Set", "MutableSet",
    "Mapping", "MutableMapping",
    "MappingView", "KeysView", "ItemsView", "ValuesView",
    "Sequence", "MutableSequence",
    "ByteString",
    ]

    文件所在路径:…lib\_collocetions_abc.py


    总结
    abc模块中定义的类兼顾了继承抽象基类与鸭子类型的设计方式。你既可以通过继承Sized来拥有__len__方法,此时instance(对象, Sized)返回True;也可以在自己设计的类中实现__len__,instance(对象, Sized)仍然返回True。

    对抽象基类来说,需要用到装饰器 @abc.abstractmethod;对于鸭子类型来说,需要重写__subclasshook__魔法方法。

    以上都是基于元类abc.ABCMeta实现的。顺便提醒,注意import abc与from collections import abc各自的区别。
    ————————————————
    版权声明:本文为CSDN博主「有关心情」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_41359051/article/details/86764867

  • 相关阅读:
    51 nod 1279 扔盘子
    洛谷 P2911 [USACO08OCT]牛骨头Bovine Bones
    1759 加减表达式
    1750 加法表达式
    poj 1006 Biorhythms
    vijos 1198 最佳课题选择
    poj 1390 Blocks
    codevs 3324 新斯诺克
    codevs 2075 yh女朋友的危机
    对拍器
  • 原文地址:https://www.cnblogs.com/miaoweiye/p/12525426.html
Copyright © 2011-2022 走看看