4 votes

retourner plusieurs valeurs en python sans casser le code précédent

J'avais une version 1 d'une fonction comme :

def f(a,b,c):
    #Some processing
    return some_result

Plus tard, je l'ai mis à niveau vers la version 2.

def f(a,b,c):
    #Some processing
    #Some more processing
    return some_result, additional_result

Cette dernière version renvoie un tuple. Par conséquent, tout le code client utilisant la version 1 devient obsolète. Puis-je obtenir additional_result sur demande ?

C'est-à-dire que vous obtenez additional_result seulement quand vous le demandez alors que vous continuez à obtenir some_result comme si rien n'avait changé.

Une astuce à laquelle j'ai pensé :

def f(a,b,c, need_additional = False):
    #Some processing
    #Some more processing
    if not need_addional:
        return some_result
    else:
        return some_result, additional_result

Rien de mieux ? Ou plus générique ?

10voto

Not_a_Golfer Points 8585

Je pense qu'une solution plus élégante serait de faire de votre ancienne fonction une enveloppe héritée pour votre nouvelle fonction :

def f(a,b,c):
    some_result, additional_result = f_new(a,b,c)
    return some_result

def f_new(a,b,c):
    #Some processing
    #Some more processing
    return some_result, additional_result

Je dois admettre que j'utilise surtout le modèle que vous avez suggéré et non le mien :), mais les arguments par défaut pour la rétrocompatibilité ne sont pas une si bonne pratique.

3voto

Alfe Points 12023

Aucune des solutions proposées n'est parfaite.

  1. Les retours flexibles basés sur des drapeaux (proposition de l'auteur de la demande) sont difficiles à maintenir et rompent toute structure API claire.
  2. Les wrappers hérités (réponse de Dvir Volk) ajouteront de plus en plus de fonctions à votre API et la laisseront donc se développer. De plus, ils ne mettent pas de couvercle sur les choses ; pour toute nouvelle valeur de retour supplémentaire, vous devrez recommencer.
  3. L'amélioration du résultat (réponse de Przemo Li) n'est possible que si vous pouvez contrôler la création du résultat. Si vous voulez renvoyer une valeur qu'une bibliothèque crée pour vous, tout ce que vous pouvez faire est de mettre une enveloppe autour de cette valeur (ce qui poserait des problèmes dès que la classe du résultat changerait à cause des mises à jour de la bibliothèque ou autre).
  4. Il n'est pas toujours possible de faire une coupe franche et de remanier tout le code d'utilisation vers une nouvelle API, par exemple si vos clients maintiennent ce code d'utilisation.
  5. Les fonctions supplémentaires (également dans la réponse de Przemo Li et déclarées KISS) encombreront également votre API.

En fait, bien que cela semble laid, je pense que je préférerais la manière déjà mentionnée de passer des résultats supplémentaires via les paramètres de sortie. Ma façon de le coder serait la suivante :

Vieil appelant :

result = f(a, b, c)
print result

Nouvel appelant :

additionalResult = [0]
result = f(a, b, c, additionalResult)
print result, additionalResult[0]

Callee :

def f(a, b, c, additionalResult=[None]):
    #Some processing
    #Some more processing
    additionalResult[0] = someValue
    return some_result

C'est également une bonne pratique, souvent utilisée en C, et il y a donc de bonnes raisons de croire que cela ne posera pas de pièges que nous aurions pu négliger, même si cela peut sembler laid.

2voto

pcalcao Points 10302

Bien que je ne sois pas un expert de Python, j'implémenterais cela de la manière que vous avez mentionnée, cela me semble être un changement assez simple afin de permettre une compatibilité rétroactive.

2voto

przemo_li Points 1284

Añadir additional_result comme domaine de some_result

Si nécessaire, changez some_result en tant que classe qui hérite de la classe de données qu'elle devrait être, mais avec additional_result . (Ainsi, le code hérité peut simplement ignorer votre "bit supplémentaire").

Ou encore mieux.

Il suffit de créer une fonction distincte qui renvoie plusieurs valeurs. KISS ;)

1voto

Silas Ray Points 11950

Si vous n'avez pas besoin d'utiliser les deux versions dans le même fichier, vous pouvez le contrôler en ayant deux versions du module qui contient la méthode, puis en important uniquement la version que vous voulez dans vos modules d'appel. Ensuite, lorsque vous finissez par remanier votre ancien code, il vous suffit de modifier l'instruction d'importation lorsque vous avez terminé.

Ou bien, utilisez le from x import y as z avec les deux implémentations dans le même module.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X