扩展python-abc类的类的自动委托

2024-04-25 19:43:41 发布

您现在位置:Python中文网/ 问答频道 /正文

如果某个类扩展了abc类(抽象基类),那么除非我定义了所有的抽象方法,否则我不能实例化它。但在实现Decorator模式时,我通常只想定义一些抽象的方法,而其他的方法——只需要委托给装饰对象。怎么做?在

我想让下面的代码起作用:

from abc import ABCMeta, abstractmethod


class IElement(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def click(self):
        return

    @abstractmethod
    def hover(self):
        return

    # ... more IElement's abstractmethods...


class StandardElement(IElement):

    def click(self):
        return "click"

    def hover(self):
        return "hover"

    # ... more implemented IElement's methods...


class MyElement(IElement):
    def __init__(self, standard_element):
        self._standard_element = standard_element
        delegate(IElement, standard_element)

    def click(self):
        return "my click"


assert MyElement(StandardElement()).click() == 'my click'
assert MyElement(StandardElement()).hover() == 'click'

而不是

^{pr2}$

为此,我需要实现上面示例中的delegate方法。如何实施?还可以考虑其他一些方法来为扩展abc类的类提供自动委托。在

p.S。 请不要把继承(class MyElement(StandardElement))作为这里的解决方案。。。上面提供了一个示例代码。在我的实际例子中,MyElement和StandardElement是完全不同的东西。不过,我必须使MyElement与StandardElement兼容,因为有时有人应该使用MyElement而不是StandardElement。我真的需要在这里实现“有一个”关系,而不是“是一个”。在


Tags: 方法selfreturn定义defelementstandardclass
3条回答

出现此错误是因为类MyElement是抽象的(您没有重写方法hover)。抽象类是至少有一个抽象方法的类。若要解决此错误,应按以下方式添加此方法,例如:

class MyElement(IElement):
    def __init__(self, standard_element):
        self._standard_element = standard_element

    def click(self):
       return "my click"

    def hover(self):
        return self._standard_element.hover()

如果您想使用__getattr__委托许多方法,那么通过abc.abstractmethod似乎是不可能的,因为__getattr__会动态地搜索不需要直接运行__getattr__就无法检查的必要方法。在

因此,我建议您将所有方法指定为抽象的(通过@abstractmethod),这些方法肯定会在每个继承的类中被重写,并使用NotImplementedError实现委派方法。例如:

^{pr2}$

基于Blckknght的回答,我创建了一个python包,扩展了multiple delegates选项和自定义委托属性名的功能。在

看看这里https://github.com/monomonedula/abc-delegation

安装:pip install abc-delegation

默认情况下,没有自动完成所需委派的方法。委派并不是抽象类的一个显式的部分,所以这并不奇怪。但是,您可以编写自己的委托元类,为您添加缺少的方法:

def _make_delegator_method(name):
    def delegator(self, *args, **kwargs):
        return getattr(self._delegate, name)(*args, **kwargs)
    return delegator

class DelegatingMeta(ABCMeta):
    def __new__(meta, name, bases, dct):
        abstract_method_names = frozenset.union(*(base.__abstractmethods__
                                                  for base in bases))
        for name in abstract_method_names:
            if name not in dct:
                dct[name] = _make_delegator_method(name)

        return super(DelegatingMeta, meta).__new__(meta, name, bases, dct)

delegator方法是在一个单独的函数中生成的,因为我们需要一个命名空间,其中name在函数创建后不会发生变化。在Python3中,您可以通过给delegator一个带有默认值的关键字参数来完成__new__方法中的所有操作,但是在Python2中没有关键字参数,所以这对您不起作用。在

以下是如何使用它:

^{pr2}$

赋值给self._delegate设置元类创建的方法将使用的对象。如果你想的话,你可以用某种方法,但这似乎是最简单的方法。在

相关问题 更多 >

    热门问题