首页 > 解决方案 > 获取虚拟方法引用的惯用方法是什么?

问题描述

我正在使用 Python 为具有多种类型的小块的文件格式构建解析器。虽然我希望我自己的解析就足够了,但我想让客户子类化解析器类以在需要时提供自定义行为。

在 C++ 中,我可以这样写:

enum ChunkTypes {
    CHUNK_FOO,
    CHUNK_BAR,
    CHUNK_BAZ,
};

class Parser {
public:
    virtual void parse_foo(size_t offset);
    virtual void parse_bar(size_t offset);
    virtual void parse_baz(size_t offset);
};

typedef void (Parser::*parse_method[])(size_t);
parse_method methods[] = {
    &Parser::parse_foo,
    &Parser::parse_bar,
    &Parser::parse_baz,
};

Parser& parser = get_parser();
while (has_more_chunks())
{
    parse_method method = methods[chunk_type()];
    size_t chunk_offset = get_chunk_offset();
    (parser.*method)(chunk_offset);
}

因为这对于不写很多 C++ 的人来说可能并不熟悉:parse_method在这个例子中,它是一个“指向成员”的指针,指向一个Parser接受size_t参数的方法。(parser.*method)(chunk_offset)将方法应用于method并将参数parser传递给它chunk_offset。请注意,这尊重虚拟调度:使用Parseroverridesparse_foo的子类(parser.*method)(chunk_offset)(当methodis时parse_foo),将调用子类的实现。

在 Python 中,我可以这样写:

class Parser:
    def parse_foo(self, offset):
        # ...

    def parse_bar(self, offset):
        # ...

    def parse_baz(self, offset):
        # ...

methods = [
    Parser.parse_foo,
    Parser.parse_bar,
    Parser.parse_baz]

parser = get_parser()
while has_more_chunks():
    method = methods[chunk_type()]
    offset = get_chunk_offset()
    method(parser, offset)

但是,具体来说,是对的实现的Parser.parse_foo引用。即使我在覆盖它的子类上调用它,所调用的仍然是原始实现。Parserparse_fooParser

有没有办法在 Python 中获得尊重虚拟调度的“方法参考”?我可以制作使用的每个实例表self.parse_foo,但这似乎很浪费。

标签: python

解决方案


最接近方法引用的东西基本上只是一个包含方法名称的字符串。您可以使用该名称来查找解析器对象上的方法getattr,然后调用它:

methods = [
    'parse_foo',
    'parse_bar',
    'parse_baz'
]

parser = get_parser()
while has_more_chunks():
    method_name = methods[chunk_type()]
    method = getattr(parser, method_name)  # get the method
    offset = get_chunk_offset()
    method(offset)  # call the bound method we retrieved earlier

或者,您可以使用调用相应方法的代理函数:

methods = [
    lambda parser, offset: parser.parse_foo(offset),
    lambda parser, offset: parser.parse_bar(offset),
    lambda parser, offset: parser.parse_baz(offset)
]

parser = get_parser()
while has_more_chunks():
    method = methods[chunk_type()]
    offset = get_chunk_offset()
    method(parser, offset)

推荐阅读