我正在使用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
参数传递给它。请注意,尊重虚拟调度:子类Parser
会覆盖parse_foo
,(parser.*method)(chunk_offset)
(当method
为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
是对Parser
parse_foo
实施的引用,具体而言。即使我在Parser
的子类上调用它来覆盖它,所谓的仍然是原始实现。
在Python中,有没有办法获得尊重虚拟调度的“方法参考”?我可以制作使用self.parse_foo
的每个实例表,但这看起来很浪费。
答案 0 :(得分:1)
最接近方法引用的东西基本上只是一个包含方法名称的字符串。您可以使用该名称在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 :(得分:0)
这不是绑定方法,但性能开销非常小:
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)
根本没有方法列表。
答案 2 :(得分:0)
所以你会在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