Python函数重载

新手上路,请多包涵

我知道 Python 不支持方法重载,但我遇到了一个问题,我似乎无法以一种好的 Python 方式解决。

我正在制作一个角色需要射击各种子弹的游戏,但是我如何编写不同的函数来创建这些子弹?例如,假设我有一个函数可以创建一个以给定速度从 A 点行进到 B 点的子弹。我会写一个这样的函数:

 def add_bullet(sprite, start, headto, speed):
    # Code ...

但我想编写其他函数来创建项目符号,例如:

 def add_bullet(sprite, start, direction, speed):
def add_bullet(sprite, start, headto, spead, acceleration):
def add_bullet(sprite, script): # For bullets that are controlled by a script
def add_bullet(sprite, curve, speed): # for bullets with curved paths
# And so on ...

等等有很多变化。有没有更好的方法可以在不使用这么多关键字参数的情况下做到这一点,导致它变得有点难看。重命名每个函数也很糟糕,因为您会得到 add_bullet1add_bullet2add_bullet_with_really_long_name

要解决一些答案:

  1. 不,我无法创建 Bullet 类层次结构,因为那太慢了。管理项目符号的实际代码是用 C 语言编写的,我的函数是 C API 的包装器。

  2. 我知道关键字参数,但检查各种参数组合变得很烦人,但默认参数有助于分配如 acceleration=0

原文由 Bullets 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 320
1 个回答

您要求的是 multiple dispatch 。请参阅 Julia 语言示例,其中演示了不同类型的调度。

然而,在看那个之前,我们将首先解决为什么 重载 在 Python 中并不是您真正想要的。

为什么不超载?

首先,需要了解重载的概念以及它不适用于 Python 的原因。

当使用可以在编译时区分数据类型的语言时,可以在编译时选择备选方案。为编译时选择创建此类替代函数的行为通常称为重载函数。 ( 维基百科

Python 是一种 动态 类型的语言,因此重载的概念根本不适用于它。然而,一切并没有丢失,因为我们可以在运行时创建这样的 _替代函数_:

在将数据类型识别推迟到运行时的编程语言中,替代函数之间的选择必须在运行时发生,基于动态确定的函数参数类型。以这种方式选择其替代实现的函数通常被称为 multimethods 。 ( 维基百科

所以我们应该能够在 Python 中执行 多重 方法——或者,它也被称为: _多重分派_。

多次派遣

多重方法也称为 _多重分派_:

多重分派或多方法是一些面向对象编程语言的特性,其中一个函数或方法可以根据其多个参数的运行时(动态)类型动态分派。 ( 维基百科

Python 不支持开箱即用1 ,但是碰巧有一个名为 multipledispatch 的优秀 Python 包可以做到这一点。

解决方案

以下是我们如何使用 multipledispatch 2包来实现您的方法:

 >>> from multipledispatch import dispatch
>>> from collections import namedtuple
>>> from types import *  # we can test for lambda type, e.g.:
>>> type(lambda a: 1) == LambdaType
True

>>> Sprite = namedtuple('Sprite', ['name'])
>>> Point = namedtuple('Point', ['x', 'y'])
>>> Curve = namedtuple('Curve', ['x', 'y', 'z'])
>>> Vector = namedtuple('Vector', ['x','y','z'])

>>> @dispatch(Sprite, Point, Vector, int)
... def add_bullet(sprite, start, direction, speed):
...     print("Called Version 1")
...
>>> @dispatch(Sprite, Point, Point, int, float)
... def add_bullet(sprite, start, headto, speed, acceleration):
...     print("Called version 2")
...
>>> @dispatch(Sprite, LambdaType)
... def add_bullet(sprite, script):
...     print("Called version 3")
...
>>> @dispatch(Sprite, Curve, int)
... def add_bullet(sprite, curve, speed):
...     print("Called version 4")
...

>>> sprite = Sprite('Turtle')
>>> start = Point(1,2)
>>> direction = Vector(1,1,1)
>>> speed = 100 #km/h
>>> acceleration = 5.0 #m/s**2
>>> script = lambda sprite: sprite.x * 2
>>> curve = Curve(3, 1, 4)
>>> headto = Point(100, 100) # somewhere far away

>>> add_bullet(sprite, start, direction, speed)
Called Version 1

>>> add_bullet(sprite, start, headto, speed, acceleration)
Called version 2

>>> add_bullet(sprite, script)
Called version 3

>>> add_bullet(sprite, curve, speed)
Called version 4

1. Python 3 目前支持 single dispatch2. 注意不要在多线程环境中使用 multipledispatch 否则你会得到奇怪的行为。

原文由 Andriy Drozdyuk 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题