Python 函数重载

2024-12-19 09:24:00
admin
原创
133
摘要:问题描述:我知道 Python 不支持方法重载,但我遇到了一个似乎无法以一种好的 Pythonic 方式解决的问题。我正在制作一款游戏,其中的角色需要射出各种子弹,但我该如何编写不同的函数来创建这些子弹呢?例如,假设我有一个函数可以创建一个以给定速度从 A 点飞到 B 点的子弹。我会写一个这样的函数:def ...

问题描述:

我知道 Python 不支持方法重载,但我遇到了一个似乎无法以一种好的 Pythonic 方式解决的问题。

我正在制作一款游戏,其中的角色需要射出各种子弹,但我该如何编写不同的函数来创建这些子弹呢?例如,假设我有一个函数可以创建一个以给定速度从 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_bullet1add_bullet2add_bullet_with_really_long_name

解决一些问题:

  1. 不,我无法创建 Bullet 类层次结构,因为那太慢了。管理项目符号的实际代码是用 C 编写的,而我的函数是 C API 的包装器。

  2. 我知道关键字参数,但检查各种参数组合很烦人,但默认参数有助于分配acceleration=0


解决方案 1:

您所要求的是多重调度。请参阅Julia语言示例,其中演示了不同类型的调度。

然而,在研究这个问题之前,我们首先来讨论一下为什么重载并不是 Python 中我们真正想要的。

为什么不重载?

首先,需要了解重载的概念以及为什么它不适用于 Python。

当使用可以在编译时区分数据类型的语言时,可以在编译时对备选方案进行选择。创建此类用于编译时选择的备选函数通常称为函数重载。(维基百科)

Python 是一种动态类型语言,因此重载的概念根本不适用于它。不过,一切还不算完,因为我们可以在运行时创建这样的替代函数:

在将数据类型识别推迟到运行时的编程语言中,必须在运行时根据动态确定的函数参数类型在备选函数中进行选择。以这种方式选择备选实现的函数通常称为多方法。(维基百科)

因此我们应该能够在 Python 中实现多方法——或者,也可以这样叫:多重分派

多重调度

多方法也称为多重分派

多重分派或多方法是一些面向对象编程语言的特性,在这些语言中,函数或方法可以根据其多个参数的运行时(动态)类型动态分派。(维基百科)

Python 本身并不支持这种功能1,但是碰巧有一个名为multipledispatch的优秀 Python 包可以实现这一点。

解决方案

以下是我们如何使用multipledispatch2包来实现您的方法:

>>> 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
  1. Python 3 目前支持单次调度

  2. 注意不要 在多线程环境中使用multipledispatch ,否则会出现奇怪的行为。

解决方案 2:

正如您所介绍的,Python 确实支持“方法重载”。事实上,您刚才描述的内容在 Python 中很容易实现,有很多不同的方法,但我会选择:

class Character(object):
    # your character __init__ and other methods go here

    def add_bullet(self, sprite=default, start=default, 
                 direction=default, speed=default, accel=default, 
                  curve=default):
        # do stuff with your arguments

在上面的代码中,default是这些参数的合理默认值,或None。然后,您可以仅使用您感兴趣的参数来调用该方法,Python 将使用默认值。

你也可以做这样的事情:

class Character(object):
    # your character __init__ and other methods go here

    def add_bullet(self, **kwargs):
        # here you can unpack kwargs as (key, values) and
        # do stuff with them, and use some global dictionary
        # to provide default values and ensure that ``key``
        # is a valid argument...

        # do stuff with your arguments

另一种方法是直接将所需的函数挂接到类或实例上:

def some_implementation(self, arg1, arg2, arg3):
  # implementation
my_class.add_bullet = some_implementation_of_add_bullet

另一种方法是使用抽象工厂模式:

class Character(object):
   def __init__(self, bfactory, *args, **kwargs):
       self.bfactory = bfactory
   def add_bullet(self):
       sprite = self.bfactory.sprite()
       speed = self.bfactory.speed()
       # do stuff with your sprite and speed

