我知道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


当前回答

你可以很容易地在Python中实现函数重载。下面是一个使用浮点数和整数的例子:

class OverloadedFunction:
    def __init__(self):
        self.router = {int : self.f_int   ,
                       float: self.f_float}
    
    def __call__(self, x):
        return self.router[type(x)](x)
    
    def f_int(self, x):
        print('Integer Function')
        return x**2
    
    def f_float(self, x):
        print('Float Function (Overloaded)')
        return x**3

# f is our overloaded function
f = OverloadedFunction()

print(f(3 ))
print(f(3.))

# Output:
# Integer Function
# 9
# Float Function (Overloaded)
# 27.0

代码背后的主要思想是,类包含您想要实现的不同(重载)函数,而Dictionary则作为路由器,根据输入类型(x)将代码指向正确的函数。

PS1。对于自定义类,如Bullet1,可以按照类似的模式初始化内部字典,如self。D = {Bullet1: self。f_Bullet1…}。代码的其余部分是相同的。

PS2。所提出的解决方案的时间/空间复杂度也相当好,每个操作的平均成本为O(1)。

其他回答

要么在定义中使用多个关键字参数,要么创建一个Bullet层次结构,将其实例传递给函数。

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

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

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

Python 3.8增加了functools.singledispatchmethod

将方法转换为单分派泛型函数。 要定义一个泛型方法,请使用@singledispatchmethod装饰它 装饰。注意,调度发生在第一个的类型上 非self或非cls参数,相应地创建你的函数:

from functools import singledispatchmethod


class Negator:
    @singledispatchmethod
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(self, arg: int):
        return -arg

    @neg.register
    def _(self, arg: bool):
        return not arg


negator = Negator()
for v in [42, True, "Overloading"]:
    neg = negator.neg(v)
    print(f"{v=}, {neg=}")

输出

v=42, neg=-42
v=True, neg=False
NotImplementedError: Cannot negate a

@singledispatchmethod支持与其他装饰器嵌套,例如 @classmethod。注意,为了允许dispatcher.register, Singledispatchmethod必须是最外层的装饰器。这是 带有negg方法的否定类:

from functools import singledispatchmethod


class Negator:
    @singledispatchmethod
    @staticmethod
    def neg(arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(arg: int) -> int:
        return -arg

    @neg.register
    def _(arg: bool) -> bool:
        return not arg


for v in [42, True, "Overloading"]:
    neg = Negator.neg(v)
    print(f"{v=}, {neg=}")

输出:

v=42, neg=-42
v=True, neg=False
NotImplementedError: Cannot negate a

相同的图案可以用于其他类似的装饰: Staticmethod、abstractmethod等。

在Python中重载方法是很棘手的。但是,可以使用传递字典、列表或原始变量的方法。

我已经为我的用例尝试了一些东西,这可以帮助理解人们重载方法。

让我们以你为例:

类重载方法调用不同类的方法。

def add_bullet(sprite=None, start=None, headto=None, spead=None, acceleration=None):

从远程类传递参数:

add_bullet(sprite = 'test', start=Yes,headto={'lat':10.6666,'long':10.6666},accelaration=10.6}

Or

add_bullet(sprite = 'test', start=Yes, headto={'lat':10.6666,'long':10.6666},speed=['10','20,'30']}

因此,从方法重载中实现对列表、字典或基本变量的处理。

为您的代码尝试一下。

你可以很容易地在Python中实现函数重载。下面是一个使用浮点数和整数的例子:

class OverloadedFunction:
    def __init__(self):
        self.router = {int : self.f_int   ,
                       float: self.f_float}
    
    def __call__(self, x):
        return self.router[type(x)](x)
    
    def f_int(self, x):
        print('Integer Function')
        return x**2
    
    def f_float(self, x):
        print('Float Function (Overloaded)')
        return x**3

# f is our overloaded function
f = OverloadedFunction()

print(f(3 ))
print(f(3.))

# Output:
# Integer Function
# 9
# Float Function (Overloaded)
# 27.0

代码背后的主要思想是,类包含您想要实现的不同(重载)函数,而Dictionary则作为路由器,根据输入类型(x)将代码指向正确的函数。

PS1。对于自定义类,如Bullet1,可以按照类似的模式初始化内部字典,如self。D = {Bullet1: self。f_Bullet1…}。代码的其余部分是相同的。

PS2。所提出的解决方案的时间/空间复杂度也相当好,每个操作的平均成本为O(1)。