Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/327.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.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_Oop_Design Patterns - Fatal编程技术网

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

python中不同参数类型的方法重载,python,oop,design-patterns,Python,Oop,Design Patterns,我正在用python编写一个预处理器,其中一部分与AST一起工作 有一个render()方法负责将各种语句转换为源代码 现在,我有这样的(缩短): 正如您所看到的,它很乏味,容易出错,而且代码很长(我有更多种类的语句) 理想的解决方案是(用Java语法): 当然,python不能这样做,因为它有动态类型。当我搜索如何模拟方法重载时,所有的答案都是“你不想在python中这样做”。我想这在某些情况下是正确的,但是这里的kwargs实际上一点用处都没有 如果没有可怕的公里长的if类型检查ifs序列,

我正在用python编写一个预处理器,其中一部分与AST一起工作

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

现在,我有这样的(缩短):

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

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

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

如果没有可怕的公里长的if类型检查ifs序列,我将如何在python中实现这一点,如上图所示?还有,最好是一种“蟒蛇式”的方式

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


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

有什么东西喜欢这项工作吗

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)

在字典中保留对类对象的引用作为键,并将其值作为要调用的函数对象,这将使代码更短,更不容易出错。这里唯一可能发生错误的地方是字典的定义。当然也可以是您的一个内部函数。

您正在寻找的重载语法可以通过使用来实现

这里是multimemethoddecorator的一个变体,它可以修饰类方法(原始的修饰普通函数)。我将变体命名为
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)为代价),但因为听起来您似乎并不需要它,所以我将以这种更简单的形式保留代码。

如果您使用的是Python 3.4(或者愿意安装for Python 2.6+),那么您可以使用它*:

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

*基于

的代码,在@unutbu的答案中添加一些性能度量:

@multimethod(float)
def foo(bar: float) -> int:
    return 'float: {}'.format(bar)

def foo_simple(bar):
    return 'string: {}'.format(bar)


返回:

> multimethod: 16.846999883651733
> standard:     4.509999990463257

一种替代实现,使用中定义的装饰器:

从functools导入singledispatch
类别S_未知:通过
等级S_块:通过
类S_空:通过
类S_函数:pass
类S_特殊块(S_块):通过
@单发
def渲染,**kwargs:
打印('呈现未知类型')
@渲染寄存器(S_块)
定义(单位:**kwargs):
打印('呈现S_块')
@render.register(S_为空)
定义(单位:**kwargs):
打印('呈现S_为空')
@render.register(S_函数)
定义(单位:**kwargs):
打印(“呈现S_函数”)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
对于t in[S_未知、S_块、S_空、S_函数、S_特殊块]:
打印(f'传递一个{t.\uuuuuu name\uuuuuuu}')
render(t())
这个输出

传递S_未知
呈现未知类型
通过S_街区
渲染S_块
通过一个空的S_
将S_渲染为空
传递S_函数
呈现S_函数
通过S_特殊块
渲染S_块
我更喜欢这个版本,因为它与使用
isinstance()
的实现具有相同的行为:当您传递S_SpecialBlock时,它会将其传递给接受S_块的渲染器

可利用性 正如dano在中提到的,这在Python3.4+中起作用,Python2.6+中有一个新的版本

如果您有Python 3.7+,
register()
属性支持使用类型注释:

@render.register
定义(s:s_块,**kwargs):
打印('呈现S_块')
注 我看到的一个问题是,必须将
s
作为位置参数传递,这意味着不能进行
渲染(s=s_Block())

由于
single\u dispatch
使用第一个参数的类型来确定要调用哪个版本的
render()
,这将导致类型错误-“render至少需要一个位置参数”(cf)

实际上,我认为如果只有一个关键字参数,应该可以使用关键字参数。。。如果你真的需要它,那么你可以做类似的事情,用不同的包装创建一个定制的装饰器。
这也是Python的一个很好的特性。

isinstance
为子类以及类本身返回true。你不能用映射来匹配语义。@Joowani Great minds之类的东西。唯一需要考虑的是,在我的实现中,字典是静态的,将在类的所有实例中使用。每次使用函数时都必须实例化字典。@roippi我从未使用过isinstance?编辑:哦,我现在明白你的意思了。我没有考虑过这一点,但这似乎不是op所需要的。有趣的方法,而且@roippi在这里真的不是问题,我总是匹配层次结构中“最深的孩子”,例如s_If,而不是s_BranchingState(如果我有)。如果有更好的东西出现,我会等一段时间,但这看起来确实是一个很好的技术(不完美,但至少不那么冗长)。我接受了这一点,因为它最适合我的项目,而且最容易理解。但是,一般来说,其他答案可能更好。请查看以下答案。从技术上讲,这是一个复制品,但它收到了一些不同的答案,所以我犹豫是否标记它。
@multimethod(float)
def foo(bar: float) -> int:
    return 'float: {}'.format(bar)

def foo_simple(bar):
    return 'string: {}'.format(bar)
import time

string_type = "test"
iterations = 10000000

start_time1 = time.time()
for i in range(iterations):
    foo(string_type)
end_time1 = time.time() - start_time1


start_time2 = time.time()
for i in range(iterations):
    foo_simple(string_type)
end_time2 = time.time() - start_time2

print("multimethod: " + str(end_time1))
print("standard: " + str(end_time2))
> multimethod: 16.846999883651733
> standard:     4.509999990463257