【问题标题】:abstractmethod decorator to all methods in class类中所有方法的抽象方法装饰器
【发布时间】:2021-09-17 13:53:43
【问题描述】:

我试图让我意识到我不需要一直使用所有方法装饰器@abstractmethod

首先,我实现了类装饰器,但这不需要任何结果:

from abc import ABC, abstractmethod


def interface(cls):
   for attr in cls.__dict__:
      if callable(getattr(cls, attr)):
         setattr(cls, attr, abstractmethod(getattr(cls, attr)))
    
   return cls


@interface
class A(ABC):
   def foo(self):
      pass

   def bar(self):
      pass


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

b = B()

其次,我尝试在课堂上的__init__ 方法中制作它,它也没有给出任何结果:

class Interface(ABC):
    def __init__(self):
        for attr in self.__dict__:
            attr_obj = getattr(self, attr)

            if callable(attr_obj):
                setattr(self, attr, abstractmethod(attr_obj))

class A(Interface):
   def __init__(self):
      super().__init__()

   def foo(self):
      pass

   def bar(self):
      pass


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


b = B()

我怎样才能实现类中的所有方法都用@abstractmethod 装饰的方式?

【问题讨论】:

  • 我相信 ABC 的实现依赖于元类魔法,需要在元类实例化之前修饰方法(即在类主体中......)。当您的装饰器拥有实例化的类对象时,为时已晚。您可以尝试使用适当修饰的方法创建类的副本,然后再将它们与类命名空间一起传递给元类构造函数。不过似乎很冒险。我只会咬紧牙关。
  • 您的第二种方法不起作用,self.__dict__实例命名空间,它不包含方法。再说一次,现在ABCMeta 做任何事情都为时已晚
  • 为什么不使用mypy 的静态类型检查和Protocol 的结构子类型?
  • @juanpa.arrivillaga 您可以通过覆盖元类的__new__ 方法并在调用原始元类的__new__ 方法之前更改类成员来规避元类的魔法。
  • 我已经用更好的方法更新了我的答案,以避免使用abstractmethod 装饰子类的方法。

标签: python python-3.x python-decorators abc


【解决方案1】:

就像@juanpa.arrivillaga 提到的那样,在类被实例化后装饰抽象类为时已晚,因为ABC 用它的元类ABCMeta 改变了它的类成员。

相反,您可以将ABCMeta 子类化(如下例中的Interface)并在调用超类的构造函数之前修改类构造函数的classdict 参数。

由于子类默认实现抽象方法,典型情况下不继续声明抽象方法,所以应避免用abstractmethod装饰子类的方法,但由于子类会继承父类的元类,您必须明确地使 Interface 的构造函数返回 ABCMeta 的对象而不是 Interface 本身,这样自定义行为就不会被继承:

from abc import ABCMeta, abstractmethod

class Interface(ABCMeta):
    def __new__(metacls, name, bases, classdict):
        for attr, value in classdict.items():
            if callable(value):
                classdict[attr] = abstractmethod(value)
        return super().__new__(ABCMeta, name, bases, classdict)

这样:

class A(metaclass=Interface):
    def foo(self):
        pass

    def bar(self):
        pass

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

b = B()

会产生:

TypeError: Can't instantiate abstract class B with abstract method bar

同时:

class A(metaclass=Interface):
    def foo(self):
        pass

    def bar(self):
        pass

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

    def bar(self):
        pass

可以正常运行。

【讨论】:

    猜你喜欢
    • 2013-10-20
    • 2016-10-28
    • 2018-12-13
    • 2019-11-12
    • 1970-01-01
    • 2014-01-14
    • 2020-01-11
    • 2021-10-16
    • 2020-09-03
    相关资源
    最近更新 更多