313 votes

Python surcharge de fonctions

Je sais que Python ne prend pas en charge la surcharge de méthode, mais j'ai couru dans un problème que je n'arrive pas à résoudre dans un joli Pythonic.

Je suis de faire un jeu où un personnage doit tirer une variété de balles, mais comment puis-je écrire de différentes fonctions pour la création de ces balles? Par exemple, supposons que j'ai une fonction qui crée une balle de voyager d'Un point a à B avec une vitesse donnée. Je voudrais écrire une fonction comme ceci:

    def add_bullet(sprite, start, headto, speed):
        ... Code ...

Mais je veux écrire d'autres fonctions pour la création de balles comme:

    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 ...

Et ainsi de suite avec de nombreuses variations. Est-il un meilleur moyen de le faire sans l'aide de sorte de nombreux mot-clé arguments cause de son arriver un peu laid rapide. Renommer chaque fonction est assez mauvais aussi, parce que vous obtenez soit add_bullet1, add_bullet2ou add_bullet_with_really_long_name.

Pour répondre à certaines des réponses:

  1. Non, je ne peux pas créer une Balle hiérarchie de classe, car c'est trop lent. Le code actuel pour la gestion de balles est dans C et mes fonctions sont des wrappers autour de C API.

  2. Je connais le mot-clé arguments, mais la vérification de toutes sortes de combinaisons de paramètres est plus ennuyeux, mais les arguments par défaut aider à attribuer comme acceleration=0

125voto

Escualo Points 12584

Python prend en charge "la surcharge de méthode", comme vous le présenter. En fait, ce que vous venez de décrire est facile à implémenter en Python, en tant de manières différentes, mais je voudrais aller avec:

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

Dans le code ci-dessus, default est plausible valeur par défaut pour ces arguments, ou None. Vous pouvez ensuite appeler la méthode avec seulement les arguments qui vous intéresse, et Python utilisera les valeurs par défaut.

Vous pouvez aussi faire quelque chose comme ceci:

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

Une autre alternative est de directement crochet de la fonction de votre choix directement de la classe ou de l'instance:

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

Encore un autre moyen est d'utiliser un abstract factory modèle:

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 

105voto

Vous pouvez utiliser "rouler propre" solution pour la surcharge de fonctions. Celui-ci est copié à partir de Guido van Rossum, l'article sur multimethods (car il y a peu de différence entre mm et une surcharge en 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

L'utilisation serait

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):
    # ...

La plupart des restrictions à l'instant sont:

  • les méthodes ne sont pas pris en charge, seules les fonctions qui ne sont pas membres de la classe;
  • l'héritage n'est pas traitée;
  • kwargs ne sont pas pris en charge;
  • l'enregistrement de nouvelles fonctions doit être fait au moment de l'import gedcom chose n'est pas thread-safe

57voto

Dave C Points 121

Une option possible est d'utiliser la multipledispatch module comme détaillé ici: http://matthewrocklin.com/blog/work/2014/02/25/Multiple-Dispatch/

Au lieu de faire ceci:

def add(self, other):
    if isinstance(other, Foo):
        ...
    elif isinstance(other, Bar):
        ...
    else:
        raise NotImplementedError()

Vous pouvez faire ceci:

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)

résultant de l'utilisation:

>>> add(1, 2)
3

>>> add(1, 'hello')
'1 + hello'

13voto

Josh Smeaton Points 18165

Ce type de comportement est généralement résolu (en POO langues) à l'aide de Polymorphisme. Chaque type de balle serait responsable pour savoir comment il se déplace. Par exemple:

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

Passer autant d'arguments pour la c_function, puis faire le travail de détermination c de la fonction d'appel en se basant sur les valeurs de la c de la fonction. Donc, python ne doit jamais appeler une fonction c. Que c une fonction examine les arguments, et puis peut déléguer à d'autres fonctions c de façon appropriée.

Vous êtes essentiellement en utilisant simplement chaque sous-classe comme un autre conteneur de données, mais par la définition de tous les arguments sur la classe de base, les sous-classes sont libres d'ignorer ceux qu'ils ne font rien.

Quand un nouveau type de puce vient le long, vous pouvez simplement définir un plus bien sur la base, modifier une fonction python afin qu'il passe le supplément de la propriété, et l'un c_function qui examine les arguments et les délégués de façon appropriée. N'a pas l'air trop mal, je suppose.

4voto

Soit utiliser plusieurs arguments mot-clé dans la définition, ou de créer un Bullet de la hiérarchie dont les instances sont passés à la fonction.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X