首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >获取虚方法引用的惯用方法是什么?

获取虚方法引用的惯用方法是什么?
EN

Stack Overflow用户
提问于 2018-06-17 04:35:00
回答 3查看 50关注 0票数 0

我正在使用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是一个“指向成员的指针”,指向一个接受size_t参数的Parser方法。(parser.*method)(chunk_offset)method方法应用于parser,并将其传递给chunk_offset参数。请注意,这将遵守虚拟分派:使用覆盖parse_foo(parser.*method)(chunk_offset)Parser子类(当methodparse_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_foo的实现,即使我在覆盖它的Parser的子类上调用它,调用的仍然是原始的实现。

在Python中,有没有一种方法可以获得尊重虚拟分派的“方法引用”?我可以创建使用self.parse_foo的每个实例的表,但这似乎很浪费。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-06-17 04:52:48

最接近方法引用的东西基本上就是一个包含方法名称的字符串。您可以使用该名称通过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)
票数 1
EN

Stack Overflow用户

发布于 2018-06-17 04:42:23

这不是一个绑定方法,但性能开销非常小:

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

如果你不想绑定到特定的参数签名,你可以这样写:

lambda parser, *args, **kwargs: parser.parse_foo(*args, **kwargs)

更好的是,如果chunk_type()返回一个字符串,比如parse_foo,你可以这样写:

getattr(parser, chunk_type())(offset)

根本没有方法列表。

票数 0
EN

Stack Overflow用户

发布于 2018-06-17 05:27:08

因此,您将使用指向C++中成员的指针。在Python中,最接近的是拥有一个实例属性,该属性将接收绑定的方法。下面是一些演示它的代码:

class A:
    def __init__(self, name):
        self.name = name  # instance identification
    # the methods
    def parse_foo(self, offset):
        print("in foo for instance {} with offset {}".format(
            self.name, offset))
    def parse_bar(self, offset):
        print("in bar for instance {} with offset {}".format(
            self.name, offset))
    def parse_baz(self, offset):
        print("in baz for instance {} with offset {}".format(
            self.name, offset))
    # an array of unbound methods
    methods = [ parse_foo, parse_bar, parse_baz ]

a = A("id_1")         # creates an A instance
a.method = getattr(a, A.methods[1].__name__)   # have a.method be a bound parse_bar method
a.method(10)

按预期打印:

in bar for instance id_1 with offset 10

这允许A的不同实例让a.method(offset)调用不同的方法。

无论如何,因为您要求的是一种惯用的方式,所以这不是很Pythonic式的。但Pythonic不是整数(枚举或索引数组)上的分派器,而Python本身就允许按名称进行分派:

a.method = getattr(a, "parse_baz")
a.method(5)

输出

in baz for instance id_1 with offset 5
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50891553

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档