关于重载:在Python中”重载”函数的最佳方法?

Best way to “overload” function in python?

本问题已经有最佳答案,请猛点这里访问。

我正在尝试在python中执行类似的操作:

1
2
3
4
5
def foo(x, y):
    # do something at position (x, y)

def foo(pos):
    foo(pos.x, pos.y)

所以我想根据我提供的参数数量调用不同版本的foo。这当然不行,因为我重新定义了foo

实现这一目标最优雅的方式是什么?我不喜欢使用命名参数。


可以使用默认参数,但如果希望在Python中使用重载,最好的方法是:

1
2
3
4
5
6
7
from pytyp.spec.dispatch import overload

@overload
def foo(x,y):

@foo.overload
def foo(x, y,z):

pytyp是一个python包(可以从https://pypi.python.org/pypi/pytyp下载)。它包括模块pytyp.spec.dispatch,其中包含诸如重载之类的装饰器。如果它在方法上,则将为所有重载方法调用此方法。


通常,您要么定义两个不同的函数,要么执行如下操作:

1
2
3
4
def foo(x, y = None):
    if y is None:
        x, y = x.x, x.y
    # do something at position (x, y)

定义两个不同函数的选项似乎很难,如果你习惯了有重载的语言,但是如果你习惯了像python或c这样的语言,那么这就是你要做的。上面的代码在Python中的主要问题是,编写第一个参数的文档有点困难,这两种情况下并不意味着相同。

另一种选择是只定义接受POS的foo版本,同时为用户提供一种类型:

1
Pos = collections.namedtuple('Pos', 'x y')

那么,任何一个写过foo(x,y)的人都可以写foo(Pos(x,y))。当然,由于必须创建一个对象,所以性能成本很低。


您可以做很多事情,比如命名的默认参数。

但是,看起来您想要的是"多分派",并且有许多实现可以帮助您完成这类工作。下面是一个类似的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> from multipledispatch import dispatch

>>> @dispatch(int, int)
... def add(x, y):
...     return x + y

>>> @dispatch(Position)
... def add(pos):
...     return"%s + %s" % (pos.x, pos.y)

>>> add(1, 2)
3

>>> pos = Position(3, 4)
>>> add(pos)
7

虽然@functools.singledispatch将要用到python3.4,但我认为这对您的示例不起作用,因为在一个例子中,您有多个参数。


您试图实现的设计看起来很糟糕,可以考虑使用不同的函数名:

1
2
3
4
5
def do_something_by_coordinates(x, y):
    pass

def do_something_by_position(pos):
    do_something_by_coordinates(pos.x, pos.y)

或者,如果您确实需要:了解Python中的Kwargs


一种方法是设置默认值:

1
2
def foo(x, y=None):    
    # now for example, you can call foo(1) and foo(1,2)

foo中,您可以检查y == None是否存在,并且对于这两种情况具有不同的逻辑。

请注意,如果分离函数,您可以有一个更好的设计,不要试图拥有同时接受坐标和位置的相同函数。