我知道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_bullet1、add_bullet2就是add_bullet_with_really_long_name。

以下是一些问题的答案:

不,我不能创建一个子弹类层次结构,因为那太慢了。管理项目符号的实际代码是用C编写的,我的函数是围绕C API的包装器。 我知道关键字参数,但检查各种参数组合是令人讨厌的,但默认参数帮助分配,如加速度=0


当前回答

Plum以一种简单的python方式支持它。从下面的README复制一个示例。

from plum import dispatch

@dispatch
def f(x: str):
    return "This is a string!"
    

@dispatch
def f(x: int):
    return "This is an integer!"

>>> f("1")
'This is a string!'

>>> f(1)
'This is an integer!'

其他回答

在默认值中使用关键字参数。如。

def add_bullet(sprite, start=default, direction=default, script=default, speed=default):

在直线子弹和曲线子弹的情况下,我将添加两个函数:add_bullet_straight和add_bullet_curved。

一个可能的选项是使用multipledispatch模块,如下所示: http://matthewrocklin.com/blog/work/2014/02/25/Multiple-Dispatch

不要这样做:

def add(self, other):
    if isinstance(other, Foo):
        ...
    elif isinstance(other, Bar):
        ...
    else:
        raise NotImplementedError()

你可以这样做:

from multipledispatch import dispatch
@dispatch(int, int)
def add(x, y):
    return x + y    

@dispatch(object, object)
def add(x, y):
    return "%s + %s" % (x, y)

使用结果的用法:

>>> add(1, 2)
3

>>> add(1, 'hello')
'1 + hello'

根据定义,在python中重载函数是不可能的(详细信息请阅读下文),但您可以使用简单的装饰器实现类似的功能

class overload:
    def __init__(self, f):
        self.cases = {}

    def args(self, *args):
        def store_function(f):
            self.cases[tuple(args)] = f
            return self
        return store_function

    def __call__(self, *args):
        function = self.cases[tuple(type(arg) for arg in args)]
        return function(*args)

你可以这样用

@overload
def f():
    pass

@f.args(int, int)
def f(x, y):
    print('two integers')

@f.args(float)
def f(x):
    print('one float')


f(5.5)
f(1, 2)

修改它以适应您的用例。

概念的澄清

function dispatch: there are multiple functions with the same name. Which one should be called? two strategies static/compile-time dispatch (aka. "overloading"). decide which function to call based on the compile-time type of the arguments. In all dynamic languages, there is no compile-time type, so overloading is impossible by definition dynamic/run-time dispatch: decide which function to call based on the runtime type of the arguments. This is what all OOP languages do: multiple classes have the same methods, and the language decides which one to call based on the type of self/this argument. However, most languages only do it for the this argument only. The above decorator extends the idea to multiple parameters.

为了澄清这一点,假设我们用一种假想的静态语言定义函数

void f(Integer x):
    print('integer called')

void f(Float x):
    print('float called')

void f(Number x):
    print('number called')


Number x = new Integer('5')
f(x)
x = new Number('3.14')
f(x)

使用静态分派(重载),您将看到“number被调用”两次,因为x已被声明为number,这就是重载所关心的。在动态分派中,你会看到“integer called, float called”,因为它们是函数被调用时x的实际类型。

您还可以尝试这段代码。我们可以尝试任何论点

# Finding the average of given number of arguments
def avg(*args):   # args is the argument name we give
    sum = 0
    for i in args:
        sum += i
        average = sum/len(args)   # Will find length of arguments we given
    print("Avg: ", average)

# call function with different number of arguments
avg(1,2)
avg(5,6,4,7)
avg(11,23,54,111,76)

@overload装饰器添加了类型提示(PEP 484)。

虽然这并没有改变Python的行为,但它确实使它更容易理解正在发生的事情,并使mypy检测错误。

参见:输入提示和PEP 484