class pretty_and_fast_factory(object):
    def sprite(self):
       return pretty_sprite
    def speed(self):
       return 10000000000.0

my_character = Character(pretty_and_fast_factory(), a1, a2, kw1=v1, kw2=v2)
my_character.add_bullet() # uses pretty_and_fast_factory

# now, if you have another factory called "ugly_and_slow_factory" 
# you can change it at runtime in python by issuing
my_character.bfactory = ugly_and_slow_factory()

# In the last example you can see abstract factory and "method
# overloading" (as you call it) in action 

解决方案 3:

您可以使用“自行解决”的函数重载解决方案。此解决方案是从Guido van Rossum 的关于多方法的文章中复制而来(因为 Python 中的多方法和重载之间几乎没有区别):

registry = {}

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function


def multimethod(*types):
    def register(function):
        name = function.__name__
        mm = registry.get(name)
        if mm is None:
            mm = registry[name] = MultiMethod(name)
        mm.register(types, function)
        return mm
    return register

用法是

from multimethods import multimethod
import unittest

# 'overload' makes more sense in this case
overload = multimethod

class Sprite(object):
    pass

class Point(object):
    pass

class Curve(object):
    pass

@overload(Sprite, Point, Direction, int)
def add_bullet(sprite, start, direction, speed):
    # ...

@overload(Sprite, Point, Point, int, int)
def add_bullet(sprite, start, headto, speed, acceleration):
    # ...

@overload(Sprite, str)
def add_bullet(sprite, script):
    # ...

@overload(Sprite, Curve, speed)
def add_bullet(sprite, curve, speed):
    # ...

目前最严格的限制是:

  • 不支持方法,仅支持非类成员的函数;

  • 不处理继承;

  • 不支持 kwargs;

  • 注册新函数应该在导入时完成,这不是线程安全的

解决方案 4:

一个可能的选择是使用 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'

解决方案 5:

在 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)

解决方案 6:

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

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

from typing import overload

@overload
def foo() -> int: ...
@overload
def foo(arg: int) -> int: ...
def foo(arg=None):
    if arg is None:
        return 0
    return arg + 1

参见:类型提示和PEP 484

解决方案 7:

这种行为通常使用多态性(在 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。我想这听起来还不错。

解决方案 8:

Python 3.8 添加了functools.singledispatchmethod

将方法转换为单分派通用函数。

要定义通用方法,请使用 @singledispatchmethod 装饰器对其进行装饰。请注意,分派发生在第一个非自身或非 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 必须是最外层的装饰器。以下是 Negator 类,其中 neg 方法与类绑定:

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

相同的模式可用于其他类似的装饰器:静态方法、抽象方法等。

解决方案 9:

从定义上讲,在 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)

修改它以使其适合您的用例。

概念澄清

  • 函数调度:有多个同名函数。应该调用哪一个?两种策略

  • 静态/编译时调度又名“重载” )。根据参数的编译时类型决定调用哪个函数。在所有动态语言中,都没有编译时类型,因此根据定义,重载是不可能的

  • 动态/运行时分派:根据参数的运行时类型决定调用哪个函数。所有 OOP 语言都是这样做的:多个类具有相同的方法,语言根据self/this参数的类型决定调用哪个方法。但是,大多数语言只针对参数执行此操作this。上述装饰器将这个想法扩展到多个参数。

为了澄清这一点,假设我们在一种假设的静态语言中定义函数

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 called”,因为x已声明为,而这才是重载所关心的。使用动态分派时,您将看到“integer called, float called”,因为这些是函数调用时Number的实际类型。x

解决方案 10:

通过传递关键字参数。

def add_bullet(**kwargs):
    #check for the arguments listed above and do the proper things

解决方案 11:

你可以很容易地在 Python 中实现函数重载。下面是一个使用floatsand 的示例integers

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

代码背后的主要思想是,一个类包含您想要实现的不同(重载)函数,而一个字典充当router根据输入将您的代码引导至正确的函数type(x)

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

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

解决方案 12:

我认为您的基本要求是在 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

Sprite:我是另一个 Sprite

脚本:

while x == True:print 'hi'

解决方案 13:

您可以使用以下 Python 代码实现此目的:

@overload
def test(message: str):
    return message

@overload
def test(number: int):
    return number + 1

解决方案 14:

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

解决方案 15:

如何在python中重载?

我知道这是一个老问题,但这个话题仍然非常相关,而且我还没有读到过一个清晰简洁的答案,所以我想我自己提供一个。


首先,安装包:

pip3 install overloading
pip3 install typing

然后,使用overload包中的装饰器定义该函数的多个实现:

from typing import Set, List
from overloading import overload

@overload
def process_data(data: List[str], join_string: str) -> str:
    return join_string.join(data)

@overload
def process_data(data: Set[str], join_string: str) -> str:
    return join_string.join(data)

@overload
def process_data(data: List[float], round_to: int) -> List[float]:
    return [round(x, round_to) for x in data]

# And so on ...

参数的数量和类型决定了调用函数的哪个版本。

  • 不幸的是,不支持基于参数名称的分派


您可以在这里找到完整的文档。

解决方案 16:

我认为Bullet具有相关多态性的类层次结构是可行的方法。您可以使用元类有效地重载基类构造函数,以便调用基类会创建适当的子类对象。下面是一些示例代码,用于说明我的意思的本质。

更新

代码已修改为可在 Python 2 和 Python 3 下运行,以保持相关性。这样做是为了避免使用 Python 的显式元类语法,因为这两个版本之间的语法有所不同。

为了实现该目标,在创建基类时通过明确调用元类来创建类BulletMetaBase的实例(而不是使用类属性或通过取决于 Python 版本的关键字参数)。BulletMeta`Bullet__metaclass__=metaclass`

class BulletMeta(type):
    def __new__(cls, classname, bases, classdict):
        """ Create Bullet class or a subclass of it. """
        classobj = type.__new__(cls, classname, bases, classdict)
        if classname != 'BulletMetaBase':
            if classname == 'Bullet':  # Base class definition?
                classobj.registry = {}  # Initialize subclass registry.
            else:
                try:
                    alias = classdict['alias']
                except KeyError:
                    raise TypeError("Bullet subclass %s has no 'alias'" %
                                    classname)
                if alias in Bullet.registry: # unique?
                    raise TypeError("Bullet subclass %s's alias attribute "
                                    "%r already in use" % (classname, alias))
                # Register subclass under the specified alias.
                classobj.registry[alias] = classobj

        return classobj

    def __call__(cls, alias, *args, **kwargs):
        """ Bullet subclasses instance factory.

            Subclasses should only be instantiated by calls to the base
            class with their subclass' alias as the first arg.
        """
        if cls != Bullet:
            raise TypeError("Bullet subclass %r objects should not to "
                            "be explicitly constructed." % cls.__name__)
        elif alias not in cls.registry: # Bullet subclass?
            raise NotImplementedError("Unknown Bullet subclass %r" %
                                      str(alias))
        # Create designated subclass object (call its __init__ method).
        subclass = cls.registry[alias]
        return type.__call__(subclass, *args, **kwargs)


class Bullet(BulletMeta('BulletMetaBase', (object,), {})):
    # Presumably you'd define some abstract methods that all here
    # that would be supported by all subclasses.
    # These definitions could just raise NotImplementedError() or
    # implement the functionality is some sub-optimal generic way.
    # For example:
    def fire(self, *args, **kwargs):
        raise NotImplementedError(self.__class__.__name__ + ".fire() method")

    # Abstract base class's __init__ should never be called.
    # If subclasses need to call super class's __init__() for some
    # reason then it would need to be implemented.
    def __init__(self, *args, **kwargs):
        raise NotImplementedError("Bullet is an abstract base class")


# Subclass definitions.
class Bullet1(Bullet):
    alias = 'B1'
    def __init__(self, sprite, start, direction, speed):
        print('creating %s object' % self.__class__.__name__)
    def fire(self, trajectory):
        print('Bullet1 object fired with %s trajectory' % trajectory)


