Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 获取虚拟方法引用的惯用方法是什么?_Python - Fatal编程技术网

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

Python 获取虚拟方法引用的惯用方法是什么?,python,Python,我正在使用Python为具有多种类型的小数据块的文件格式构建解析器。虽然我希望自己的解析足够了,但如果需要,我希望让客户机对解析器类进行子类化,以提供自定义行为 在C++中,我可以写出这样的东西: enum ChunkTypes { CHUNK_FOO, CHUNK_BAR, CHUNK_BAZ, }; class Parser { public: virtual void parse_foo(size_t offset); virtual void p

我正在使用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);
}
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)
由于这对不太懂C++的人来说可能不太熟悉:
parse\u method
,在本例中,是指向接受
size\t
参数的
Parser
方法的“指向成员的指针”
(parser.*method)(chunk\u offset)
将方法
method
应用于
parser
,并将
chunk\u offset
参数传递给它。请注意,这方面的虚拟分派:使用覆盖
parse_foo
(Parser.*method)(chunk_offset)
(当
method
parse_foo
时)的子类的实现将被调用

在Python中,我可以编写如下内容:

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);
}
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
是对
Parser
parse_foo
实现的引用。即使我在重写它的
解析器
的子类上调用它,所调用的仍然是原始实现


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

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

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\u type()
返回一个字符串,如
parse\u foo
,您可以编写:

getattr(parser, chunk_type())(offset)

根本没有方法列表。

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

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)

<>所以你可以在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)
调用不同的方法


不管怎么说,由于您要求的是一种惯用的方式,所以这并不是很符合python。但不是Pythonic的是整数(枚举或索引数组)上的分派器,而Python本机允许按名称分派:

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

in baz for instance id_1 with offset 5