Comment utiliser la surcharge de méthode en Python?


169

J'essaie d'implémenter la surcharge de méthode en Python:

class A:
    def stackoverflow(self):    
        print 'first method'
    def stackoverflow(self, i):
        print 'second method', i

ob=A()
ob.stackoverflow(2)

mais la sortie est second method 2; De même:

class A:
    def stackoverflow(self):    
        print 'first method'
    def stackoverflow(self, i):
        print 'second method', i

ob=A()
ob.stackoverflow()

donne

Traceback (most recent call last):
  File "my.py", line 9, in <module>
    ob.stackoverflow()
TypeError: stackoverflow() takes exactly 2 arguments (1 given)

Comment faire fonctionner cela?


39
En Python, considérez les méthodes comme un ensemble spécial d '" attributs ", et il ne peut y avoir qu'un seul " attribut " (et donc une méthode) d'un nom donné pour un objet. La dernière méthode remplace toutes les méthodes précédentes. En Java, les méthodes ne sont pas des citoyens de première classe (ce ne sont pas des "attributs d'objets"), mais sont plutôt appelées par "l'envoi de messages" qui sont résolus statiquement en fonction du type le plus proche (c'est là qu'intervient la surcharge ).



6
Pourquoi aucune des réponses à cette question n'est-elle encore acceptée? Il suffit de cliquer sur la coche à gauche de votre réponse préférée ...
glglgl

Réponses:


150

C'est une surcharge de méthode et non un remplacement de méthode . Et en Python, vous faites tout cela dans une seule fonction:

class A:

    def stackoverflow(self, i='some_default_value'):    
        print 'only method'

ob=A()
ob.stackoverflow(2)
ob.stackoverflow()

Vous ne pouvez pas avoir deux méthodes avec le même nom en Python - et vous n'en avez pas besoin.

Consultez la section Valeurs d'argument par défaut du didacticiel Python. Voir «Least Astonishment» et l'argument Mutable Default pour une erreur courante à éviter.

Edit : Voir PEP 443 pour plus d'informations sur les nouvelles fonctions génériques de répartition unique dans Python 3.4.


119
et vous n'avez pas besoin de - IMHO parfois, il serait très pratique d'avoir une surcharge de méthode comme par exemple en C ++. Ok, ce n'est pas «nécessaire» dans le sens où cela ne peut pas être fait en utilisant d'autres constructions - mais cela rendrait certaines choses plus faciles et plus simples.
Andreas Florath

7
@AndreasFlorath Je ne suis pas d'accord. Apprenez à aimer la frappe de canard et écrivez chaque méthode pour qu'elle ne fasse qu'une seule chose et qu'il n'y ait pas besoin de surcharge de méthode.
agf

4
+1 pour m'avoir fait lire sur "l'erreur commune à éviter" avant de me faire prendre
mdup

43
Je voudrais être un peu en désaccord;) ... la surcharge rend souvent le code plus propre, car vous n'emballez pas la méthode avec trop d'instructions if-else pour gérer différents cas. En un sens, toute la gamme des langages fonctionnels utilise une idée similaire, c'est-à-dire la mise en correspondance de modèles d'arguments. Ce qui signifie que vous auriez des méthodes plus petites et plus propres ... plutôt que des méthodes géantes illisibles.
sten

2
@ user1019129 C'est le but des fonctions génériques de distribution unique ajoutées dans Python 3.4 et liées dans ma réponse.
agf le

62

Vous pouvez également utiliser pythonlangutil :

from pythonlangutil.overload import Overload, signature

class A:
    @Overload
    @signature()
    def stackoverflow(self):    
        print 'first method'

    @stackoverflow.overload
    @signature("int")
    def stackoverflow(self, i):
        print 'second method', i

6
Je pense que c'est la seule réponse valable à la question. Je voterais deux fois si je pouvais.
Michael

3
c'est bien, mais cela ne fonctionne pas sur les fonctions brutes, juste les méthodes au sein d'une classe.
Legit Stack

1
@LegitStack Cette fonctionnalité peut également être ajoutée. Ce n'est pas impossible.
Ehsan Keshavarzian

3
@LegitStack J'ai mis à jour le code sur GitHub, maintenant cela fonctionne aussi avec des fonctions.
Ehsan Keshavarzian

1
@PaulPrice C'est vrai. J'ai mis à jour ma réponse et supprimé la section d'assistance officielle. Vous pouvez toujours utiliser mon code pour envoyer des surcharges. Il fonctionne maintenant avec les méthodes et les fonctions. Récupérez le code de GitHub. Je n'ai pas encore mis à jour PyPi.
Ehsan Keshavarzian

