¿Cómo uso la sobrecarga de métodos en Python?


169

Estoy tratando de implementar la sobrecarga de métodos en Python:

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

ob=A()
ob.stackoverflow(2)

pero la salida es second method 2; similar:

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

ob=A()
ob.stackoverflow()

da

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

¿Cómo hago para que esto funcione?


39
En Python, piense en los métodos como un conjunto especial de " atributos ", y solo puede haber un " atributo " (y, por lo tanto, un método) de un nombre dado para un objeto. El último método sobrescribe cualquier método anterior. En Java, los métodos no son ciudadanos de primera clase (no son "atributos de objetos"), sino que se invocan al "enviar mensajes" que se resuelven estáticamente en función del tipo más cercano (que es donde entra la sobrecarga ).



66
¿Por qué todavía no se acepta ninguna de las respuestas a esta pregunta? Simplemente haga clic en la marca de verificación que se encuentra a la izquierda de su respuesta favorita ...
glglgl

Respuestas:


150

Es la sobrecarga de métodos, no la anulación de métodos . Y en Python, lo haces todo en una función:

class A:

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

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

No puede tener dos métodos con el mismo nombre en Python, y no es necesario.

Consulte la sección Valores de argumento predeterminados del tutorial de Python. Consulte "Menos asombro" y el argumento predeterminado mutable para ver un error común que debe evitarse.

Editar : Consulte PEP 443 para obtener información sobre las nuevas funciones genéricas de despacho único en Python 3.4.


119
y no es necesario : en mi humilde opinión, a veces sería muy útil tener una sobrecarga de métodos como, por ejemplo, en C ++. Ok, no es 'necesario' en el sentido de que no se puede hacer usando otras construcciones, pero haría algunas cosas más fáciles y simples.
Andreas Florath

77
@AndreasFlorath No estoy de acuerdo. Aprenda a amar la escritura de patos y escriba cada método para que solo haga una cosa, y no haya necesidad de sobrecargar el método.
agf

44
+1 por hacerme leer sobre el "error común a evitar" antes de que me atraparan
mdup

43
Me gustaría estar en desacuerdo un poco;) ... la sobrecarga a menudo hace que el código sea más limpio, porque no empaqueta el método con demasiadas instrucciones if-else para manejar diferentes casos. En cierto sentido, toda la gama de lenguajes funcionales utiliza una idea similar, es decir, coincidencia de patrones de argumentos. Lo que significa que tendría métodos más pequeños y más limpios ... en lugar de métodos gigantes ilegibles.
sten

2
@ user1019129 Ese es el propósito de las funciones genéricas de despacho único agregadas en Python 3.4, y vinculadas en mi respuesta.
agf

62

También puedes 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

66
Creo que esa es la única respuesta válida a la pregunta. Doblaría mi voto si pudiera.
Michael

3
es bueno, pero no funciona en funciones en bruto, solo en métodos dentro de una clase.
Legit Stack

1
@LegitStack Esa funcionalidad también se puede agregar. No es imposible.
Ehsan Keshavarzian

3
@LegitStack Actualicé el código en GitHub, ahora también funciona con funciones.
Ehsan Keshavarzian

1
@PaulPrice Eso es correcto. Actualicé mi respuesta y eliminé la sección de soporte oficial. Todavía puede usar mi código para despachar sobrecargas. Ahora funciona con métodos y funciones. Toma el código de GitHub. No he actualizado PyPi todavía.
Ehsan Keshavarzian

48

En Python, no haces las cosas de esa manera. Cuando las personas hacen eso en lenguajes como Java, generalmente quieren un valor predeterminado (si no lo hacen, generalmente quieren un método con un nombre diferente). Entonces, en Python, puede tener valores predeterminados .

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 puede ver, puede usar esto para desencadenar un comportamiento separado en lugar de simplemente tener un valor predeterminado.

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

2
Principalmente Nonees útil cuando desea un valor predeterminado mutable. El comportamiento separado debe estar en funciones separadas.
AGF

@agf: Nonetambién puede ser útil como un valor predeterminado genuino.
Chris Morgan

Sí, pero me refería a usarlo como un valor centinela, que es cómo lo usa en su respuesta, y como creo que mi comentario deja en claro.
agf

