209 votes

Nommage forcé des paramètres en Python

En Python, vous pouvez avoir une définition de fonction :

def info(object, spacing=10, collapse=1)

qui peut être appelé de l'une des manières suivantes :

info(odbchelper)                    
info(odbchelper, 12)                
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

grâce à Python qui autorise les arguments dans n'importe quel ordre, à condition qu'ils soient nommés.

Le problème que nous rencontrons est qu'au fur et à mesure que certaines de nos fonctions les plus importantes se développent, des personnes peuvent ajouter des paramètres entre spacing y collapse ce qui signifie que des valeurs erronées peuvent être attribuées à des paramètres qui ne sont pas nommés. En outre, il n'est pas toujours évident de savoir ce qu'il faut entrer. Nous cherchons un moyen de forcer les gens à nommer certains paramètres - pas seulement une norme de codage, mais idéalement un drapeau ou un plugin pydev ?

de sorte que dans les 4 exemples ci-dessus, seul le dernier passe le contrôle car tous les paramètres sont nommés.

Il est probable que nous ne l'activerons que pour certaines fonctions, mais toute suggestion sur la manière de mettre cela en œuvre - ou si c'est même possible - serait appréciée.

378voto

Eli Bendersky Points 82298

Dans Python 3 - Oui, vous pouvez spécifier * dans la liste des arguments.

En documents :

Les paramètres qui suivent "*" ou "*identifier" sont des paramètres à mot-clé uniquement et ne peuvent être transmis qu'avec des arguments de type mot-clé.

def foo(pos, *, forcenamed): ... print(pos, forcenamed) ... foo(pos=10, forcenamed=20) 10 20 foo(10, forcenamed=20) 10 20 foo(10, 20) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: foo() takes exactly 1 positional argument (2 given)

Elle peut également être combinée avec **kwargs :

def foo(pos, *, forcenamed, **kwargs):

Pour compléter l'exemple :

def foo(pos, *, forcenamed ):
    print(pos, forcenamed)

foo(pos=10, forcenamed=20)
foo(10, forcenamed=20)
# basically you always have to give the value!
foo(10)

de la production :

Traceback (most recent call last):
  File "/Users/brando/anaconda3/envs/metalearning/lib/python3.9/site-packages/IPython/core/interactiveshell.py", line 3444, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-12-ab74191b3e9e>", line 7, in <module>
    foo(10)
TypeError: foo() missing 1 required keyword-only argument: 'forcenamed'

Vous êtes donc obligé de toujours donner la valeur. Si vous ne l'appelez pas, vous n'avez pas à faire quoi que ce soit d'autre avec l'argument "forced".

64voto

martega Points 460

Vous pouvez forcer les gens à utiliser des arguments de type mot-clé dans Python3 en définissant une fonction de la manière suivante.

def foo(*, arg0="default0", arg1="default1", arg2="default2"):
    pass

En faisant du premier argument un argument de position sans nom, vous obligez tous ceux qui appellent la fonction à utiliser les arguments par mot-clé, ce qui est, je pense, la question que vous posiez. En Python2, la seule façon de faire cela est de définir une fonction comme ceci

def foo(**kwargs):
    pass

Cela forcera l'appelant à utiliser les kwargs mais ce n'est pas une très bonne solution car il faudrait alors mettre une vérification pour n'accepter que l'argument dont on a besoin.

15voto

Mario Rossi Points 4527

C'est vrai, la plupart des langages de programmation de faire de l'ordre des paramètres une partie du contrat d'appel de la fonction, mais cela n'a pas d'effet sur la qualité de l'appel de fonction. besoin de l'être. Pourquoi en serait-il ainsi ? Ma question est donc de savoir si Python est différent des autres langages de programmation à cet égard. En plus d'autres bonnes réponses pour Python 2, veuillez considérer ce qui suit :

__named_only_start = object()

def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1):
    if _p is not __named_only_start:
        raise TypeError("info() takes at most 3 positional arguments")
    return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse)

La seule façon pour un appelant de fournir des arguments spacing y collapse positionnellement (sans exception) serait :

info(arg1, arg2, arg3, module.__named_only_start, 11, 2)

