我知道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 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 3.4 PEP-0443。添加了单分派通用函数。
下面是PEP的简短API描述。
要定义一个泛型函数,请使用@singledispatch装饰器来装饰它。注意,分派发生在第一个参数的类型上。创建相应的函数:
from functools import singledispatch
@singledispatch
def fun(arg, verbose=False):
if verbose:
print("Let me just say,", end=" ")
print(arg)
若要向函数添加重载实现,请使用泛型函数的register()属性。这是一个装饰器,接受一个类型参数,并装饰一个实现该类型操作的函数:
@fun.register(int)
def _(arg, verbose=False):
if verbose:
print("Strength in numbers, eh?", end=" ")
print(arg)
@fun.register(list)
def _(arg, verbose=False):
if verbose:
print("Enumerate this:")
for i, elem in enumerate(arg):
print(i, elem)
一个可能的选项是使用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'