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


当前回答

这种类型的行为通常是通过多态性(在OOP语言中)解决的。每一种子弹都有自己的飞行轨迹。例如:

class Bullet(object):
    def __init__(self):
        self.curve = None
        self.speed = None
        self.acceleration = None
        self.sprite_image = None

class RegularBullet(Bullet):
    def __init__(self):
        super(RegularBullet, self).__init__()
        self.speed = 10

class Grenade(Bullet):
    def __init__(self):
        super(Grenade, self).__init__()
        self.speed = 4
        self.curve = 3.5

add_bullet(Grendade())

def add_bullet(bullet):
    c_function(bullet.speed, bullet.curve, bullet.acceleration, bullet.sprite, bullet.x, bullet.y)


void c_function(double speed, double curve, double accel, char[] sprite, ...) {
    if (speed != null && ...) regular_bullet(...)
    else if (...) curved_bullet(...)
    //..etc..
}

向存在的c_function传递尽可能多的参数,然后根据初始c函数中的值确定调用哪个c函数。所以,Python应该只调用一个c函数。那个c函数查看参数,然后可以适当地委托给其他c函数。

本质上,您只是将每个子类用作不同的数据容器,但是通过在基类上定义所有潜在的参数,子类可以自由地忽略它们不做任何操作的参数。

当出现新的类型的项目符号时,您可以简单地在基础上再定义一个属性,更改一个python函数,以便它传递额外的属性,以及一个c_function,以适当地检查参数和委托。我想听起来还不算太糟。

其他回答

你可以很容易地在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)。

我认为你的基本要求是在Python中有一个类似C/ c++的语法,并且尽可能不让人头疼。尽管我喜欢Alexander Poluektov的回答,但它并不适用于课堂。

以下内容应该适用于类。它通过区分非关键字参数的数量来工作(但它不支持通过类型来区分):

class TestOverloading(object):
    def overloaded_function(self, *args, **kwargs):
        # Call the function that has the same number of non-keyword arguments.
        getattr(self, "_overloaded_function_impl_" + str(len(args)))(*args, **kwargs)

    def _overloaded_function_impl_3(self, sprite, start, direction, **kwargs):
        print "This is overload 3"
        print "Sprite: %s" % str(sprite)
        print "Start: %s" % str(start)
        print "Direction: %s" % str(direction)

    def _overloaded_function_impl_2(self, sprite, script):
        print "This is overload 2"
        print "Sprite: %s" % str(sprite)
        print "Script: "
        print script

它可以这样简单地使用:

test = TestOverloading()

test.overloaded_function("I'm a Sprite", 0, "Right")
print
test.overloaded_function("I'm another Sprite", "while x == True: print 'hi'")

输出:

这是过载3 精灵:我是精灵 开始:0 方向:对 这是过载2 精灵:我是另一个精灵 脚本: 当x == True:打印'hi'

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

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

参见:输入提示和PEP 484

根据定义,在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的实际类型。

在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']}

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

为您的代码尝试一下。