Como uso a sobrecarga de método no Python?


169

Estou tentando implementar a sobrecarga de método em Python:

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

ob=A()
ob.stackoverflow(2)

mas a saída é second method 2; similarmente:

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)

Como eu faço isso funcionar?


39
No Python, pense nos métodos como um conjunto especial de " atributos ", e só pode haver um " atributo " (e, portanto, um método) de um determinado nome para um objeto. O último método substitui os métodos anteriores. Em Java, os métodos não são cidadãos de primeira classe (não são "atributos de objetos"), mas são invocados pelo "envio de mensagens" que são resolvidas estaticamente com base no tipo mais próximo (que é onde a sobrecarga entra).



6
Por que nenhuma das respostas a esta pergunta foi aceita ainda? Basta clicar sobre a marca de verificação outlied à esquerda de sua resposta favorita ...
glglgl

Respostas:


150

É uma sobrecarga de método, não uma substituição de método . E no Python, você faz tudo em uma função:

class A:

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

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

Você não pode ter dois métodos com o mesmo nome em Python - e não precisa.

Consulte a seção Valores do argumento padrão do tutorial do Python. Consulte "Menos espanto" e o Argumento padrão mutável para evitar um erro comum.

Edit : Veja PEP 443 para obter informações sobre as novas funções genéricas de despacho único no Python 3.4.


119
e você não precisa - IMHO em algum momento seria muito útil ter uma sobrecarga de método como, por exemplo, em C ++. Ok, não é 'necessário' no sentido de que não pode ser feito usando outras construções - mas tornaria algumas coisas mais fáceis e mais simples.
Andreas Florath 18/04

7
@AndreasFlorath Eu discordo. Aprenda a amar a digitação com patos e escreva cada método, para que ele faça apenas uma coisa, e não há necessidade de sobrecarga de método.
precisa saber é

4
+1 por me fazer ler sobre o "erro comum para evitar" antes de eu fui pego
mdup

43
Eu gostaria de discordar um pouco;) ... a sobrecarga geralmente torna o código mais limpo, porque você não compacta o método com muitas instruções if-else para lidar com casos diferentes. Em certo sentido, toda a gama de linguagens funcionais usa idéias semelhantes, isto é, correspondência de padrões de argumentos. O que significa que você teria métodos menores e mais limpos. Em vez de métodos gigantes ilegíveis.
sten 28/02

2
@ user1019129 Esse é o objetivo das funções genéricas de despacho único adicionadas no Python 3.4 e vinculadas na minha resposta.
AGF

62

Você também pode usar 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
Eu acho que é a única resposta válida para a pergunta. Eu votaria duas vezes se pudesse.
Michael

3
é bom, mas não funciona em funções brutas, apenas métodos dentro de uma classe.
Legit Stack

1
@ LegitStack Essa funcionalidade também pode ser adicionada. Não é impossível.
Ehsan Keshavarzian

3
@LegitStack Atualizei o código no GitHub, agora ele também funciona com funções.
Ehsan Keshavarzian

1
@PaulPrice Isso mesmo. Atualizei minha resposta e removi a seção de suporte oficial. Você ainda pode usar meu código para despachar sobrecargas. Agora ele funciona com métodos e funções. Pegue o código do GitHub. Ainda não atualizei o PyPi.
Ehsan Keshavarzian

48

No Python, você não faz as coisas dessa maneira. Quando as pessoas fazem isso em linguagens como Java, geralmente desejam um valor padrão (se não o fizerem, geralmente desejam um método com um nome diferente). Portanto, no Python, você pode ter valores padrão .

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'

Como você pode ver, você pode usar isso para acionar um comportamento separado, em vez de apenas ter um valor padrão.

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

2
Principalmente Noneé útil quando você deseja um valor padrão mutável. O comportamento separado deve estar em funções separadas.
perfil completo

@agf: Nonetambém pode ser útil como um valor padrão genuíno.
21812 Chris Morgan

Sim, mas eu estava me referindo a usá-lo como um valor sentinela, que é como você o usa em sua resposta e, como acho que meu comentário deixa claro.
perfil completo