48

En Python, vous ne faites pas les choses de cette façon. Quand les gens font cela dans des langages comme Java, ils veulent généralement une valeur par défaut (s'ils ne le font pas, ils veulent généralement une méthode avec un nom différent). Ainsi, en Python, vous pouvez avoir des valeurs par défaut .

class A(object):  # Remember the ``object`` bit when working in Python 2.x

    def stackoverflow(self, i=None):
        if i is None:
            print 'first form'
        else:
            print 'second form'

Comme vous pouvez le voir, vous pouvez l' utiliser pour déclencher un comportement séparé plutôt que d'avoir simplement une valeur par défaut.

>>> ob = A()
>>> ob.stackoverflow()
first form
>>> ob.stackoverflow(2)
second form

2
C'est surtout Noneutile lorsque vous souhaitez une valeur par défaut modifiable. Un comportement distinct devrait être dans des fonctions distinctes.
agf

@agf: Nonepeut également être utile comme véritable valeur par défaut.
Chris Morgan

Oui, mais je faisais référence à son utilisation comme valeur sentinelle, c'est ainsi que vous l'utilisez dans votre réponse, et comme je pense que mon commentaire le montre clairement.
agf

vous dites «généralement»? insinuez-vous que ce n'est pas toujours le cas?
joel

24

Vous ne pouvez pas, jamais besoin et ne voulez pas vraiment.

En Python, tout est un objet. Les classes sont des choses, donc ce sont des objets. Les méthodes aussi.

Il y a un objet appelé Aqui est une classe. Il a un attribut appelé stackoverflow. Il ne peut avoir qu'un seul de ces attributs.

Lorsque vous écrivez def stackoverflow(...): ..., ce qui se passe, c'est que vous créez un objet qui est la méthode et que vous l'assignez à l' stackoverflowattribut de A. Si vous écrivez deux définitions, la seconde remplace la première, de la même manière que l'affectation se comporte toujours.

De plus, vous ne voulez pas écrire de code qui fasse le plus fou des choses pour lesquelles la surcharge est parfois utilisée. Ce n'est pas ainsi que fonctionne la langue.

Au lieu d'essayer de définir une fonction distincte pour chaque type de chose qui pourrait vous être donné (ce qui n'a pas de sens puisque vous ne spécifiez pas de types pour les paramètres de fonction de toute façon), arrêtez de vous soucier de ce que sont les choses et commencez à penser à ce qu'elles peuvent faire .

Non seulement vous ne pouvez pas en écrire un distinct pour gérer un tuple par rapport à une liste, mais vous ne voulez pas ou n'en avez pas besoin .

Tout ce que vous faites est de profiter du fait qu'ils sont tous les deux, par exemple, itérables (c'est-à-dire que vous pouvez écrire for element in container:). (Le fait qu'ils ne soient pas directement liés par héritage n'est pas pertinent.)


6
TBH, je serais plus prudent avec "jamais besoin de". C'est quelque chose qui peut être étiqueté sur chaque fonctionnalité de n'importe quel monde réel et un langage de programmation complet, et n'est donc pas un argument valable. Qui a besoin de générateurs? Qui a besoin de cours? Les langages de programmation ne sont que du sucre syntaxique à quelque chose de plus concret.
Sebastian Mach

6
Pas du tout d'accord. Il se peut que vous n'ayez «jamais eu besoin de» ou «que vous n'ayez jamais voulu», mais il y a suffisamment d'applications pour lesquelles vous le voulez désespérément. Essayez par exemple d'écrire un programme qui gère à la fois les tableaux Python et numpy gracieusement sans encombrer votre programme d'instanceof ...
Elmar Zander

1
Sur la base de la réponse de Masi, je dirais que «vous ne pouvez pas» est maintenant incorrect et obsolète. Sur la base de l'existence du @overloaddécorateur, je dirais que "ne pas vraiment vouloir" est discutable, au mieux. D'après PEP-3124, "... c'est actuellement un anti-modèle courant pour le code Python pour inspecter les types d'arguments reçus ... la 'manière évidente' de le faire est par l'inspection de type, mais c'est fragile et fermé à extension ... "Il semble donc que suffisamment de gens le voulaient, que cela soit devenu une partie de Python.
Mike S

@MikeS, la norme @overloadest pour la saisie uniquement.
Narfanar

