7 votes

Modules Python recommandés pour la gestion des arguments de fonction ?

Il existe de nombreux modules Python pour analyser et coordonner les options de ligne de commande (argparse, getopt, blargs, etc.). Et Python a la chance de posséder de bonnes fonctionnalités/idiomes intégrés pour gérer des arguments de fonction variés (par exemple, valeurs par défaut, *varargs, **keyword_args). Mais lorsque je lis le code de divers projets pour les fonctions de haut niveau, je constate que les arguments de fonction sont nettement moins disciplinés et standardisés que les arguments de ligne de commande.

Pour les fonctions simples, ce n'est pas un problème ; les fonctions d'arguments intégrées fonctionnent très bien et sont plus que suffisantes. Mais il existe de nombreux modules riches en fonctionnalités dont les fonctions de premier niveau fournissent de nombreux arguments et options différents (certains complémentaires ou exclusifs), différents modes de fonctionnement, des valeurs par défaut, des surcharges, etc. En d'autres termes, ils ont une complexité d'arguments proche de celle des arguments de la ligne de commande. Et ils semblent largement gérer leurs arguments dans ad hoc moyens.

Compte tenu du nombre de modules de traitement en ligne de commande existants, et de leur raffinement au fil du temps, je m'attendais à ce qu'il y ait au moins quelques modules permettant de simplifier la gestion des arguments de fonctions compliquées. Mais j'ai cherché dans PyPi, stackoverflow, et Google sans succès. Alors... y a-t-il des modules de gestion des arguments de fonction (pas de ligne de commande !) que vous recommanderiez ?

--mise à jour avec l'exemple--

Il est difficile de donner un exemple concret vraiment simple, car le cas d'utilisation n'apparaît que lorsque l'on a affaire à un module sophistiqué. Mais voici un essai pour expliquer le problème en code : Un module de formatage avec des valeurs par défaut qui peuvent être surchargées lors de l'instanciation du formatage, ou lorsque la fonction/méthode est appelée. Pour n'avoir que quelques options, il y a déjà beaucoup de verbiage sur la gestion des options et les noms des options sont répétés. ad nauseam .

defaults = { 'indent':     4,
              'prefix':    None,
              'suffix':    None,
              'name':      'aFormatter',
              'reverse':   False,
              'show_name': False
            }

class Formatter(object):

    def __init__(self, **kwargs):
        self.name    = kwargs.get('name',    defaults['name'])
        self.indent  = kwargs.get('indent',  defaults['indent'])
        self.prefix  = kwargs.get('prefix',  defaults['prefix'])
        self.suffix  = kwargs.get('suffix',  defaults['suffix'])
        self.reverse = kwargs.get('reverse', defaults['reverse'])
        self.show_name = kwargs.get('show_name', defaults['show_name'])

    def show_lower(self, *args, **kwargs):
        indent = kwargs.get('indent', self.indent) or 0
        prefix = kwargs.get('prefix', self.prefix) 
        suffix = kwargs.get('suffix', self.suffix)
        reverse = kwargs.get('reverse', self.reverse)
        show_name = kwargs.get('show_name', self.show_name)

        strings = []
        if show_name:
            strings.append(self.name + ": ")
        if indent:
            strings.append(" " * indent)
        if prefix:
            strings.append(prefix)
        for a in args:
            strings.append(a.upper() if reverse else a.lower())
        if suffix:
            strings.append(suffix)
        print ''.join(strings)

if __name__ == '__main__':
    fmt = Formatter()
    fmt.show_lower("THIS IS GOOD")
    fmt.show_lower("THIS", "IS", "GOOD")
    fmt.show_lower('this IS good', reverse=True)
    fmt.show_lower("something!", show_name=True)

    upper = Formatter(reverse=True)
    upper.show_lower("this is good!")
    upper.show_lower("and so is this!", reverse=False)

3voto

senderle Points 41607

Lorsque j'ai lu votre question pour la première fois, je me suis dit que vous demandiez un module de pansement, et qu'il n'existait pas parce que personne ne veut écrire un module qui permet à une mauvaise conception de persister.

Mais je me suis rendu compte que la situation est plus complexe que cela. Le but de la création d'un module tel que celui que vous décrivez est de créer un code réutilisable et général. Maintenant, il se peut très bien que certaines interfaces soient complexes à juste titre. Mais ces interfaces sont précisément les interfaces qui ne peuvent probablement pas être gérées facilement par du code général. Elles sont complexes parce qu'elles traitent d'un domaine problématique comportant de nombreux cas particuliers.

En d'autres termes, si une interface ne peut vraiment pas peut être remaniée, alors elle nécessite probablement beaucoup de code personnalisé et spécial qui n'est pas assez prévisible pour mériter d'être généralisé dans un module. Inversement, si une interface peut facilement être corrigée avec un module du type que vous décrivez, alors elle peut probablement aussi être remaniée -- dans ce cas, elle devrait l'être.

2voto

