如何在Python中将方法注册到类变量?

时间:2017-03-31 18:14:10

标签: python python-3.x xml-parsing

我正在编写一个程序,将XML文档集合转换为HTML。这些文档需要相似但不完全相同的转换,因此我希望将大部分细节抽象为通用BaseParser类,然后为封装特定于文档的转换的每个文档编写单个子类。我使用Python标准库xml.etree.ElementTree包进行基于事件的解析。

我希望能够编写这样的代码,其中函数的逻辑与它应该被调用时捆绑在一起。

class CustomParser(BaseParser):
    @on_tag('word', {'lang':'en'})
    def found_en_word(self, tag, attrs):
        # do something

为了使其工作,装饰器需要在类变量(或实例变量)中注册found_en_word函数,尽管对于每个实例来说都有多余的具有自己的副本,因此控制流可以在BaseParser类中分开。

我现在的解决方案,如下所示,是使用元类在类上创建callbacks字典。

class Meta(type):
    def __new__(cls, clsname, bases, dct):
        callbacks = {}
        for key, value in dct.items():
            if hasattr(value, '_on_tag'):
                callbacks[value._on_tag] = value
        ret = type(clsname, bases, dct)
        ret.callbacks = callbacks
        return ret

def on_tag(tag, attrs=None):
    def decorator(f):
        f._on_tag = (tag, attrs)
        return f
    return decorator

class BaseParser(metaclass=Meta):
    ...

不幸的是,它看起来并不像我希望的那样继承元类:似乎元类用于构造一个修改过的BaseParser类,CustomParser只是从中继承

可以在Python中使用或不使用元类来实现此构造吗?

1 个答案:

答案 0 :(得分:1)

您的元类未正确构建类。如the docs中所述,您实际上需要致电type.__new__(Meta, clsname, bases, dct)。只需调用type(clsname, bases, dct),您就构建了一个不是自定义元类实例的普通类。

修复后,您会遇到另一个问题,即您尝试使用_on_tag作为字典键,但_on_tag包含字典,并且字典不可清除。这与你的主要问题有些相似,但你必须找出一些方法来处理它(可能是让用户做@on_tag('word', ('lang', 'en'))而不是@on_tag('word', {'lang': 'en'}))。