Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

polymorphisme en Python

+3 votes

Quelles pratiques recommandez-vous pour qu'une fonction puisse prendre un paramètre pouvant avoir plusieurs type ?

En général, j'essaie d'écrire :

class A:
    def do_something():
        ...

class B:
    def do_something():
        ...

def ma_fonction_polymorphe(param):
    param.do_something()

Mais parfois, le contexte ne se prête pas à cette écriture. Je suis alors tenté d'écrire :

def ma_fonction_polymorphe(param):
    if isinstance(param, "A"):
        ...
        param.do_something()
        ...
    else:
        ...
        param.do_something_else()
        ...

Mais je trouve ça vilain, surtout lorsque je sais que, en général, param sera de type A. Je m'oriente alors vers :

def ma_fonction_polymorphe(param):
    try:
        ...
        param.do_something()
        ...
    except AttributeError:
       # le type B n'implémente pas la méthode do_something()
        ...
        param.do_something_else()
        ...

Mais ça ne me satisfait pas vraiment : l'intention n'est pas explicite et, si je veux être précis dans la gestion de l'exception, ça m'oblige à écrire du code en plus. Qu'en pensez-vous ?

demandé 18-Dec-2016 par sobriquet (124 points)

3 Réponses

+1 vote
répondu 18-Dec-2016 par foxmask (2,652 points)
+2 votes

Au hasard, pourquoi ne pas délocaliser ces bouts de code qui changent selon la classe... dans la classe ?

class A:
    def do_something():
        ...

    def do():
        self.do_something()


class B:
    def do_something_else():
        ...

    def do():
        self.do_something_else()

def ma_fonction_polymorphe(param):
    param.do()

En bref, tu écris une interface commune aux classes que tu veux appeler de la même manière. L'avantage direct, c'est que le code qui dépend de la classe est dans sa définition.

Logique, simple, et avec un bon naming ça n'a pas besoin de beaucoup de doc.

C'est, au passage, exactement ce qu'on fait dans un pattern Visiteur.

répondu 18-Dec-2016 par lucas (2,108 points)
+3 votes

Si tu es sur Python 3, tu peux jeter un oeil à functools.singledispatch. La doc devrait aller, mais voici un exemple :

from functools import singledispatch


@singledispatch
def ma_fonction_polymorphe(param):
    ''' Fonction "de base". '''

    print('Basique', type(param), param)


@ma_fonction_polymorphe.register(int)
def _(param):
    ''' La fonction ma_fonction_polymorphe reçoit un entier en entrée. '''

    print(type(param), param)
    print(param)


@ma_fonction_polymorphe.register(list)
@ma_fonction_polymorphe.register(tuple)
def _(param):
    ''' La fonction ma_fonction_polymorphe reçoit une list/tuple en entrée.

        Si on souhaite faire plus générique en voulant accepter tout type
        de séquence, on aurait pu faire seulement :

        from collections.abc import Sequence
        @ma_fonction_polymorphe.register(Sequence)
    '''

    print(type(param), param)
    for item in param:
        print(item)


if __name__ == '__main__':
    ma_fonction_polymorphe(42)
    ma_fonction_polymorphe(list(range(10)))
    ma_fonction_polymorphe(tuple(range(10)))
    ma_fonction_polymorphe(range(10))
répondu 19-Dec-2016 par Tiger-222 (498 points)
edité 19-Dec-2016 par Tiger-222

C'est cool cette feature. J'en étais resté à cette page http://python-3-patterns-idioms-test.readthedocs.io/en/latest/PatternConcept.html merci pour l'exemple

Si ça peut aider, j'ai écrit un petit article un peu plus fouillé : le polymorphisme en Python.

...