0
点赞
收藏
分享

微信扫一扫

python __subclasshook__()方法

虽然你可以做任何你想做的事情,通常在__subclasshook__()方法中应该遵循通用 模式。标准的过程是检查在给定类的 MRO 中的某处定义的方法的集合是否可用,如下所示:

      from abc import ABCMeta, abstractmethod

      class Pushable(metaclass=ABCMeta):

          @abstractmethod

def push(self, x): """ 推入任意参数

"""

@classmethod

def __subclasshook__(cls, C):

if cls is Pushable:

if any("push" in B.__dict__ for B in C.__mro__):

                      return True

              return NotImplemented

通过以这种方式定义的__subclasshook__()方法,你现在可以确认隐式实现接口的 实例也被视为接口的实例:

   >>> class SomethingWithPush:

   ...     def push(self, x):

   ...         pass

   ...

   >>> isinstance(SomethingWithPush(), Pushable)

   True

不幸的是,这种验证类型兼容性和实现完整性的方法没有考虑类方法的签名。因此, 如果预期参数的数量与实现的不同,仍将被认为是兼容的。在大多数情况下,这不是一个 问题,但是如果你需要这样细粒度的控制接口,zope.interface 包可以做到。如前所 述,__subclasshook__()方法不会限制你在 isinstance()函数的逻辑中添加更多复 杂性以实现类似的控制级别。

补充抽象基类的另外两个功能是函数注解和类型提示。函数注解是在第 2 章中简要描述的 语法元素。它允许你用任意表达式注解函数及其参数。如第 2 章中所述,在类级别之下,这只是 一个不提供任何语法意义的功能桩。在使用此功能的标准库中没有实用程序来强制执行任何行 为。无论如何,你可以使用它作为一个方便且轻量级的方式通知开发人员期望的参数接口。例如, IRectangle 接口之前是使用 zope.interface,现在考虑使用抽象基类重写它,如下所示:

      from abc import (

          ABCMeta,

          abstractmethod,

          abstractproperty

      )

      class IRectangle(metaclass=ABCMeta):

          @abstractproperty

          def width(self):

return

          @abstractproperty

          def height(self):

return

          @abstractmethod

          def area(self):

""" 返回矩形的面积 """

          @abstractmethod

          def perimeter(self):

""" 返回矩形的周长 """

@classmethod

def __subclasshook__(cls, C):

if cls is IRectangle:

if all([

any("area" in B.__dict__ for B in C.__mro__), any("perimeter" in B.__dict__ for B in C.__mro__), any("width" in B.__dict__ for B in C.__mro__), any("height" in B.__dict__ for B in C.__mro__),

                  ]):

                      return True

return NotImplemented

如果你有一个只在矩形上工作的函数,例如说 draw_rectangle(),你可以使用如

下所示的方式对期望参数的接口进行注解:

def draw_rectangle(rectangle: IRectange):

...

这只给开发人员增加了一些有关预期的信息。甚至这是通过非正式约定,因为,我们 知道,纯注解不包含句法意义。但是,它们在运行时是可访问的,因此我们可以做更多的 事情。这里是一个通用装饰器的示例实现,它能够验证函数注解的接口,如果它使用抽象 基类提供的话,如下所示:

def ensure_interface(function):

signature = inspect.signature(function) parameters = signature.parameters

          @wraps(function)

          def wrapped(*args, **kwargs):

              bound = signature.bind(*args, **kwargs)

              for name, value in bound.arguments.items():

                  annotation = parameters[name].annotation

                  if not isinstance(annotation, ABCMeta):

                      continue

                  if not isinstance(value, annotation):

                      raise TypeError(

                          "{} does not implement {} interface"

                          "".format(value, annotation)

                      )

              function(*args, **kwargs)

          return wrapped

一旦完成,我们可以创建一些具体的类,它隐式实现 IRectangle 接口(不继承 IRectangle),并更新 draw_rectangle()函数的实现,看看整个解决方案是如何工 作的,如下所示:

class ImplicitRectangle:

def __init__(self, width, height):

self._width = width self._height = height

          @property

          def width(self):

return self._width

          @property

          def height(self):

return self._height def area(self):

              return self.width * self.height

          def perimeter(self):

              return self.width * 2 + self.height * 2

@ensure_interface

def draw_rectangle(rectangle: IRectangle):

          print(

              "{} x {} rectangle drawing"

              "".format(rectangle.width, rectangle.height)

)

举报

相关推荐

0 条评论