La convention de ne pas utiliser d'éléments privés appartenant à d'autres modules est déjà très basique en Python. Comme pour Python lui-même, cette convention pour les paramètres ne serait que partiellement appliquée.

Dans le cas contraire, les appels devraient être de la forme :

info(arg1, arg2, arg3, spacing=11, collapse=2)

Un appel

info(arg1, arg2, arg3, 11, 2)

attribuerait la valeur 11 au paramètre _p et une exception soulevée par la première instruction de la fonction.

Caractéristiques :

  • Paramètres avant _p=__named_only_start sont admis de manière positionnelle (ou nominative).
  • Paramètres après _p=__named_only_start doit être fourni par son nom uniquement (à moins que la connaissance de l'objet sentinelle spécial __named_only_start est obtenu et utilisé).

Pour :

  • Les paramètres sont explicites en termes de nombre et de signification (ces derniers si de bons noms sont également choisis, bien entendu).
  • Si la sentinelle est spécifiée comme premier paramètre, tous les arguments doivent être spécifiés par leur nom.
  • Lors de l'appel de la fonction, il est possible de passer en mode positionnel en utilisant l'objet sentinelle __named_only_start dans la position correspondante.
  • On peut s'attendre à de meilleures performances que les autres solutions.

Cons :

  • La vérification a lieu au moment de l'exécution et non de la compilation.
  • Utilisation d'un paramètre supplémentaire (mais pas d'un argument) et d'une vérification supplémentaire. Légère dégradation des performances par rapport aux fonctions normales.
  • La fonctionnalité est un piratage sans support direct de la langue (voir note ci-dessous).
  • Lors de l'appel de la fonction, il est possible de passer en mode positionnel en utilisant l'objet sentinelle __named_only_start dans la bonne position. Oui, cela peut aussi être considéré comme un avantage.

N'oubliez pas que cette réponse n'est valable que pour Python 2. Python 3 met en œuvre le mécanisme similaire, mais très élégant, supporté par le langage, décrit dans d'autres réponses.

J'ai découvert que lorsque j'ouvre mon esprit et que j'y réfléchis, aucune question ou décision d'autrui ne me semble stupide, idiote ou simplement idiote. Bien au contraire : j'apprends généralement beaucoup.

11voto

Bryan Oakley Points 63365

Il ne s'agit pas d'un problème de codage, mais d'un problème social. En créant le code, vous créez un contrat : "donnez-moi ces arguments dans cet ordre et je vous donnerai un résultat". Un jour, quelqu'un choisit de rompre ce contrat. Dans quel but ? Pourquoi un membre de votre équipe pense-t-il qu'il est acceptable d'apporter une modification incompatible avec le passé d'une telle manière ?

10voto

Anthon Points 4119

Vous pouvez le faire d'une certaine manière qui fonctionne à la fois avec Python 2 et Python 3 En effet, il est possible de créer un "faux" premier argument de mot-clé avec une valeur par défaut qui ne se produira pas "naturellement". Ce mot-clé peut être précédé d'un ou de plusieurs arguments sans valeur :

_dummy = object()

def info(object, _kw=_dummy, spacing=10, collapse=1):
    if _kw is not _dummy:
        raise TypeError("info() takes 1 positional argument but at least 2 were given")

Cela permettra :

info(odbchelper)        
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

mais non :

info(odbchelper, 12)                

Si vous changez la fonction en :

def info(_kw=_dummy, spacing=10, collapse=1):

alors tous les arguments doivent avoir des mots-clés et info(odbchelper) ne fonctionnera plus.

Cela vous permettra de positionner des arguments de mots clés supplémentaires à n'importe quel endroit après _kw sans vous obliger à les placer après la dernière entrée. Cela a souvent du sens, par exemple le fait de regrouper les choses de manière logique ou de classer les mots-clés par ordre alphabétique peut faciliter la maintenance et le développement.

Il n'est donc pas nécessaire de revenir à l'utilisation de def(**kwargs) et de perdre les informations relatives à la signature dans votre éditeur intelligent. Votre contrat social consiste à fournir certaines informations, en forçant (certaines d'entre elles) à exiger des mots-clés, l'ordre dans lequel ces informations sont présentées n'est plus pertinent.

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