Sven Marnach Points 133943
  1. Je ne pense pas que l'analyse syntaxique des lignes de commande et le traitement des arguments de fonction aient beaucoup en commun. Le principal problème avec la ligne de commande est que la seule structure de données disponible est une liste plate de chaînes de caractères, et que vous ne disposez pas d'un instrument tel qu'un en-tête de fonction pour définir la signification de chaque chaîne. Dans l'en-tête d'une fonction Python, vous pouvez donner des noms à chacun des paramètres, vous pouvez accepter des conteneurs comme paramètres, vous pouvez définir des valeurs d'arguments par défaut, etc. Ce qu'une bibliothèque d'analyse syntaxique de ligne de commande fait, c'est fournir à la ligne de commande certaines des fonctionnalités que Python offre pour les appels de fonction : donner des noms aux paramètres, attribuer des valeurs par défaut, convertir aux types souhaités, etc. En Python, toutes ces fonctionnalités sont intégrées, vous n'avez donc pas besoin d'une bibliothèque pour atteindre ce niveau de commodité.

  2. En ce qui concerne votre exemple, il existe de nombreuses façons d'améliorer cette conception en utilisant les fonctionnalités offertes par la langue. Vous pouvez utiliser des valeurs d'arguments par défaut au lieu de votre defaults vous pouvez encapsuler tous les drapeaux dans un dictionnaire FormatterConfig et ne passer qu'un seul argument au lieu de tous ces arguments encore et encore. Mais supposons que vous voulez exactement l'interface que vous avez donnée dans le code d'exemple. Une façon d'y parvenir serait le code suivant :

    class Config(dict):
        def __init__(self, config):
            dict.__init__(self, config)
            self.__dict__ = self
    
    def get_config(kwargs, defaults):
        config = defaults.copy()
        config.update(kwargs)
        return Config(config)
    
    class Formatter(object):
    
        def __init__(self, **kwargs):
            self.config = get_config(kwargs, defaults)
    
        def show_lower(self, *args, **kwargs):
            config = get_config(kwargs, self.config)
    
            strings = []
            if config.show_name:
                strings.append(config.name + ": ")
            strings.append(" " * config.indent)
            if config.prefix:
                strings.append(config.prefix)
            for a in args:
                strings.append(a.upper() if config.reverse else a.lower())
            if config.suffix:
                strings.append(config.suffix)
            print "".join(strings)

    Python offre de nombreux outils pour effectuer ce type de gestion des arguments. Ainsi, même si nous décidons de ne pas utiliser certains d'entre eux (comme les arguments par défaut), nous pouvons toujours éviter de nous répéter trop souvent.

1voto

Mike Graham Points 22480

Si votre API est si complexe que vous pensez qu'il serait plus facile d'utiliser un module pour traiter les options qui vous ont été transmises, il y a de fortes chances que l'option réel La solution consiste à simplifier votre API. Le fait que certains modules aient des façons très complexes d'appeler des choses est une honte, pas une fonctionnalité.

0voto

shahjapan Points 4043

C'est du ressort du développeur, mais si vous créez une bibliothèque qui peut être utile pour d'autres projets ou qui sera publiée par d'autres utilisateurs, je pense que vous devez d'abord identifier votre problème et l'analyser,

Documentez bien vos fonctions, il est bon de minimiser le nombre d'arguments, fournir des valeurs par défaut pour les arguments fonctionnels lorsque les utilisateurs peuvent avoir du mal à spécifier ce qui doit être transmis.

et pour certaines exigences complexes, vous pouvez fournir des classmethods qui peut être remplacé pour une programmation avancée ou par des utilisateurs avancés qui veulent réellement réaliser ce qu'ils jouent avec la bibliothèque, l'héritage est toujours présent.

et vous pouvez lire le PEP8 qui peut aussi être utile, mais le but ultime est de spécifier le nombre minimum d'arguments, de restreindre les utilisateurs à entrer les arguments requis, il est bon de fournir des valeurs par défaut pour les arguments optionnels - de la même manière que votre library / code est facilement compréhensible par les développeurs ordinaires aussi...

0voto

Matt Alcock Points 1913

Vous pourriez écrire un code plus générique pour la mise en défaut.

Si vous envisagez de procéder par défaut dans l'autre sens, en passant par les valeurs par défaut et en écrasant les mots-clés s'ils n'existent pas.

 defaults = { 'indent':     4,
          'prefix':    None,
          'suffix':    None,
          'name':      'aFormatter',
          'reverse':   False,
          'show_name': False
        }

class Formatter(object):

   def __init__(self, **kwargs):
      for d,dv in defaults.iteritems():
         kwargs[d] = kwargs.get(d, dv)

Note complémentaire : Je recommanderais d'utiliser les mots-clés args dans la section __init__ définition de la méthode avec les valeurs par défaut. Cela permet à la function devient réellement le contrat pour les autres développeurs et utilisateurs de votre classe ( Formatter )

def __init__(self, indent=4, reverse=False .....etc..... ):

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