我知道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 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的实际类型。
一个可能的选项是使用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中有一个类似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'
你可以很容易地在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)。