você diz "geralmente"? você está implicando que isso nem sempre é verdade?
joel

24

Você não pode, nunca precisa e realmente não quer.

No Python, tudo é um objeto. Classes são coisas, então são objetos. Então são métodos.

Há um objeto chamado, Aque é uma classe. Ele tem um atributo chamado stackoverflow. Só pode ter um desses atributos.

Quando você escreve def stackoverflow(...): ..., o que acontece é que você cria um objeto que é o método e o atribui ao stackoverflowatributo de A. Se você escrever duas definições, a segunda substitui a primeira, da mesma maneira que a atribuição sempre se comporta.

Além disso, você não deseja escrever um código que faça o tipo mais selvagem de coisas para as quais a sobrecarga às vezes é usada. Não é assim que a linguagem funciona.

Em vez de tentar definir uma função separada para cada tipo de coisa que você poderia receber (o que faz pouco sentido, já que você não especifica tipos para parâmetros de função), pare de se preocupar com o que são as coisas e comece a pensar no que elas podem fazer .

Você não apenas pode escrever um documento separado para lidar com uma tupla versus uma lista, mas também não quer ou precisa .

Tudo o que você faz é aproveitar o fato de que ambos são, por exemplo, iteráveis ​​(ou seja, você pode escrever for element in container:). (O fato de eles não estarem diretamente relacionados por herança é irrelevante.)


6
TBH, eu teria mais cuidado com "nunca precisa". Isso é algo que pode ser marcado em todos os recursos de qualquer mundo real e na linguagem de programação completa e, portanto, não é um argumento válido. Quem precisa de geradores? Quem precisa de aulas? Linguagens de programação são apenas açúcar sintático para algo mais concreto.
Sebastian Mach

6
Discordo completamente. Pode ser que você "nunca precisei" ou "nunca quis", mas existem aplicativos suficientes onde você deseja desesperadamente. Tente por exemplo, escrever um programa que lida com Python e matrizes numpy graciosamente sem desarrumar seu programa com instanceof de ...
Elmar Zander

1
Com base na resposta do masi, eu diria que "você não pode" agora está incorreto e obsoleto. Com base na existência do @overloaddecorador, eu diria que "realmente não quero" é discutível, na melhor das hipóteses. No PEP-3124, "... atualmente é um anti-padrão comum para o código Python inspecionar os tipos de argumentos recebidos ... a 'maneira óbvia' de fazer isso é por inspeção de tipo, mas é frágil e fechado para extensão ... "Portanto, parece que muita gente queria, que se tornou parte do Python.
Mike S

@MikeS, o padrão @overloadé apenas para digitação.
Narfanar

@ Narfanar Não sei como sua resposta se aplica ao meu comentário. Você poderia explicar?
Mike S

16

Enquanto @agf estava certo com a resposta no passado, agora com o PEP-3124 , obtivemos nosso açúcar de sintaxe. Veja a documentação de digitação para obter detalhes sobre o @overloaddecorador, mas observe que isso é realmente apenas açúcar sintático e IMHO é sobre isso que todas as pessoas discutem desde então. Pessoalmente concordo que ter múltiplas funções com assinaturas diferentes torna-lo mais legível, em seguida, ter uma única função com mais de 20 argumentos tudo pronto para um valor padrão ( Nonena maioria das vezes) e depois ter que mexer com intermináveis if, elif, elsecadeias para descobrir o que o chamador realmente quer que nossa função tenha relação com o conjunto de argumentos fornecido. Isso estava muito atrasado após o Python Zen

Bonito é melhor que feio.

e sem dúvida também

Simples é melhor que complexo.

Diretamente da documentação oficial do Python vinculada acima:

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>

exatamente o que eu estava procurando, mais puro do que a definição própria sobrecarga decorador
pcko1

2
btw: para quem quer saber por que isso não está funcionando, sugiro dar uma olhada na discussão em stackoverflow.com/questions/52034771/… - as @overloadfunções ed não devem ter nenhuma implementação real. Isso não é óbvio no exemplo da documentação do Python.
masi 17/03

