Come utilizzo il metodo di overloading in Python?


169

Sto cercando di implementare il sovraccarico del metodo in Python:

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

ob=A()
ob.stackoverflow(2)

ma l'output è second method 2; simile:

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

ob=A()
ob.stackoverflow()

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

Come faccio a farlo funzionare?


39
In Python, pensa ai metodi come un insieme speciale di " attributi ", e può esserci solo un " attributo " (e quindi un metodo) di un dato nome per un oggetto. L'ultimo metodo sovrascrive tutti i metodi precedenti. In Java, i metodi non sono cittadini di prima classe (non sono "attributi di oggetti"), ma sono piuttosto invocati da "invio di messaggi" che vengono risolti staticamente in base al tipo più vicino (che è dove arriva il sovraccarico ).



6
Perché nessuna delle risposte a questa domanda è ancora accettata? Basta fare clic sul segno di spunta esterno a sinistra della risposta preferita ...
glglgl

Risposte:


150

È un metodo che sovraccarica non un metodo prioritario . E in Python, fai tutto in un'unica funzione:

class A:

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

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

Non puoi avere due metodi con lo stesso nome in Python - e non è necessario.

Vedi la sezione Valori argomenti predefiniti dell'esercitazione Python. Vedi "Minimo stupore" e l'argomento predefinito mutevole per un errore comune da evitare.

Modifica : vedere PEP 443 per informazioni sulle nuove funzioni generiche di invio singolo in Python 3.4.


119
e non è necessario - IMHO a volte sarebbe molto utile avere un metodo che sovraccarica come ad esempio in C ++. Ok, non è "necessario", nel senso che non può essere fatto usando altri costrutti, ma renderebbe alcune cose più facili e più semplici.
Andreas Florath,

7
@AndreasFlorath Non sono d'accordo. Impara ad amare la tipizzazione delle anatre e scrivi ogni metodo in modo che faccia solo una cosa, e non è necessario sovraccaricare il metodo.
AGF

4
+1 per avermi fatto leggere "l'errore comune da evitare" prima di essere scoperto
mdup,

43
Vorrei essere in disaccordo un po ';) ... il sovraccarico spesso rende il codice più pulito, perché non comprimete il metodo con troppe istruzioni if-else per gestire casi diversi. In un certo senso l'intera gamma di linguaggi funzionali usa un'idea simile, ad esempio la corrispondenza degli argomenti. Il che significa che avresti metodi più piccoli e più puliti .. piuttosto che giganteschi illeggibili.
Steen,

2
@ user1019129 Questo è lo scopo delle singole funzioni generiche di invio aggiunte in Python 3.4 e collegate nella mia risposta.
agf

62

Puoi anche usare 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
Penso che sia l'unica risposta valida alla domanda. Vorrei raddoppiare il voto se potessi.
Michael,

3
è buono, ma non funziona su funzioni non elaborate, ma solo metodi all'interno di una classe.
Legit Stack

1
@LegitStack Questa funzionalità può anche essere aggiunta. Non è impossibile.
Ehsan Keshavarzian,

3
@LegitStack Ho aggiornato il codice su GitHub, ora funziona anche con le funzioni.
Ehsan Keshavarzian,

1
@PaulPrice Esatto. Ho aggiornato la mia risposta e rimosso la sezione di supporto ufficiale. Puoi ancora usare il mio codice per inviare sovraccarichi. Ora funziona con metodi e funzioni. Prendi il codice da GitHub. Non ho ancora aggiornato PyPi.
Ehsan Keshavarzian,

48

In Python, non fai le cose in questo modo. Quando le persone lo fanno in lingue come Java, generalmente vogliono un valore predefinito (se non lo fanno, generalmente vogliono un metodo con un nome diverso). Quindi, in Python, puoi avere valori predefiniti .

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'

Come puoi vedere, puoi usarlo per innescare un comportamento separato piuttosto che avere semplicemente un valore predefinito.

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

2
Principalmente Noneè utile quando si desidera un valore predefinito modificabile. Il comportamento separato dovrebbe essere in funzioni separate.
AGF

@agf: Nonepuò anche essere utile come un vero valore predefinito.
Chris Morgan,

Sì, ma mi riferivo al suo utilizzo come valore sentinella, che è come lo usi nella tua risposta, e come penso il mio commento chiarisca.
AGF

