Python函数重载


213

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


5
作品只有一个参数,但在这里(的人来到这里,从搜索引擎):docs.python.org/3/library/...
leewz

1
这似乎是默认值的好地方。您可以将一些设置为“无”,然后进行检查。布尔值的影响似乎可以忽略不计
Andrew Scott Evans

必须使用default value + if + else与C ++相同的功能。这是C ++具有比Python更好的可读性的几件事之一
Deqing 2015年

我对为什么kwargs不是一个有效的答案感到困惑。您说您不想使用很多关键字参数,因为它变得很丑陋……这仅仅是问题的本质。如果您有很多论点,而且因为您有很多论点而超出预期,那么您会感到混乱吗?是否要使用许多参数而不在任何地方指定它们?Python不是头脑读者。
微积分

我们不知道对象script, curve是什么类型,它们是否有共同的祖先,它们支持什么方法。使用鸭式输入法,由班级设计人员确定它们需要支持哪些方法。大概Script支持某种基于时间步的回调(但是它应该返回什么对象?该时间步的位置?该时间步的轨迹?)。据推测start, direction, speedstart, headto, spead, acceleration这两种类型的描述轨迹的,但同样是由你来设计接收类知道如何解压它们并处理它们。
smci

Answers:


144

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

但是,在查看该内容之前,我们将首先解决为什么重载并不是python中真正想要的。

为什么不超载?

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

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

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

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

因此,我们应该能够做到多方法在python-或者,它也可称为:多分派

多次派遣

多重方法也称为多重调度