@Narfanar Je ne sais pas comment votre réponse s'applique à mon commentaire. Pourriez-vous expliquer?
Mike S

16

Alors que @agf avait raison avec la réponse dans le passé maintenant avec PEP-3124, nous avons obtenu notre sucre de syntaxe. Voir la documentation de frappe pour plus de détails sur le @overloaddécorateur, mais notez que ce n'est vraiment que du sucre de syntaxe et à mon humble avis, c'est tout ce que les gens se disputent depuis. Personnellement , je suis d' accord que d' avoir plusieurs fonctions avec des signatures différentes rend plus lisible ayant alors une fonction unique avec plus de 20 arguments tous ensemble à une valeur par défaut ( la Noneplupart du temps), puis avoir à bricoler en utilisant sans fin if, elif, des elsechaînes pour savoir ce l'appelant veut en fait que notre fonction fasse avec l'ensemble d'arguments fourni. C'était attendu depuis longtemps après le Python Zen

Beau est mieux que laid.

et sans doute aussi

Le simple vaut mieux que le complexe.

Directement à partir de la documentation officielle Python liée ci-dessus:

from typing import overload
@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> Tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    <actual implementation>

exactement ce que je cherchais, plus soigné que de définir son propre décorateur de surcharge
pcko1

2
btw: pour quiconque se demande pourquoi cela ne fonctionne pas, je suggère de jeter un œil à la discussion dans stackoverflow.com/questions/52034771/... - les @overloadfonctions ed ne sont pas censées avoir une implémentation réelle. Cela n'est pas évident d'après l'exemple de la documentation Python.
masi

15

J'écris ma réponse en Python 3.2.1.

def overload(*functions):
    return lambda *args, **kwargs: functions[len(args)](*args, **kwargs)

Comment ça fonctionne:

  1. overloadprend n'importe quelle quantité de callables et les stocke dans tuple functions, puis retourne lambda.
  2. Le lambda prend n'importe quelle quantité d'arguments, puis retourne le résultat de l'appel de la fonction stockée dans functions[number_of_unnamed_args_passed]appelé avec des arguments passés au lambda.

Usage:

class A:
    stackoverflow=overload(                    \
        None, \ 
        #there is always a self argument, so this should never get called
        lambda self: print('First method'),      \
        lambda self, i: print('Second method', i) \
    )

14

Je pense que le mot que vous recherchez est «surcharge». Il n'y a pas de surcharge de méthode en python. Vous pouvez cependant utiliser des arguments par défaut, comme suit.

def stackoverflow(self, i=None):
    if i != None:     
        print 'second method', i
    else:
        print 'first method'

Lorsque vous lui passez un argument, il suivra la logique de la première condition et exécutera la première instruction d'impression. Lorsque vous ne lui passez aucun argument, il entre dans la elsecondition et exécute la deuxième instruction d'impression.


13

J'écris ma réponse en Python 2.7:

En Python, la surcharge de méthode n'est pas possible; si vous voulez vraiment accéder à la même fonction avec des fonctionnalités différentes, je vous suggère d'opter pour le remplacement de méthode.

class Base(): # Base class
    '''def add(self,a,b):
        s=a+b
        print s'''

    def add(self,a,b,c):
        self.a=a
        self.b=b
        self.c=c

        sum =a+b+c
        print sum

class Derived(Base): # Derived class
    def add(self,a,b): # overriding method
        sum=a+b
        print sum



add_fun_1=Base() #instance creation for Base class
add_fun_2=Derived()#instance creation for Derived class

add_fun_1.add(4,2,5) # function with 3 arguments
add_fun_2.add(4,2)   # function with 2 arguments

9

En Python, la surcharge n'est pas un concept appliqué. Cependant, si vous essayez de créer un cas où, par exemple, vous voulez qu'un initialiseur soit exécuté si un argument de type est passé fooet un autre initialiseur pour un argument de type baralors, puisque tout en Python est traité comme objet, vous pouvez vérifier le nom du type de classe de l'objet passé et écrivez la gestion conditionnelle en fonction de cela.

class A:
   def __init__(self, arg)
      # Get the Argument's class type as a String
      argClass = arg.__class__.__name__

      if argClass == 'foo':
         print 'Arg is of type "foo"'
         ...
      elif argClass == 'bar':
         print 'Arg is of type "bar"'
         ...
      else
         print 'Arg is of a different type'
         ...

Ce concept peut être appliqué à plusieurs scénarios différents par différentes méthodes selon les besoins.


7

En Python, vous feriez cela avec un argument par défaut.