dici "in generale"? stai insinuando che non è sempre così?
Joel,

24

Non puoi, non devi mai e non vuoi davvero.

In Python, tutto è un oggetto. Le classi sono cose, quindi sono oggetti. Così sono i metodi.

C'è un oggetto chiamato Ache è una classe. Ha un attributo chiamato stackoverflow. Può avere solo uno di questi attributi.

Quando scrivi def stackoverflow(...): ..., succede che crei un oggetto che è il metodo e lo assegni stackoverflowall'attributo di A. Se scrivi due definizioni, la seconda sostituisce la prima, nello stesso modo in cui si comporta sempre il compito.

Inoltre, non si desidera scrivere codice che fa il più selvaggio per il genere di cose per le quali viene talvolta utilizzato il sovraccarico. Non è così che funziona la lingua.

Invece di provare a definire una funzione separata per ogni tipo di cosa che potresti ricevere (il che ha poco senso dal momento che non specifichi comunque i tipi per i parametri della funzione), smetti di preoccuparti di cosa sono e inizia a pensare a cosa possono fare .

Non solo non puoi scriverne uno separato per gestire una tupla rispetto a un elenco, ma non vuoi né ne hai bisogno .

Tutto quello che fai è approfittare del fatto che sono entrambi, ad esempio, iterabili (cioè puoi scrivere for element in container:). (Il fatto che non siano direttamente correlati per eredità è irrilevante.)


6
TBH, starei più attento con "mai necessario". Questo è qualcosa che può essere taggato su ogni caratteristica di qualsiasi mondo reale e turing completo linguaggio di programmazione, e quindi non è un argomento valido. Chi ha bisogno di generatori? Chi ha bisogno di lezioni? I linguaggi di programmazione sono solo zucchero sintattico per qualcosa di più concreto.
Sebastian Mach,

6
Completamente in disaccordo. Può darsi che "non sia mai stato necessario" o "mai voluto", ma ci sono abbastanza applicazioni in cui si desidera disperatamente. Prova ad esempio a scrivere un programma che gestisca con grazia sia le matrici Python che quelle intorpidite senza sporcare il tuo programma con le istanze di ...
Elmar Zander,

1
Sulla base della risposta di Masi, direi che "non puoi" ora è errato e obsoleto. In base all'esistenza del @overloaddecoratore, direi che "non voglio davvero" è discutibile, nella migliore delle ipotesi. Da PEP-3124, "... è attualmente un anti-pattern comune per il codice Python per ispezionare i tipi di argomenti ricevuti ... il" modo ovvio "per farlo è tramite l'ispezione del tipo, ma questo è fragile e chiuso a extension ... "Quindi sembra che un numero sufficiente di persone lo vogliano, diventando parte di Python.
Mike S,

@MikeS, lo standard @overloadè solo per la digitazione.
Narfanar,

@Narfanar Non so come la tua risposta si applica al mio commento. Potresti spiegare?
Mike S,

16

Mentre @agf aveva ragione con la risposta in passato ora con PEP-3124, abbiamo ottenuto il nostro zucchero di sintassi. Vedi la documentazione di battitura per i dettagli sul @overloaddecoratore, ma nota che questo è davvero solo zucchero di sintassi e IMHO questo è tutto ciò di cui le persone hanno discusso da allora. Personalmente sono d'accordo che avere molteplici funzioni con firme diverse rende più leggibile quindi avere una singola funzione con più di 20 argomenti tutto pronto per un valore di default ( Nonela maggior parte del tempo) e poi dover smanettare con infinite if, elif, elsecatene di scoprire cosa il chiamante vuole davvero che la nostra funzione abbia a che fare con l'insieme di argomenti fornito. Ciò era atteso da tempo dopo lo Python Zen

Bello è meglio che brutto.

e probabilmente anche

Semplice è meglio di complesso.

Direttamente dalla documentazione ufficiale di Python collegata sopra:

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>

esattamente quello che stavo cercando, più ordinato che definire il proprio decoratore di overload
pcko1

2
btw: per chiunque si chieda perché questo non funziona, suggerirei di dare un'occhiata alla discussione in stackoverflow.com/questions/52034771/… - le @overloadfunzioni ed non dovrebbero avere alcuna implementazione effettiva. Questo non è ovvio dall'esempio nella documentazione di Python.
masi,

15

Scrivo la mia risposta in Python 3.2.1.

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

