问题描述
我知道 Python 不支持方法重载,但是我遇到了一个问题,我似乎无法以一种很好的 Pythonic 方式来解决.
我正在制作一个游戏,其中角色需要发射各种子弹,但是如何编写不同的函数来创建这些子弹?例如,假设我有一个函数可以创建一个以给定速度从 A 点到 B 点的子弹.我会写一个这样的函数:
def add_bullet(sprite, start, headto, speed):... 代码 ...
但是我想编写其他函数来创建项目符号,例如:
def add_bullet(sprite, start, direction, speed):def add_bullet(精灵,开始,头部,速度,加速度):def add_bullet(sprite, script): # 由脚本控制的子弹def add_bullet(sprite, curve, speed): # 用于带有曲线路径的子弹... 等等 ...
等等,有很多变化.有没有更好的方法来做到这一点而不使用这么多关键字参数导致它变得有点难看.重命名每个函数也很糟糕,因为您会得到 add_bullet1
、add_bullet2
或 add_bullet_with_really_long_name
.
解决一些问题:
不,我无法创建 Bullet 类层次结构,因为这太慢了.管理子弹的实际代码是用 C 语言编写的,我的函数是 C API 的包装器.
我知道关键字参数,但检查各种参数组合很烦人,但默认参数有助于分配,例如
acceleration=0
您所要求的称为多重调度.请参阅 Julia 语言示例,其中演示了不同类型的调度.
然而,在讨论之前,我们将首先解决为什么重载在 Python 中并不是您真正想要的.
为什么不重载?
首先,需要了解重载的概念以及为什么它不适用于 Python.
当使用可以区分数据类型的语言时编译时,可以在备选方案中进行选择编译时.创建此类替代功能的行为编译时选择通常称为重载功能.(维基百科)
Python 是一个动态 类型语言,因此重载的概念根本不适用于它.然而,一切都没有丢失,因为我们可以在运行时创建这样的替代函数:
在将数据类型识别推迟到运行时选择之间的选择函数必须在运行时发生,基于动态确定的函数参数的类型.替代的函数以这种方式选择的实现被引用最多通常为多方法.(维基百科)
所以我们应该能够在 Python 中实现multimethods——或者,它被称为:multiple dispatch.
多次分派
多方法也称为多分派:
Multiple dispatch 或 multimethods 是一些的特性面向对象的编程语言,其中的函数或方法可以根据运行时(动态)类型动态调度它的论据不止一个.(维基百科)
Python 不支持开箱即用的功能,但碰巧的是,有一个出色的 Python 包 multipledispatch 就是这样做的.
解决方案
这里是我们如何使用 multipledispatch 包来实施您的方法:
>>>从多分派进口分派>>>从集合导入namedtuple>>>from types import * # 我们可以测试 lambda 类型,例如:>>>type(lambda a: 1) == LambdaType真的>>>Sprite = namedtuple('Sprite', ['name'])>>>Point = namedtuple('Point', ['x', 'y'])>>>Curve = namedtuple('Curve', ['x', 'y', 'z'])>>>Vector = namedtuple('Vector', ['x','y','z'])>>>@dispatch(Sprite, Point, Vector, int)... def add_bullet(精灵,开始,方向,速度):... 打印(称为版本 1")...>>>@dispatch(Sprite, Point, Point, int, float)... def add_bullet(sprite, start, headto, speed, 加速度):...打印(称为版本2")...>>>@dispatch(精灵,LambdaType)... def add_bullet(sprite, script):...打印(称为版本3")...>>>@dispatch(精灵,曲线,整数)... def add_bullet(精灵,曲线,速度):...打印(称为版本4")...>>>sprite = Sprite('海龟')>>>开始 = 点(1,2)>>>方向 = 向量(1,1,1)>>>速度 = 100 #km/h>>>加速度 = 5.0 #m/s**2>>>脚本 = lambda 精灵:sprite.x * 2>>>曲线 = 曲线(3, 1, 4)>>>headto = Point(100, 100) # 某处很远>>>add_bullet(精灵,开始,方向,速度)称为版本 1>>>add_bullet(精灵,开始,头部,速度,加速度)称为版本 2>>>add_bullet(精灵,脚本)称为版本 3>>>add_bullet(精灵,曲线,速度)称为版本 42.注意不要在多线程环境中使用 multipledispatch 否则你会得到奇怪的行为.
I know that Python does not support method overloading, but I've run into a problem that I can't seem to solve in a nice Pythonic way.
I am making a game where a character needs to shoot a variety of bullets, but how do I write different functions for creating these bullets? For example suppose I have a function that creates a bullet travelling from point A to B with a given speed. I would write a function like this:
def add_bullet(sprite, start, headto, speed):
... Code ...
But I want to write other functions for creating bullets like:
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 ...
And so on with many variations. Is there a better way to do it without using so many keyword arguments cause its getting kinda ugly fast. Renaming each function is pretty bad too because you get either add_bullet1
, add_bullet2
, or add_bullet_with_really_long_name
.
To address some answers:
No I can't create a Bullet class hierarchy because thats too slow. The actual code for managing bullets is in C and my functions are wrappers around C API.
I know about the keyword arguments but checking for all sorts of combinations of parameters is getting annoying, but default arguments help allot like
acceleration=0
What you are asking for is called multiple dispatch. See Julia language examples which demonstrates different types of dispatches.
However, before looking at that, we'll first tackle why overloading is not really what you want in Python.
Why Not Overloading?
First, one needs to understand the concept of overloading and why it's not applicable to Python.
Python is a dynamically typed language, so the concept of overloading simply does not apply to it. However, all is not lost, since we can create such alternative functions at run-time:
So we should be able to do multimethods in Python—or, as it is alternatively called: multiple dispatch.
Multiple dispatch
The multimethods are also called multiple dispatch:
Python does not support this out of the box, but, as it happens, there is an excellent Python package called multipledispatch that does exactly that.
Solution
Here is how we might use multipledispatch package to implement your methods:
>>> from multipledispatch import dispatch
>>> from collections import namedtuple
>>> from types import * # we can test for lambda type, e.g.:
>>> type(lambda a: 1) == LambdaType
True
>>> Sprite = namedtuple('Sprite', ['name'])
>>> Point = namedtuple('Point', ['x', 'y'])
>>> Curve = namedtuple('Curve', ['x', 'y', 'z'])
>>> Vector = namedtuple('Vector', ['x','y','z'])
>>> @dispatch(Sprite, Point, Vector, int)
... def add_bullet(sprite, start, direction, speed):
... print("Called Version 1")
...
>>> @dispatch(Sprite, Point, Point, int, float)
... def add_bullet(sprite, start, headto, speed, acceleration):
... print("Called version 2")
...
>>> @dispatch(Sprite, LambdaType)
... def add_bullet(sprite, script):
... print("Called version 3")
...
>>> @dispatch(Sprite, Curve, int)
... def add_bullet(sprite, curve, speed):
... print("Called version 4")
...
>>> sprite = Sprite('Turtle')
>>> start = Point(1,2)
>>> direction = Vector(1,1,1)
>>> speed = 100 #km/h
>>> acceleration = 5.0 #m/s**2
>>> script = lambda sprite: sprite.x * 2
>>> curve = Curve(3, 1, 4)
>>> headto = Point(100, 100) # somewhere far away
>>> add_bullet(sprite, start, direction, speed)
Called Version 1
>>> add_bullet(sprite, start, headto, speed, acceleration)
Called version 2
>>> add_bullet(sprite, script)
Called version 3
>>> add_bullet(sprite, curve, speed)
Called version 4
这篇关于Python 函数重载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!