扩展python abc类

时间:2016-12-24 01:47:12

标签: python abc

如果某个类扩展了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'

而不是

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

    def click(self):
        return "my click"

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

    # ... more manually delegated IElement's methods to self._standard_element object, aggregated in initialiser...


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

为此,我需要从上面的例子中实现delegate方法。怎么实现呢?还可以考虑为扩展abc类的类提供自动委派的一些其他方法。

P.S。 请不要在此建议继承(class MyElement(StandardElement))作为解决方案......上面提供的代码只是一个示例。在我的实际案例中,与StandardElement相比,MyElement是完全不同的。尽管如此,我还是要让MyElement与StandardElement兼容,因为有时人们应该使用MyElement而不是StandardElement。我真的需要实施"有一个"这里的关系,而不是"是"。

3 个答案:

答案 0 :(得分:3)

默认情况下,没有自动方法可以执行所需的委派。委托并不是抽象类的明确部分,所以这不应该是一个惊喜。但是,您可以编写自己的委托元类,为您添加缺少的方法:

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)

委托方法是在一个单独的函数中生成的,因为我们需要一个名称空间,其中name在创建函数后不会改变。在Python 3中,您可以在__new__方法中执行所有操作,方法是为delegator提供一个仅带有默认值的关键字参数,但Python 2中没有关键字唯一参数,因此不会为你工作。

以下是您使用它的方式:

class MyElement(IElement):
    __metaclass__ = DelegatingMeta

    def __init__(self, standard_element):
        self._delegate = standard_element

    def click(self):
        return "my click"

分配给self._delegate设置元类创建的方法将使用的对象。如果你愿意,你可以做出某种方法,但这似乎是最简单的方法。

答案 1 :(得分:0)

您收到此错误,因为类<!DOCTYPE html> <html> <title>Lets See</title> <link href="https://fonts.googleapis.com/css?family=Josefin+Sans" rel="stylesheet"> <style> h1 { font-family: cursive; } button { font-family: josefin sans; font-size: 30px; border-radius: 10px; background-color: red; color: white; } input { font-family: josefin sans; font-size: 25px; } </style> <body> <input id="txtJob" type="text" name="txtJob"> & <input type="text" id="twoPpl" name="twoPpl"> <br><br> <button onclick="myFunction()">Test</button> <h1 id="people" name="people" style="float: right;"></h1><br><br><h1 style="float: right;">%</h1><h1 style="float: right;" id="demo"></h1> <script> function myFunction() { var jobValue = document.getElementById('txtJob').value var twoPeople = document.getElementById('twoPpl').value var skip = "&nbsp;& "; var newVar = jobValue + skip; var finVar = newVar + twoPeople; var x = document.getElementById("demo") var y = document.getElementById("name"); x.innerHTML = Math.floor((Math.random() * 100) + 1); document.getElementById('people').innerHTML = finVar; } </script> </body> </html> 是抽象的(您没有覆盖方法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

答案 2 :(得分:0)

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

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

查看

安装:pip install abc-delegation