class Bullet2(Bullet):
    alias = 'B2'
    def __init__(self, sprite, start, headto, spead, acceleration):
        print('creating %s object' % self.__class__.__name__)


class Bullet3(Bullet):
    alias = 'B3'
    def __init__(self, sprite, script): # script controlled bullets
        print('creating %s object' % self.__class__.__name__)


class Bullet4(Bullet):
    alias = 'B4'
    def __init__(self, sprite, curve, speed): # for bullets with curved paths
        print('creating %s object' % self.__class__.__name__)


class Sprite: pass
class Curve: pass

b1 = Bullet('B1', Sprite(), (10,20,30), 90, 600)
b2 = Bullet('B2', Sprite(), (-30,17,94), (1,-1,-1), 600, 10)
b3 = Bullet('B3', Sprite(), 'bullet42.script')
b4 = Bullet('B4', Sprite(), Curve(), 720)
b1.fire('uniform gravity')
b2.fire('uniform gravity')

输出:

creating Bullet1 object
creating Bullet2 object
creating Bullet3 object
creating Bullet4 object
Bullet1 object fired with uniform gravity trajectory
Traceback (most recent call last):
  File "python-function-overloading.py", line 93, in <module>
    b2.fire('uniform gravity') # NotImplementedError: Bullet2.fire() method
  File "python-function-overloading.py", line 49, in fire
    raise NotImplementedError(self.__class__.__name__ + ".fire() method")
NotImplementedError: Bullet2.fire() method

解决方案 17:

使用带有默认值的关键字参数。例如

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

对于直子弹与弯子弹的情况,我会添加两个功能:add_bullet_straightadd_bullet_curved

解决方案 18:

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

或者

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

因此,可以通过方法重载来实现对列表、字典或原始变量的处理。

用你的代码尝试一下。

解决方案 19:

Plum以简单的 Python 方式支持它。从下面的 README 中复制一个示例。

from plum import dispatch

@dispatch
def f(x: str):
    return "This is a string!"
    

@dispatch
def f(x: int):
    return "This is an integer!"

>>> f("1")
'This is a string!'

>>> f(1)
'This is an integer!'

解决方案 20:

我的解决方案

def _either(**kwargs):
    return len([True for _, v in kwargs.items() if v is not None]) == 1

def overload(func, _overloaded_args=None):
    """enable overloading toward all params
    Usage:
        @overload
        def overloaded_func(either1=None, either2=None, either3=None):
            pass
        @overload
        def overloaded_func(must1, must2, either1=None, either2=None):
            pass
    """
    def inner(*func_args, **func_kwargs):
        nonlocal _overloaded_args
        __overloaded_args = _overloaded_args
        if __overloaded_args is None:
            __overloaded_args = list(func_kwargs.keys())
        if __overloaded_args:
            __overloaded_kwargs = {k: v for k, v in func_kwargs.items()
                                  if k in __overloaded_args}
            assert _either(**__overloaded_kwargs), (
                'function overloading should contain a single overloaded param.'
                f' overloaded params: {__overloaded_args}. inputs: {func_kwargs}')
        return func(*func_args, **func_kwargs)
    return inner

def overload_args(*_overloaded_args):
    """enable overloading toward specified params
    Usage:
        @overload_args('either1', 'either2')
        def overloaded_func(either1=None, either2=None):
            pass
        @overload_args('either1', 'either2')
        def overloaded_func(either1=None, either2=None, optional1=None, optional2=123):
            pass
    """
    def inner(func):
        return overload(func, _overloaded_args=_overloaded_args)
    return inner

解决方案 21:

从 Python 3.10 开始,你可以使用 match-case 语句来执行此操作。如下所示:

def add_bullet(arguments):
    match arguments:
        case (sprite, start, direction, speed):
            # your code here
        case (sprite, start, headto, spead, acceleration):
            # your code here
        case (sprite, script):
            # your code here
        case (sprite, curve, speed):
            # your code here

只需确保以元组或列表的形式输入参数即可。如下所示:

new_bullet = add_bullet((mysprite, myscript))
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1590  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1361  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   18  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   18  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   19  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用