Come funziona:

  1. overloadprende qualsiasi quantità di callable e le memorizza in tupla functions, quindi restituisce lambda.
  2. La lambda accetta qualsiasi quantità di argomenti, quindi restituisce il risultato della funzione di chiamata memorizzata nella functions[number_of_unnamed_args_passed]chiamata con argomenti passati alla lambda.

Uso:

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

Penso che la parola che stai cercando sia "sovraccarico". Non esiste alcun metodo di sovraccarico in Python. È comunque possibile utilizzare gli argomenti predefiniti, come indicato di seguito.

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

Quando gli passi un argomento, seguirà la logica della prima condizione ed eseguirà la prima istruzione di stampa. Quando non gli passi argomenti, entrerà nella elsecondizione ed eseguirà la seconda istruzione di stampa.


13

Scrivo la mia risposta in Python 2.7:

In Python, il sovraccarico del metodo non è possibile; se vuoi davvero accedere alla stessa funzione con caratteristiche diverse, ti suggerisco di scegliere il metodo di sostituzione.

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

In Python, il sovraccarico non è un concetto applicato. Tuttavia, se stai cercando di creare un caso in cui, ad esempio, desideri eseguire un inizializzatore se viene passato un argomento di tipo fooe un altro inizializzatore per un argomento di tipo bar, poiché tutto in Python viene gestito come oggetto, puoi controllare il nome del tipo di classe dell'oggetto passato e scrivere la gestione condizionale in base a quello.

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

Questo concetto può essere applicato a più scenari diversi attraverso metodi diversi, se necessario.


7

In Python, lo faresti con un argomento predefinito.

class A:

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

5

Ho appena trovato questo https://github.com/bintoro/overloading.py per chiunque sia interessato.

Dal readme del repository collegato:

il sovraccarico è un modulo che fornisce l'invio di funzioni in base ai tipi e al numero di argomenti di runtime.

Quando viene invocata una funzione sovraccaricata, il dispatcher confronta gli argomenti forniti con le firme di funzione disponibili e chiama l'implementazione che fornisce la corrispondenza più accurata.

Caratteristiche

La convalida della funzione al momento della registrazione e le regole di risoluzione dettagliate garantiscono un risultato unico e ben definito in fase di esecuzione. Implementa la cache di risoluzione delle funzioni per prestazioni eccezionali. Supporta parametri opzionali (valori predefiniti) nelle firme delle funzioni. Valuta gli argomenti posizionali e delle parole chiave quando si risolve la corrispondenza migliore. Supporta funzioni di fallback ed esecuzione di codice condiviso. Supporta il polimorfismo degli argomenti. Supporta classi ed ereditarietà, inclusi metodi di classe e metodi statici.


3

Python non supporta il sovraccarico dei metodi come Java o C ++. Potremmo sovraccaricare i metodi ma possiamo usare solo l'ultimo metodo definito.

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

Dobbiamo fornire argomenti opzionali o * arg per fornire un numero diverso di arg su chiamata.

Per gentile concessione di https://www.geeksforgeeks.org/python-method-overloading/


Questo non è un sovraccarico. Si chiama sovrascrittura. Quello successivo è supportato da Python. Il primo può essere implementato con decoratori.
Paebbels

2

Python 3.x include una libreria di digitazione standard che consente il sovraccarico del metodo con l'uso di @overload decorator. Sfortunatamente, questo serve a rendere il codice più leggibile, poiché i metodi decorati @overload dovranno essere seguiti da un metodo non decorato che gestisca diversi argomenti. Altro può essere trovato qui qui, ma per il tuo esempio:

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
Il "The" alla fine della tua risposta mi fa pensare che non hai finito di scrivere la tua risposta. Si prega di modificare la risposta per completarlo.
Artjom B.

0

Nel file 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

Nel file Main.py

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

Possiamo sovraccaricare il metodo usando multipledispatch


Ciò richiede l'uso del pacchetto multipledispatch ( pypi.org/project/multipledispatch ), che non fa parte del core python.
Antony,

0

Python ha aggiunto il decoratore @overload con PEP-3124 per fornire zucchero sintattico per il sovraccarico tramite l'ispezione del tipo, invece di lavorare semplicemente con la sovrascrittura.

Esempio di codice per sovraccarico tramite @overload da 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

viene trasformato da @ overload-decorator in:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.