class A:

    def stackoverflow(self, i=None):    
        if i == None:
            print 'first method'
        else:
            print 'second method',i

5

Je viens de tomber sur https://github.com/bintoro/overloading.py pour quiconque pourrait être intéressé.

Depuis le fichier readme du référentiel lié:

overloading est un module qui fournit une répartition des fonctions en fonction des types et du nombre d'arguments d'exécution.

Lorsqu'une fonction surchargée est appelée, le répartiteur compare les arguments fournis aux signatures de fonction disponibles et appelle l'implémentation qui fournit la correspondance la plus précise.

Caractéristiques

La validation des fonctions lors de l'enregistrement et les règles de résolution détaillées garantissent un résultat unique et bien défini lors de l'exécution. Implémente la mise en cache de la résolution des fonctions pour d'excellentes performances. Prend en charge les paramètres facultatifs (valeurs par défaut) dans les signatures de fonction. Évalue les arguments de position et de mot-clé lors de la résolution de la meilleure correspondance. Prend en charge les fonctions de secours et l'exécution de code partagé. Prend en charge le polymorphisme des arguments. Prend en charge les classes et l'héritage, y compris les méthodes de classe et les méthodes statiques.


3

Python ne prend pas en charge la surcharge de méthodes comme Java ou C ++. Nous pouvons surcharger les méthodes mais ne pouvons utiliser que la dernière méthode définie.

# First sum method.
# Takes two argument and print their sum
def sum(a, b):
    s = a + b
    print(s)

# Second sum method
# Takes three argument and print their sum
def sum(a, b, c):
    s = a + b + c
    print(s)

# Uncommenting the below line shows an error    
# sum(4, 5)

# This line will call the second sum method
sum(4, 5, 5)

Nous devons fournir des arguments optionnels ou * args afin de fournir un nombre différent d'arguments lors de l'appel.

Gracieuseté de https://www.geeksforgeeks.org/python-method-overloading/


Ce n'est pas une surcharge. C'est ce qu'on appelle l'écrasement. Le dernier est pris en charge par Python. Le premier peut être mis en œuvre avec des décorateurs.
Paebbels le

2

Python 3.x inclut une bibliothèque de typage standard qui permet la surcharge de méthodes avec l'utilisation de @overload decorator. Malheureusement, il s'agit de rendre le code plus lisible, car les méthodes décorées @overload devront être suivies d'une méthode non décorée qui gère différents arguments. Vous pouvez en trouver plus ici ici, mais pour votre exemple:

from typing import overload
from typing import Any, Optional
class A(object):
    @overload
    def stackoverflow(self) -> None:    
        print('first method')
    @overload
    def stackoverflow(self, i: Any) -> None:
        print('second method', i)
    def stackoverflow(self, i: Optional[Any] = None) -> None:
        if not i:
            print('first method')
        else:
            print('second method', i)

ob=A()
ob.stackoverflow(2)

1
Le "Le" à la fin de votre réponse me fait penser que vous n'avez pas fini de rédiger votre réponse. Veuillez modifier votre réponse pour la compléter.
Artjom B.

0

Dans le fichier MathMethod.py

from multipledispatch import dispatch
@dispatch(int,int)
def Add(a,b):
   return a+b 
@dispatch(int,int,int)  
def Add(a,b,c):
   return a+b+c 
@dispatch(int,int,int,int)    
def Add(a,b,c,d):
   return a+b+c+d

Dans le fichier Main.py

import MathMethod as MM 
print(MM.Add(200,1000,1000,200))

Nous pouvons surcharger la méthode en utilisant multipledispatch


Cela nécessite l'utilisation du package multipledispatch ( pypi.org/project/multipledispatch ), qui ne fait pas partie du noyau python.
Antony

0

Python a ajouté le décorateur @overload avec PEP-3124 pour fournir du sucre syntaxique pour la surcharge via l'inspection de type - au lieu de simplement travailler avec l'écrasement.

Exemple de code sur la surcharge via @overload de PEP-3124

from overloading import overload
from collections import Iterable

def flatten(ob):
    """Flatten an object to its component iterables"""
    yield ob

@overload
def flatten(ob: Iterable):
    for o in ob:
        for ob in flatten(o):
            yield ob

@overload
def flatten(ob: basestring):
    yield ob

est transformé par le @ overload-decorator en:

def flatten(ob):
    if isinstance(ob, basestring) or not isinstance(ob, Iterable):
        yield ob
    else:
        for o in ob:
            for ob in flatten(o):
                yield ob
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.