Je sais que Python ne prend pas en charge la surcharge de méthode, mais j'ai rencontré un problème que je n'arrive pas à résoudre d'une manière sympa Pythonic.
Je fais un jeu où un personnage doit tirer une variété de balles, mais comment puis-je écrire différentes fonctions pour créer ces balles? Par exemple, supposons que j'ai une fonction qui crée une balle se déplaçant du point A vers B avec une vitesse donnée. J'écrirais une fonction comme celle-ci:
def add_bullet(sprite, start, headto, speed):
... Code ...
Mais je veux écrire d'autres fonctions pour créer des puces 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 variantes. Existe-t-il une meilleure façon de le faire sans utiliser autant d'arguments de mots clés, cela devient un peu laid rapidement. Changement de nom chaque fonction est assez mal aussi parce que vous obtenez soit add_bullet1
, add_bullet2
ou add_bullet_with_really_long_name
.
Pour répondre à certaines réponses:
Non, je ne peux pas créer une hiérarchie de classes Bullet car c'est trop lent. Le code réel pour la gestion des puces est en C et mes fonctions sont des wrappers autour de l'API C.
Je connais les arguments des mots clés, mais vérifier toutes sortes de combinaisons de paramètres devient ennuyeux, mais les arguments par défaut aident à attribuer comme
acceleration=0
default value + if + else
pour faire la même chose que C ++. C'est l'une des très rares choses que C ++ a une meilleure lisibilité que Python ...script, curve
sont, ont-ils un ancêtre commun, quelles méthodes ils prennent en charge. Avec la frappe de canard, c'est à vous de concevoir la classe pour déterminer les méthodes dont ils ont besoin pour prendre en charge.Script
Prend probablement en charge une sorte de rappel basé sur le pas de temps (mais quel objet doit-il retourner? La position à ce pas de temps? La trajectoire à ce pas de temps?). Vraisemblablementstart, direction, speed
, lesstart, headto, spead, acceleration
deux décrivent les types de trajectoires, mais encore une fois, c'est à vous de concevoir la classe de réception pour savoir comment les déballer et les traiter.Réponses:
Ce que vous demandez s'appelle l' envoi multiple . Voir des exemples de langage Julia qui illustrent différents types de dépêches.
Cependant, avant de regarder cela, nous aborderons d'abord pourquoi la surcharge n'est pas vraiment ce que vous voulez en python.
Pourquoi ne pas surcharger?
Tout d'abord, il faut comprendre le concept de surcharge et pourquoi il ne s'applique pas au python.
Python est un langage typé dynamiquement , donc le concept de surcharge ne s'applique tout simplement pas à lui. Cependant, tout n'est pas perdu, car nous pouvons créer de telles fonctions alternatives au moment de l'exécution:
Nous devrions donc être capables de faire des multiméthodes en python - ou, comme on l'appelle alternativement: la répartition multiple .
Envoi multiple
Les multiméthodes sont également appelées expédition multiple :
Python ne prend pas cela en charge dès la sortie de la boîte 1 , mais, en l'occurrence, il existe un excellent package python appelé multipledispatch qui fait exactement cela.
Solution
Voici comment nous pourrions utiliser le package multipledispatch 2 pour implémenter vos méthodes:
1. Python 3 prend actuellement en charge la répartition unique. 2. Faites attention de ne pas utiliser la diffusion multiple dans un environnement multi-thread ou vous obtiendrez un comportement étrange.
la source
speed
pourrait changer au milieu de la fonction lorsqu'un autre thread définit sa propre valeur despeed
) !!! Il m'a fallu beaucoup de temps pour réaliser que c'était la bibliothèque qui était le coupable.Python prend en charge la «surcharge de méthode» telle que vous la présentez. En fait, ce que vous venez de décrire est trivial à implémenter en Python, de tant de façons différentes, mais j'irais avec:
Dans le code ci-dessus,
default
est une valeur par défaut plausible pour ces arguments, ouNone
. Vous pouvez ensuite appeler la méthode avec uniquement les arguments qui vous intéressent et Python utilisera les valeurs par défaut.Vous pouvez également faire quelque chose comme ceci:
Une autre alternative consiste à accrocher directement la fonction souhaitée directement à la classe ou à l'instance:
Encore une autre façon consiste à utiliser un modèle d'usine abstrait:
la source
if sprite and script and not start and not direction and not speed...
juste pour savoir qu'il s'agit d'une action spécifique. car un appelant peut appeler la fonction en fournissant tous les paramètres disponibles. Pendant la surcharge, définissez pour vous les ensembles exacts de paramètres pertinents.Vous pouvez utiliser la solution "roll-your-own" pour la surcharge de fonctions. Celui-ci est copié de l'article de Guido van Rossum sur les multiméthodes (car il y a peu de différence entre mm et surcharge en python):
L'usage serait
Limitations les plus restrictives à l'heure actuelle sont:
la source
Une option possible consiste à utiliser le module de répartition multiple comme détaillé ici: http://matthewrocklin.com/blog/work/2014/02/25/Multiple-Dispatch
Au lieu de faire ceci:
Tu peux le faire:
Avec l'utilisation résultante:
la source
En Python 3.4 a été ajouté PEP-0443. Fonctions génériques à envoi unique .
Voici une courte description de l'API de PEP.
Pour définir une fonction générique, décorez-la avec le décorateur @singledispatch. Notez que la répartition se produit sur le type du premier argument. Créez votre fonction en conséquence:
Pour ajouter des implémentations surchargées à la fonction, utilisez l'attribut register () de la fonction générique. Il s'agit d'un décorateur, prenant un paramètre de type et décorant une fonction implémentant l'opération pour ce type:
la source
Ce type de comportement est généralement résolu (dans les langages POO) en utilisant le polymorphisme. Chaque type de balle serait responsable de savoir comment elle se déplace. Par exemple:
Passez autant d'arguments à la fonction c_ qui existent, puis déterminez la fonction c à appeler en fonction des valeurs de la fonction c initiale. Ainsi, python ne devrait jamais appeler que la fonction one c. Cette fonction c examine les arguments, puis peut déléguer à d'autres fonctions c de manière appropriée.
Vous utilisez essentiellement chaque sous-classe comme un conteneur de données différent, mais en définissant tous les arguments potentiels sur la classe de base, les sous-classes sont libres d'ignorer celles avec lesquelles elles ne font rien.
Lorsqu'un nouveau type de puce arrive, vous pouvez simplement définir une propriété de plus sur la base, modifier la fonction python afin qu'elle transmette la propriété supplémentaire et la fonction c_function qui examine les arguments et délègue de manière appropriée. Cela ne semble pas trop mal, je suppose.
la source
pos+v*t
et ensuite la comparer aux limites de l'écranif x > 800
et ainsi de suite. L'appel de ces fonctions plusieurs centaines de fois par image s'est avéré être trop lent. C'était quelque chose comme 40 fps à 100% cpu avec du python pur à 60 fps avec 5% -10% quand c'est fait en C.add_bullet
et extrayez tous les champs dont vous avez besoin. Je vais modifier ma réponse.En passant les arguments du mot clé .
la source
Soit utiliser plusieurs arguments de mot-clé dans la définition, soit créer une
Bullet
hiérarchie dont les instances sont passées à la fonction.la source
Je pense que votre exigence de base est d'avoir une syntaxe similaire à C / C ++ en python avec le moins de maux de tête possible. Bien que j'aie aimé la réponse d'Alexander Poluektov, cela ne fonctionne pas pour les cours.
Les éléments suivants devraient fonctionner pour les classes. Il fonctionne en distinguant par le nombre d'arguments non-mot-clé (mais ne prend pas en charge la distinction par type):
Et il peut être utilisé simplement comme ceci:
Production:
la source
Le
@overload
décorateur a été ajouté avec des indices de type (PEP 484). Bien que cela ne change pas le comportement de python, cela facilite la compréhension de ce qui se passe et permet à mypy de détecter les erreurs.Voir: Conseils de type et PEP 484
la source
Je pense qu'une
Bullet
hiérarchie de classes avec le polymorphisme associé est la voie à suivre. Vous pouvez effectivement surcharger le constructeur de la classe de base en utilisant une métaclasse afin que l'appel de la classe de base entraîne la création de l'objet de sous-classe approprié. Voici un exemple de code pour illustrer l'essence de ce que je veux dire.Actualisé
Le code a été modifié pour fonctionner sous Python 2 et 3 pour le garder pertinent. Cela a été fait d'une manière qui évite d'utiliser la syntaxe explicite de métaclasse de Python, qui varie entre les deux versions.
Pour atteindre cet objectif, une
BulletMetaBase
instance de laBulletMeta
classe est créée en appelant explicitement la métaclasse lors de la création de laBullet
classe de base (plutôt qu'en utilisant l'__metaclass__=
attribut class ou via unmetaclass
argument de mot clé selon la version de Python).Production:
la source
Bullet
sous - classes sans avoir à modifier la classe de base ou la fonction d'usine à chaque fois que vous ajoutez un autre sous-type. (Bien sûr, si vous utilisez C plutôt que C ++, je suppose que vous n'avez pas de classes.) Vous pouvez également créer une métaclasse plus intelligente qui détermine par elle-même la sous-classe à créer en fonction du type et / ou du nombre des arguments passés (comme le fait C ++ pour supporter la surcharge).Python 3.8 a ajouté functools.singledispatchmethod
Production
Production:
la source
Utilisez des arguments de mots clés avec des valeurs par défaut. Par exemple
Dans le cas d'une balle droite par rapport à une balle courbe, j'ajouterais deux fonctions:
add_bullet_straight
etadd_bullet_curved
.la source
la surcharge des méthodes est délicate en python. Cependant, il pourrait être utile de passer les variables dict, list ou primitives.
J'ai essayé quelque chose pour mes cas d'utilisation, cela pourrait aider ici à comprendre les gens pour surcharger les méthodes.
Prenons votre exemple:
une méthode de surcharge de classe avec appel aux méthodes de classe différente.
passez les arguments de la classe distante:
OU
Ainsi, la gestion des variables de liste, de dictionnaire ou primitives est en cours de surcharge de méthode.
essayez-le pour vos codes.
la source
Un simple décorateur
Vous pouvez l'utiliser comme ceci
Modifiez-le pour l'adapter à votre cas d'utilisation.
self/this
argument. Cependant, la plupart des langues ne le font que pour l'this
argument. Le décorateur ci-dessus étend l'idée à plusieurs paramètres.Pour clarifier, assumer un langage statique et définir les fonctions
Avec la répartition statique (surcharge), vous verrez deux fois "numéro appelé", car
x
a été déclaré commeNumber
, et c'est tout ce qui compte pour la surcharge. Avec la répartition dynamique, vous verrez "entier appelé, float appelé", car ce sont les types réelsx
au moment où la fonction est appelée.la source
x
pour la répartition dynamique, ni dans quel ordre les deux méthodes ont été appelées pour la répartition statique. Je vous recommande de modifier les relevés d'impression enprint('number called for Integer')
etc.