多种调度或多种方法是某些面向对象的编程语言的功能,其中可以基于多个参数的运行时(动态)类型来动态调度函数或方法。(维基百科

Python不支持这个开箱1,但是,因为它发生,有一个优秀的Python包称为multipledispatch这正是这么做的。

这是我们可能如何使用multidispatch 2包来实现您的方法的方法:

>>> 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
>>> 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.注意不要在多线程环境中使用 调度,否则会出现奇怪的行为。


6
在多线程环境中“ multipledispatch”有什么问题?由于服务器端的代码通常在多线程环境中!只是想把它挖出来!
danzeer

7
@danzeer不是线程安全的。我看到参数被两个不同的线程修改(即,speed当另一个线程将其自身的值设置为时,函数的中间值可能会改变speed)!我花了很长时间才意识到是罪魁祸首是图书馆。
Andriy Drozdyuk '17

108

演示时,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 

107
所有这些看起来都是可变参数的示例,而不是重载。由于重载使您可以具有相同的功能,不同类型的参数可以作为参数。例如:sum(real_num1,real_num2)和sum(imaginary_num1,imaginary_num2)都将具有相同的调用语法,但是实际上期望使用2种不同的类型作为输入,并且实现方式也必须在内部进行更改
Efren

17
使用答案,您将如何向调用者展示哪些参数合在一起?只需将每个参数都带有默认值的一堆参数提供相同的功能,但是就API而言,它就不太优雅了
Greg Ennis

6
以上都不是重载,实现将必须检查参数输入的所有组合(或忽略参数),例如:if sprite and script and not start and not direction and not speed...只是知道它在特定的动作中。因为调用者可以调用提供所有可用参数的函数。重载时,为您定义相关参数的确切集合。
Roee Gavirel

5
当人们说python支持方法重载时,这非常令人沮丧。它不是。您在引号中加上“方法超载”这一事实表明您已经意识到了这一事实。您可以通过多种技术获得类似的功能,例如此处提到的一种。但是方法重载有一个非常具体的定义。
霍华德·斯沃普

我认为目的是当方法重载不是python的功能时,上述机制可用于实现等效效果。
拉尔响了

93

您可以使用“自己动手”解决方案进行函数重载。这是从Guido van Rossum关于多方法的文章中复制(因为mm和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):
    # ...

最严格的限制,目前主要有:

  • 不支持方法,仅支持不是类成员的函数;
  • 不处理继承;
  • 不支持kwarg;
  • 注册新功能应该在导入时完成,这不是线程安全的

6
装饰器+1,用于扩展此用例中的语言。
Eloims

1
+1,因为这是个好主意(可能是OP应该带的东西)---我从未见过Python中的多方法实现。
Escualo 2014年

39

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

4
为什么没有得到更多的选票?由于缺少示例,我正在猜测...我已经创建了一个示例,给出了一个示例,该示例说明了如何使用multidispatch软件包实现OP问题的解决方案。
Andriy Drozdyuk

19

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

11

通常使用多态来解决这种类型的行为(在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来检查参数并适当地委派。我猜听起来还不错。


1
那是我最初的方法,但是出于性能原因,我不得不用C重写该代码
。– Bullets

@Bullets,我建议可能有许多不同的选项可用来提高性能,而不是编写很多可能不会做很多事情的c函数。例如:创建一个实例可能很昂贵,因此维护一个对象池。尽管我这样说是在不知道您发现速度太慢的情况下进行的。出于兴趣,这种方法到底慢了什么?除非要在边界的C侧花费大量时间,否则我不能认为Python(本身)是真正的问题。
Josh Smeaton

也许还有其他方法可以提高性能,但是使用C比使用Python更好。问题在于计算子弹的运动并检测它们何时超出屏幕边界。我有一种方法来计算子弹的位置,pos+v*t然后与屏幕边界进行比较if x > 800等等。每帧调用这些函数数百次,结果太慢了。在C中完成时,它的速度约为40 fps(在100%cpu下使用纯python)到60 fps(在5%-10%之间)
。– Bullets

@子弹,那么公平。我仍然会使用封装数据的方法。将bullet实例传递给add_bullet,并提取所需的所有字段。我将编辑答案。
Josh Smeaton

@Bullets:您可以结合使用C函数和Josh使用Cython建议的OOP方法。它允许早期绑定,因此不会影响速度。
jfs


4

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


我将建议第二种方法:制作一些BulletParams ...类以指定项目符号详细信息。
John Zwinck

您能详细说明一下吗?我试图用不同的项目符号创建一个类层次结构,但这是行不通的,因为Python太慢。它不能足够快地计算出所需数量的子弹的运动,因此我不得不用C编写该部分。所有的add_bullet变体都调用相应的C函数。
子弹

4

我认为您的基本要求是在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
脚本:
而x == True:print'hi'


4

@overload用类型的提示(PEP 484)添加装饰器。尽管这不会改变python的行为,但确实可以更轻松地了解正在发生的事情,并让mypy检测错误。
请参阅:键入提示PEP 484


您可以添加一些示例吗?
Gerrit

3

我认为Bullet具有相关多态性的类层次结构是必经之路。您可以通过使用元类有效地重载基类构造函数,以便调用基类可导致创建适当的子类对象。下面是一些示例代码,以说明我的意思。

更新

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

为了实现这一目标,一个BulletMetaBase的实例BulletMeta类是由创建时显式调用元类来创建Bullet基类(而不是使用__metaclass__=类属性或通过metaclass取决于Python版本关键字参数)。

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

嗯,这仍然只是将函数命名为add_bullet1,add_bullet2等的一种好方法。
符号

1
@Bullets:也许是,或者也许只是创建工厂函数的一种稍微复杂的方法。一件好事是它支持Bullet子类的层次结构,而不必每次添加另一个子类型时都修改基类或工厂函数。(当然,如果您使用的是C而不是C ++,那么我想您可能没有类。)您还可以创建一个更聪明的元类,该元类根据类型和/或数字自行找出要创建的子类。传递的参数(如C ++确实支持重载)。
martineau 2011年

1
这个继承想法也是我的第一选择。
丹尼尔·莫勒

3

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

相同的模式可用于其他类似的修饰符:staticmethod,abstractmethod等。


2

将关键字参数与默认值一起使用。例如

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

对于直子弹和弯曲子弹,我将添加两个函数:add_bullet_straightadd_bullet_curved


2

重载方法在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']}

因此,正在通过方法重载实现列表,字典或原始变量的处理。

试试看您的代码。


2

只是一个简单的装饰

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)

在静态分派(超载)中,您将看到两次“被调用”,因为x已被声明为Number,这就是所有超载所关心的。使用动态分派,您将看到“整数调用,浮点调用”,因为这些x是调用函数时的实际类型。


关键的是,该示例没有说明针对动态调度调用的是哪种方法x,也没有针对静态调度调用两种方法的调用顺序。建议您编辑打印报表print('number called for Integer')
SMCI
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.