python中不同参数类型的方法重载

2022-08-10 14:11:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在用python编写一个预处理器,其中的一部分用于AST。

有一个render()方法负责将各种语句转换为源代码。

现在,我有这样一个(简称):

def render(self, s):
    """ Render a statement by type. """

    # code block (used in structures)
    if isinstance(s, S_Block):
        # delegate to private method that does the work
        return self._render_block(s)

    # empty statement
    if isinstance(s, S_Empty):
        return self._render_empty(s)

    # a function declaration
    if isinstance(s, S_Function):
        return self._render_function(s)

    # ...

如您所见,它很乏味,容易出错,而且代码很长(我有更多种类的语句)。

理想的解决方案是(在Java语法中):

String render(S_Block s)
{
    // render block
}

String render(S_Empty s)
{
    // render empty statement
}

String render(S_Function s)
{
    // render function statement
}

// ...

当然,python不能这样做,因为它有动态类型。当我寻找如何模仿方法重载时,所有的答案都只是说“你不想在python中那样做”。我想这在某些情况下是正确的,但是这里kwargs一点用处都没有。

如果上面所示的类型检查ifs,那么在python中,如果没有长达一公里的可怕序列,我将如何做到这一点?另外,最好是用“Python式”的方法?

注意:可以有多个“Renderer”实现,它们以不同的方式呈现语句。因此,我不能将呈现代码移到语句中,而只调用s.render()。必须在renderer类中完成。

(我找到了一些interesting "visitor" code,但我不确定它是否真的是我想要的东西)。


Tags: 方法selfstringreturnifcodefunction语句renderblockemptyisinstancestatement
3条回答
网友
1楼 ·

想要这个吗?

self.map = {
            S_Block : self._render_block,
            S_Empty : self._render_empty,
            S_Function: self._render_function
}
def render(self, s):
    return self.map[type(s)](s)

将对类对象的引用保留为字典中的键,并将其值设置为要调用的函数对象,这将使代码更短,更不容易出错。这里唯一可能出错的地方是字典的定义。或者你的内在功能。

网友
2楼 ·

您正在寻找的重载语法可以使用Guido van Rossum's multimethod decorator来实现。

这里是multimethod decorator的一个变体,它可以修饰类方法(原始的修饰纯函数)。我将变量命名为multidispatch,以消除与原始变量的歧义:

import functools

def multidispatch(*types):
    def register(function):
        name = function.__name__
        mm = multidispatch.registry.get(name)
        if mm is None:
            @functools.wraps(function)
            def wrapper(self, *args):
                types = tuple(arg.__class__ for arg in args) 
                function = wrapper.typemap.get(types)
                if function is None:
                    raise TypeError("no match")
                return function(self, *args)
            wrapper.typemap = {}
            mm = multidispatch.registry[name] = wrapper
        if types in mm.typemap:
            raise TypeError("duplicate registration")
        mm.typemap[types] = function
        return mm
    return register
multidispatch.registry = {}

它可以这样使用:

class Foo(object):
    @multidispatch(str)
    def render(self, s):
        print('string: {}'.format(s))
    @multidispatch(float)
    def render(self, s):
        print('float: {}'.format(s))
    @multidispatch(float, int)
    def render(self, s, t):
        print('float, int: {}, {}'.format(s, t))

foo = Foo()
foo.render('text')
# string: text
foo.render(1.234)
# float: 1.234
foo.render(1.234, 2)
# float, int: 1.234, 2

上面的演示代码显示了如何根据参数的类型重载Foo.render方法。

这段代码搜索完全匹配的类型,而不是检查isinstance关系。可以修改它来处理这个问题(代价是查找O(n)而不是O(1)),但是因为听起来你不需要这样做,所以我将把代码放在这个更简单的格式中。

网友
3楼 ·

如果您使用的是Python3.4(或者愿意为Python2.6+安装backport),那么可以使用^{}来完成以下操作:

from functools import singledispatch

class S_Block(object): pass
class S_Empty(object): pass
class S_Function(object): pass


class Test(object):
    def __init__(self):
        self.render = singledispatch(self.render)
        self.render.register(S_Block, self._render_block)
        self.render.register(S_Empty, self._render_empty)
        self.render.register(S_Function, self._render_function)

    def render(self, s):
        raise TypeError("This type isn't supported: {}".format(type(s)))

    def _render_block(self, s):
        print("render block")

    def _render_empty(self, s):
        print("render empty")

    def _render_function(self, s):
        print("render function")


if __name__ == "__main__":
    t = Test()
    b = S_Block()
    f = S_Function()
    e = S_Empty()
    t.render(b)
    t.render(f)
    t.render(e)

输出:

render block
render function
render empty

*基于this gist.的代码