第7章 抽象基类

07 抽象基类

7.1 使用抽象基类
  • 抽象基类的基本目的就是提供有点形式化的方法,来测试一个对象是否符合特定规范。
    • isinstance({}, dict)
    • hasattr({}, "__getitem__")
7.2 声明虚拟子类
  • abc模块提供的第一个内容是名为ABCMeta的元类 。所有抽象基类可以任意地声明它是任意具体类的父类(不是派生类),包括在标准库的具体类。ABCMeta的实例通过使用register方法提供了对声明的实现(这些使用ABCMeta作为他们元类的类都是类本身)

  • 这并没有对dict类本身进行任何修改,这样做就是的dict对象也被标识为AbstractDict的实例,并且现在的dict自身也被标识为一个AbstractDict的子类

    import abc
    
    class AbstractDitct(metaclass=abc.ABCMeta):
        def foo(self):
            return "123"
    
    print(AbstractDitct.register(dict))  #  <class "dict">
    print(isinstance({}, AbstractDict))  # True
    print(issubclass(dict, AbstractDict))  # True
    
    import abc
    
    class MySequence(metaclass=abc.ABCMeta):
        pass
    
    print(MySequence.register(list))
    print(MySequence.register(tuple))
    
    print(type(MySequence))  # <class 'abc.ABCMeta'>
    
    class CustomListClass():
        pass
    
    print(MySequence.register(CustomListClass))
    print(issubclass(CustomListClass, MySequence))
    
  • 使用register 作为装饰器

    import abc
    
    class MySequence(metaclass=abc.ABCMeta):
        pass
    
    print(MySequence.register(list))
    print(MySequence.register(tuple))
    
    print(type(MySequence))  # <class 'abc.ABCMeta'>
    
    @MySequence.register
    class CustomListClass():
        pass
    
    print(issubclass(CustomListClass, MySequence))
    
  • 鸭子类型 范例最基本的问题是,一个对象是否有某个属性或方法(比如叫起来像只鸭子),而不是对象是否是这个或那个类的子类

    import abc
    
    
    class AbstractDuck(metaclass=abc.ABCMeta):
        @classmethod
        def __subclasshook__(cls, other):  # 优先register
            quack = getattr(other, "quack", None)
            # callable() 是否可调用
            return callable(quack)
    
    
    class Duck():
        def quack(self):
            pass
    
    class NotDuck():
        quack = "foo"
    
    print(issubclass(Duck, AbstractDuck)) 
    print(issubclass(NotDuck, AbstractDuck))
    
    import abc
    
    
    class AbstractDuck(metaclass=abc.ABCMeta):
        @classmethod
        def __subclasshook__(cls, other):
            quack = getattr(other, "quack", None)
            # callable() 是否可调用
            # return callable(quack)
            if callable(quack):
                return True
            return NotImplementedError
    
    
    class Duck():
        def quack(self):
            pass
    
    class NotDuck():
        quack = "foo"
    
    print(issubclass(Duck, AbstractDuck))
    print(AbstractDuck.register(NotDuck))
    print(issubclass(NotDuck, AbstractDuck))  # AssertionError
    
7.3 声明协议
  • 使用NotImplementedError

    • 基类Task 不能提供任务主体,这需要子类完成;未能重写_run的子类都会引发NotImplementedError
    from datetime import datetime
    
    class Task:
    
        def __init__(self):
            self.runs = []
    
        def run(self):
            start = datetime.now()
            result = self._run()
            end = datetime.now()
            self.runs.append({
                "start":start,
                "end": end,
                "result": result
            })
        def _run(self):
            raise NotImplementedError("Task subclasses must define a _run method.")
    
    t = Task()
    t.run()
    
  • 使用元类

    • Task类本身可以被实例化,但是不在声明_run方法,因此使用run方法会抛出AttributeError
    • Task子类被创建时 元类会运行__new__方法,解析器将不在允许创建没有_run的子类
    from datetime import datetime, timezone
    
    
    class TaskMeta(type):
        def __new__(cls, name, bases, attrs):
            if attrs.pop("abstract", False):
                print(123)
                return super(TaskMeta, cls).__new__(cls, name, bases, attrs)
    
            new_class = super(TaskMeta, cls).__new__(cls, name, bases, attrs)
            if not hasattr(new_class, "_run") or not callable(new_class._run):
                raise TypeError("Task subclasses must define a _run method.")
            return new_class
    
    class Task(metaclass=TaskMeta):
        abstract = True
    
        def __init__(self):
            self.runs = []
    
        def run(self):
            start = datetime.now(tz=timezone.utc)
            result = self._run()
            end = datetime.now(tz=timezone.utc)
            self.runs.append({
                "start": start,
                "end": end,
                "result": result
            })
            return result
    
    class TaskSubclass(Task): # TypeError: Task subclasses must define a _run method.
        pass
    
    t = Task()
    
    • Task类本身不能实例化;允许创建子类,但不允许实例化
    • @abc.abstractmethod 指定了一个必须被所有子类重写的特定方法
    import abc
    from datetime import datetime, timezone
    
    class Task(metaclass=abc.ABCMeta):
        def __init__(self):
            self.runs = []
    
        def run(self):
            start = datetime.now(tz=timezone.utc)
            result = self._run()
            end = datetime.now(tz=timezone.utc)
            self.runs.append({
                "start": start,
                "end": end,
                "result": result
            })
            return result
    
        @abc.abstractmethod # 
        def _run(self):
            pass
        
        
    class Subtask(Task):
    	# 子类如果包含 _run 方法则可以实例化 
        def _run(self):
            return 2
    
    st = Subtask()
    st.run()
    print(st.runs)
    # t = Task()
    
    • 抽象属性:@property
    import abc
    
    class AbstractClass(metaclass=abc.ABCMeta):
        @property
        @abc.abstractmethod
        def foo(self):
            pass
    
    class VaildChild(AbstractClass):
        @property  # 会把foo 当做属性而不是方法
        def foo(self):
            return "bar"
    
    vc = VaildChild()
    print(vc.foo)
    
    • 抽象类或静态方法
    import abc
    class AbstractClass(metaclass=abc.ABCMeta):
        @classmethod
        @abc.abstractmethod
        def foo(cls):
            return 42
    
    class ValidChild(AbstractClass):
        @classmethod
        def foo(cls):
            return "bar"
    
    print(ValidChild.foo())
    
    vc = ValidChild()
    print(vc.foo())
    
7.4 内置抽象基类
  • 最常用的抽象基类适用于集合的抽象基类,collections.abc

  • 抽象基类能被划分为两种基本类别

    • 一种需要和检查单一方法(比如 IterableCallable)
    • 一种作为普通内置python类型的替身
  • 抽象基类

    • Callable(__call__)
    • Container(__contains__)
    • Hashable(__hash__)
    • Iterable(__iter__)
    • Sized(__len__)
    • Iterator(__iter__)继承Iterable,提供__iter__的实现(返回自身并可以被重写),并添加抽象方法__next__

    任何包含相应方法的类都会自动地被当作相关抽象基类的子类

    from collections.abc import Sized
    
    class Foo:
        def __len__(self):
            return 42
    
    print(issubclass(Foo, Sized))
    
    # 类可以直接作为抽象基类的子类
    class Bar(Sized):
        pass
    
    b = Bar()
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

壹如年少遲夏歸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值