dices "en general"? ¿Estás insinuando que esto no siempre es así?
joel

24

No puedes, nunca necesitas y realmente no quieres.

En Python, todo es un objeto. Las clases son cosas, entonces son objetos. Así son los métodos.

Hay un objeto llamado Aque es una clase. Tiene un atributo llamadostackoverflow . Solo puede tener uno de esos atributos.

Cuando escribe def stackoverflow(...): ..., lo que sucede es que crea un objeto que es el método, y lo asigna al stackoverflowatributo deA . Si escribe dos definiciones, la segunda reemplaza a la primera, de la misma manera que la asignación siempre se comporta.

Además, no desea escribir código que haga el tipo de cosas más salvajes para las que a veces se usa la sobrecarga. Así no es como funciona el lenguaje.

En lugar de tratar de definir una función separada para cada tipo de cosa que se le podría dar (lo cual tiene poco sentido ya que de todos modos no especifica tipos para parámetros de función), deje de preocuparse por qué son las cosas y comience a pensar en lo que pueden hacer .

No solo no puede escribir uno separado para manejar una tupla frente a una lista, sino que tampoco quiere o necesita hacerlo .

Todo lo que debe hacer es aprovechar el hecho de que ambos son, por ejemplo, iterables (es decir, puede escribir for element in container:). (El hecho de que no estén directamente relacionados por herencia es irrelevante).


66
TBH, sería más cuidadoso con "nunca necesito". Esto es algo que se puede etiquetar en cada característica de cualquier mundo real y en un lenguaje de programación completo, y por lo tanto no es un argumento válido. ¿Quién necesita generadores? ¿Quién necesita clases? Los lenguajes de programación son simplemente azúcar sintáctica para algo más concreto.
Sebastian Mach

66
Totalmente en desacuerdo. Puede ser que "nunca lo haya necesitado" o "nunca haya querido", pero hay suficientes aplicaciones donde desesperadamente desea hacerlo. Intente, por ejemplo, escribir un programa que maneje tanto Python como arreglos numpy con gracia sin ensuciar su programa con la instancia de ...
Elmar Zander

1
Según la respuesta de masi, diría que "no puedes" ahora es incorrecto y obsoleto. Basado en la existencia del @overloaddecorador, diría que "realmente no quiero" es discutible, en el mejor de los casos. De PEP-3124, "... actualmente es un antipatrón común para el código Python inspeccionar los tipos de argumentos recibidos ... la 'forma obvia' de hacerlo es mediante inspección de tipo, pero esto es frágil y está cerrado a extensión ... "Entonces parece que suficientes personas quisieran, que se convirtió en parte de Python.
Mike S

@MikeS, el estándar @overloades solo para escribir.
Narfanar

@Narfanar No sé cómo se aplica tu respuesta a mi comentario. ¿Podrías explicar?
Mike S

16

Si bien @agf tenía razón con la respuesta en el pasado ahora con PEP-3124 , obtuvimos nuestro azúcar de sintaxis. Consulte la documentación de mecanografía para obtener detalles sobre@overload decorador, pero tenga en cuenta que esto es realmente solo sintaxis de azúcar y, en mi humilde opinión, esto es todo lo que la gente ha estado discutiendo desde entonces. Personalmente estoy de acuerdo que el tener múltiples funciones con diferentes firmas hace que sea más fácil de leer y luego tener una sola función con más de 20 argumentos todo listo para un valor por defecto ( Nonela mayoría de las veces) y luego tener que tocar el violín alrededor usando un sinfín if, elif, elsecadenas para averiguar qué la persona que llama realmente quiere que nuestra función tenga que ver con el conjunto de argumentos proporcionado. Esto se retrasó mucho después del Python Zen

Hermoso es mejor que feo.

y posiblemente también

Simple es mejor que complejo.

Directamente de la documentación oficial de Python vinculada anteriormente:

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>

exactamente lo que estaba buscando, más limpio que definir un decorador de sobrecarga propio
pcko1

2
por cierto: para cualquiera que se pregunte por qué esto no está funcionando, le sugiero que eche un vistazo a la discusión en stackoverflow.com/questions/52034771/… : @overloadno se supone que las funciones ed tengan una implementación real. Esto no es obvio del ejemplo en la documentación de Python.
masi

