【问题标题】:Creating dynamic ABC class based on user defined class基于用户定义类创建动态ABC类
【发布时间】:2013-01-08 15:39:09
【问题描述】:

我正在编写一个插件框架,我希望能够编写一个装饰器interface,它将用户类转换为 ABC 类并将所有方法替换为抽象方法。我无法让它工作,我认为问题与错误的 mro 有关,但我可能是错的。

我基本上需要写:

@interface
class X:
    def test(self):
        pass

x = X() # should fail, because test will be abstract method.

用抽象版本替换方法很简单(您必须遍历 func 并用 abc.abstractmethod(func) 替换它们),但我在创建动态类型时遇到了问题,这将是一个 ABCmeta 元类。

现在我有类似的东西:

from abc import ABCMeta

class Interface(metaclass=ABCMeta):
    pass

def interface(cls):
    newcls = type(cls.__name__, (Interface, cls), {})
    # substitute all methods with abstract ones
    for name, func in inspect.getmembers(newcls, predicate=inspect.isfunction):
        setattr(newcls, name, abstractmethod(func))
    return newcls

但它不起作用 - 我可以在没有错误的情况下初始化 X 类。

根据 Python 中 ABC 的标准用法,我们可以这样写:

class X(metaclass=ABCMeta):
    @abstractmethod
    def test(self):
        pass

x = X() # it will fail

如何在 Python3 中创建动态类型,它的行为会像元类 ABCmeta 一样,并将所有函数替换为抽象函数?

【问题讨论】:

  • interface 函数应该返回一些东西吗? (我收到x = X() # should fail, because test will be abstract method.)。
  • 当然,抱歉打错了,interface 应该是装饰器返回新类:) - 我已经解决了问题
  • documentation 确实声明了Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported,这就解释了为什么它不起作用。我正在研究是否还有办法做到这一点。
  • 但理论上我并不是要添加或修改方法的抽象状态(...)-我正在尝试基于该旧类型创建新类型-如果您在代码中执行此操作- 你创建像class Y(X): @abstractmethod def test2(self): pass 这样的东西,它可以工作,所以我应该能够使用动态代码来做到这一点(甚至将函数 dict 复制到新类型上)
  • 相反,您确实在创建每个函数后将其修改为抽象。您的代码首先创建 newcls 类,然后 动态应用 abstractmethod 装饰器。 (因此我尝试颠倒顺序,首先将abstractmethod 应用于cls 的方法,然后创建newcls,但它也不起作用。

标签: python python-3.x abstract-class introspection


【解决方案1】:

诀窍不是使用setattr 来重置每个属性,而是改为将这些修改后的属性作为字典传递给type 函数:

import inspect
from abc import ABCMeta, abstractmethod

class Interface(metaclass=ABCMeta):
    pass

def interface(cls):
    attrs = {n: abstractmethod(f)
             for n, f in inspect.getmembers(cls, predicate=inspect.isfunction)}

    return type(cls.__name__, (Interface, cls), attrs)

@interface
class X(metaclass=ABCMeta):
    def test(self):
        pass

x = X()
# does fail:
# Traceback (most recent call last):
#   File "test.py", line 19, in <module>
#     x = X() # should fail, because test will be abstract method.
# TypeError: Can't instantiate abstract class X with abstract methods test

【讨论】:

  • 我现在已经创建了相同的代码!谢谢你的帮助! :)
  • @danilo2:我将其设置为继承Interfacecls,因为您还需要包含非函数成员。如果在某些情况下这会成为问题并且它不应该继承 cls,您可以在字典理解中使用 f: abstractmethod(f) if inspect.isfunction(f) else f
猜你喜欢
  • 1970-01-01
  • 2020-12-23
  • 2011-12-30
  • 1970-01-01
  • 2016-06-17
  • 1970-01-01
  • 1970-01-01
  • 2013-02-21
  • 2014-08-22
相关资源
最近更新 更多