15

Eu escrevo minha resposta no Python 3.2.1.

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

Como funciona:

  1. overloadpega qualquer quantidade de chamadas e as armazena em tupla functions, depois retorna lambda.
  2. O lambda recebe qualquer quantidade de argumentos e retorna o resultado da função de chamada armazenada em functions[number_of_unnamed_args_passed]chamada com argumentos passados ​​para o 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

Eu acho que a palavra que você está procurando é "sobrecarregar". Não há método de sobrecarga no python. No entanto, você pode usar argumentos padrão, da seguinte maneira.

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

Quando você passa um argumento, ele segue a lógica da primeira condição e executa a primeira instrução de impressão. Quando você não passa argumentos, ele entra na elsecondição e executa a segunda instrução de impressão.


13

Escrevo minha resposta em Python 2.7:

No Python, a sobrecarga de método não é possível; se você realmente deseja acessar a mesma função com recursos diferentes, sugiro que você substitua o método.

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

No Python, sobrecarregar não é um conceito aplicado. No entanto, se você estiver tentando criar um caso em que, por exemplo, você queira que um inicializador seja executado se for passado um argumento do tipo fooe outro inicializador para um argumento do tipo bar, como tudo no Python é tratado como objeto, é possível verificar o nome do tipo de classe do objeto passado e escreva o tratamento condicional com base nisso.

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

Esse conceito pode ser aplicado a vários cenários diferentes através de métodos diferentes, conforme necessário.


7

No Python, você faria isso com um argumento padrão.

class A:

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

5

Acabei de encontrar este https://github.com/bintoro/overloading.py para qualquer pessoa que possa estar interessada.

No leia-me do repositório vinculado:

overloading é um módulo que fornece o despacho de funções com base nos tipos e no número de argumentos de tempo de execução.

Quando uma função sobrecarregada é chamada, o expedidor compara os argumentos fornecidos às assinaturas de funções disponíveis e chama a implementação que fornece a correspondência mais precisa.

Recursos

A validação de função no registro e regras detalhadas de resolução garantem um resultado único e bem definido em tempo de execução. Implementa o cache de resolução de funções para obter um ótimo desempenho. Suporta parâmetros opcionais (valores padrão) nas assinaturas de funções. Avalia os argumentos posicionais e de palavras-chave ao resolver a melhor correspondência. Suporta funções de fallback e execução de código compartilhado. Suporta polimorfismo de argumento. Oferece suporte a classes e herança, incluindo métodos de classe e métodos estáticos.


3

O Python não suporta sobrecarga de métodos como Java ou C ++. Podemos sobrecarregar os métodos, mas podemos usar apenas o método definido mais recente.

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

Precisamos fornecer argumentos opcionais ou * args para fornecer um número diferente de argumentos na chamada.

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


Isso não está sobrecarregando. É chamado de substituição. O último é suportado pelo Python. O primeiro pode ser implementado com decoradores.
Paebbels

2

O Python 3.x inclui uma biblioteca de digitação padrão, que permite a sobrecarga de métodos com o uso do decorador @overload. Infelizmente, isso é para tornar o código mais legível, pois os métodos decorados @overload precisarão ser seguidos por um método não decorado que lide com argumentos diferentes. Pode encontrar mais aqui aqui, mas para o seu exemplo:

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
O "O" no final da sua resposta me faz pensar que você não terminou de escrever sua resposta. Por favor edite sua resposta para completá-lo.
Artjom B.

0

No arquivo 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

No arquivo Main.py

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

Podemos sobrecarregar o método usando multipledispatch


Isso requer o uso do pacote multipledispatch ( pypi.org/project/multipledispatch ), que não faz parte do núcleo do python.
Antony

0

Python adicionou o decorador @overload com o PEP-3124 para fornecer açúcar sintático para sobrecarga via inspeção de tipo - em vez de apenas trabalhar com substituição.

Exemplo de código em sobrecarga 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

é transformado pelo decorador de sobrecarga @ para:

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.