15

Escribo mi respuesta en Python 3.2.1.

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

Cómo funciona:

  1. overload toma cualquier cantidad de callables y las almacena en tupla functions , luego devuelve lambda.
  2. La lambda toma cualquier cantidad de argumentos, luego devuelve el resultado de la función de llamada almacenada en functions[number_of_unnamed_args_passed]llamada con los argumentos pasados ​​a la 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

Creo que la palabra que estás buscando es "sobrecargar". No hay sobrecarga de métodos en python. Sin embargo, puede utilizar argumentos predeterminados, de la siguiente manera.

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

Cuando le pase un argumento, seguirá la lógica de la primera condición y ejecutará la primera declaración de impresión. Cuando no le pase argumentos, entrará en la elsecondición y ejecutará la segunda declaración de impresión.


13

Escribo mi respuesta en Python 2.7:

En Python, la sobrecarga de métodos no es posible; Si realmente desea acceder a la misma función con diferentes características, le sugiero que elija la anulación de métodos.

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 sobrecarga no es un concepto aplicado. Sin embargo, si está intentando crear un caso en el que, por ejemplo, desea que se realice un inicializador si se pasa un argumento de tipo fooy otro inicializador para un argumento de tipo bar, ya que todo en Python se maneja como un objeto, puede verificar el nombre del tipo de clase del objeto pasado y el manejo condicional de escritura basado en eso.

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

Este concepto se puede aplicar a múltiples escenarios diferentes a través de diferentes métodos según sea necesario.


7

En Python, haría esto con un argumento predeterminado.

class A:

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

5

Acabo de encontrar este https://github.com/bintoro/overloading.py para cualquiera que pueda estar interesado.

Del archivo Léame del repositorio vinculado:

La sobrecarga es un módulo que proporciona el despacho de funciones en función de los tipos y la cantidad de argumentos de tiempo de ejecución.

Cuando se invoca una función sobrecargada, el despachador compara los argumentos proporcionados con las firmas de funciones disponibles y llama a la implementación que proporciona la coincidencia más precisa.

Caracteristicas

La validación de la función al registrarse y las reglas de resolución detalladas garantizan un resultado único y bien definido en tiempo de ejecución. Implementa el almacenamiento en caché de resolución de funciones para un gran rendimiento. Admite parámetros opcionales (valores predeterminados) en firmas de funciones. Evalúa argumentos posicionales y de palabras clave al resolver la mejor coincidencia. Admite funciones de respaldo y ejecución de código compartido. Soporta argumento polimorfismo. Admite clases y herencia, incluidos métodos de clase y métodos estáticos.


3

Python no admite la sobrecarga de métodos como Java o C ++. Podemos sobrecargar los métodos, pero solo podemos usar el último método definido.

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

Necesitamos proporcionar argumentos opcionales o * args para proporcionar un número diferente de argumentos al llamar.

Cortesía de https://www.geeksforgeeks.org/python-method-overloading/


Esto no se está sobrecargando. Se llama sobrescribir. El último es compatible con Python. El primero se puede implementar con decoradores.
Paebbels

2

Python 3.x incluye una biblioteca de escritura estándar que permite la sobrecarga de métodos con el uso del decorador @overload. Desafortunadamente, esto es para hacer que el código sea más legible, ya que los métodos decorados de sobrecarga necesitarán ser seguidos por un método no decorado que maneje diferentes argumentos. Aquí se puede encontrar más , pero para su ejemplo:

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
El "The" al final de su respuesta me hace pensar que no ha terminado de escribir su respuesta. Por favor, editar su respuesta para completarlo.
Artjom B.

0

En el archivo 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

En el archivo Main.py

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

Podemos sobrecargar el método usando multipledispatch


Esto requiere el uso del paquete multipledispatch ( pypi.org/project/multipledispatch ), que no forma parte del núcleo de python.
Antonio

0

Python agregó el decorador @overload con PEP-3124 para proporcionar azúcar sintáctica para la sobrecarga mediante inspección de tipo, en lugar de simplemente trabajar con la sobrescritura.

Ejemplo de código sobre sobrecarga a través de @overload desde 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

es transformado por @ overload-decorator 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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.