L’opérateur splat (l’étoile: *) en Python


L’utilisation du signe étoile (*, dit opérateur « splat ») en Python est très simple, mais certains cas sont peu intuitifs. Les nouveaux venus ont souvent besoin d’un peu plus d’explications que ce que donne la doc. Les utilisateurs d’autres langages sont généralement déroutés, car ils sont habitués à certaines fonctionnalités qu’on ne retrouve pas en Python.

Ce que * ne permet pas de faire

Il n’y a pas de pointeur en Python, et les passages par référence sont automatiques. Du coup :

mon_objet = MaClasse()
mon_pointeur = *mon_objet
ma_valeur = **mon_pointeur

N’existe pas en Python. On ne peut pas récupérer un pointeur. On ne peut pas choisir si l’on passe une variable par valeur ou par référence. Tout est automatique et transparent.

Les usages basiques de *

La multiplication et la puissance fonctionnent comme on l’attend :

>>> print(2*3) # multiplier 2 par 3
6
>>> print(2**3) # élever 2 à la puissance 3
8

Mais déjà, Python se démarque du lot, car l’opérateur * est surchargé par défaut, et peut s’appliquer aux chaines de caractères et aux listes. Pour les chaines, c’est simple :

>>> print("a" * 3) # on peut multiplier une chaîne par un nombre, et cela donne une chaîne
aaa
>>> print("a" ** 3) # ça ne marche pas avec les puissances
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

Pour les listes, c’est plus subtil. Une liste de nombres se multiplie sans y penser :

>>> l = [0, 1, 3]
>>> print(l * 2)
 # on peut multiplier une liste par un nombre, cela donne une liste
[0, 1, 3, 0, 1, 3]
>>> print(l ** 2)
 # ça ne marche pas avec les puissances
TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

En revanche, multiplier une liste d’objets modifiables ne fait que répéter la référence vers cet objet :

>>> l = [{}]
 # on fait une liste contenant un dictionnaire
>>> dicos = l * 3
 # on peut multiplier une liste par un nombre, cela donne une liste
>>> print(dicos)
[{}, {}, {}]

On a l’impression que le comportement est le même que précédemment, en fait pas du tout. Ici on a pas une liste de 3 dictionnaires, mais une liste de 3 références vers le même dictionnaire. Si on modifie le premier élément de la liste, la modification se voit partout :

>>> d = dicos[0] # on récupère ce qu'on croit être le premier dictionnaire
>>> d["Nouvelle cle"] = "Nouvelle valeur" # on le modifie
>>> print(dicos) # afficher la liste montre que les 3 dictionnaires sont en fait un seul et même objet
[{'Nouvelle cle': 'Nouvelle valeur'},
 {'Nouvelle cle': 'Nouvelle valeur'},
 {'Nouvelle cle': 'Nouvelle valeur'}]

Plus pragmatiquement, on peut juste vérifier que c’est la même référence en utilisant la fonction id() :

 >>> id(d[0])
    139656035401544
>>> id(d[1]) # même dico !
    139656035401544
>>> id({}) # dico différent
    139655914013832

Moralité, * sur une liste fait rarement ce qu’on veut.

Unpacking

Python intègre une fonctionnalité, l’unpacking, qui permet de prendre chaque élément d’un itérable et de les attribuer à des variables distinctes, d’un seul coup. C’est un raccourci très pratique :

>>> drapeau = ("bleu", "blanc", "rouge") # ici on utilise un tuple, mais ça marche avec tout itérable
>>> premiere_couleur = drapeau[0]
>>> deuxieme_couleur = drapeau[1]
>>> troisieme_couleur = drapeau[2]
>>> print(premiere_couleur)
'bleu'
>>> print(deuxieme_couleur)
'blanc'
>>> print(troisieme_couleur)
'rouge'
>>> couleur1, couleur2, couleur3 =  drapeau # la même opération, en une ligne grâce à l'unpacking
>>> print(couleur1)
'bleu'
>>> print(couleur2)
'blanc'
>>> print(couleur3)
'rouge'

Vous n’avez rien à faire, l’unpacking est automatique : il suffit de mettre à gauche du signe = le même nombre de variables qu’il y a d’éléments dans la séquence à droite du signe =. Dans le cas contraire, Python râle :

>>> un, deux = drapeau
ValueError: too many values to unpack
>>> un, deux, trois, quatre = drapeau
ValueError: need more than 3 values to unpack

Quel rapport avec * ?

Et bien d’abord, il permet de gérer ce cas où il y a plus d’éléments que de variables en disant « je veux que cette variable contienne le reste »:

couleur1, *autres_couleurs = drapeau
>>> couleur1
    'bleu'
>>> autres_couleurs
    ['blanc', 'rouge']
>>> *autres_couleurs, derniere_couleur = drapeau
>>> autres_couleurs
    ['bleu', 'blanc']
>>> derniere_couleur
    'rouge'

Ensuite, il permet de forcer l’unpacking dans le cas où c’est ambigu.

Faisons une petite fonction de test qui ne fait qu’afficher chacun de ses paramètres :

>>> def afficher_trois_elements(elem1, elem2=None, elem3=None):
...     print(elem1)
...     print(elem2)
...     print(elem3)
...
...
>>> afficher_trois_elements(drapeau)
('bleu', 'blanc', 'rouge')
None
None

Passer drapeau affiche logiquement le tuple comme premier paramètre, et ensuite les valeurs par défaut du premier et du second paramètre.

En utilisant *, nous pouvons forcer l’unpacking de telle sorte que les valeurs du tuple soient passées individuellement comme autant de paramètres :

>>> afficher_trois_elements(*drapeau)
bleu
blanc
rouge

Très pratique quand vous utilisez une collection tout au long du programme pour vous éviter de sans cesse trainer des variables intermédiaires. D’autant que ça marche combiné aux slices :

>>> l = [1, 2, 3, "element que l'on ne veut pas"]
>>> afficher_trois_elements(*l[:-1])
1
2
3

Encore mieux, on peut utiliser ** pour forcer l’unpacking des dictionnaires. Les valeurs du dictionnaire deviennent les valeurs des paramètres, mais cette association se fait par nom : chaque clé du dictionnaire doit correspondre à un nom de paramètre. Ainsi :

>>> elements = {"elem1": "eau", "elem2": "feu", "elem3": "air"}
 # les clés ont le bon nom
>>> afficher_trois_elements(**elements)
eau
feu
air

Si une clé ne possède pas le nom adéquat, tout plante :

>>> elements = {"elem1": "eau", "elem2": "feu", "rien_a_voir": "air"}
>>> afficher_trois_elements(**elements)
TypeError: afficher_trois_elements() got an unexpected keyword argument 'rien_a_voir'

Une autre erreur courante est d’utiliser * avec un dictionnaire. Dans ce cas l’unpacking fonctionne, mais comme itérer sur un dictionnaire donne une liste de clés, c’est comme si vous passiez une liste en paramètres contenant les clés :

>>> elements = {"elem1": "eau", "elem2": "feu", "elem3";: "air"}
>>> afficher_trois_elements(*elements)
elem2
elem3
elem1

Si vous donnez moins de valeurs qu’il n’y a de paramètres, Python remplit tout ce qu’il peut :

>>> afficher_trois_elements(*drapeau[:-1])
bleu
blanc
None
>>> elements = {"elem1": "eau"}
>>> afficher_trois_elements(**elements)
eau
None
None

Dans le cas inverse – si il y a plus d’éléments que de paramètres – Python vous envoie vous brosser :

>>> forces = ("rouge", "bleu", "jaune", "rose", "vert")
>>> afficher_trois_elements(*forces)
TypeError: afficher_trois_elements() takes at most 3 arguments (5 given)

Paramétrage dynamique

Attention, cet usage est souvent confondu avec celui qu’on vient de voir dans la partie précédente. Ils ne font pas du tout la même chose !

Il est parfois pratique de définir une fonction qui accepte un nombre infini de paramètres. Exemple bidon, une fonction qui multiplie ses arguments entre eux :

>>> def multiply(a, b):
...     return a * b # attention, là on utilise <code>*</code> pour multiplier, ne cherchez rien de compliqué ;-)
...
>>> print(multiply(2, 3))
6

Bien sûr, si on veut rajouter un troisième paramètre, il faut la réécrire. Pareil pour un quatrième. Finalement, on finit par demander de passer une liste pour permettre un nombre arbitraire :

>>> def multiply(elements_a_multiplier):
...     res = 1
...     for i in elements_a_multiplier:
...         res = res * i
...     return res
...
>>> multiply((1, 2, 3, 4))
24

Et bien sachez qu’il existe une autre possibilité, autoriser le passage d’une infinité de paramètres ! Cela se fait bien sûr avec *.

>>> def multiply(*tous_les_elements): # on ne change pas grand chose, on rajoute juste <code>*</code>
...     res = 1
...     for i in tous_les_elements:
...         res = res * i
...     return res
...
>>> multiply(1, 2, 3)
 # mais plus besoin d'une séquence !
26
>>> multiply(1, 2, 3, 4, 5)
120

Comment ça marche ? C’est simple, tous les arguments sont automatiquement stockés dans une liste, et cette liste est le paramètre que l’on a désigné par *.

Ce système très puissant peut être utilisé conjointement avec des paramètres normaux :

>>>def afficher(elem1, elem2, *elemx):
...    print(elem1)
...    print(elem2)
...    for e in elemx:
...        print("(*) %s" % e)>>> afficher("Toi", "Moi", "Luke", "Anakin", "Obi Wan", "Robert")
Toi
Moi
(*) Luke
(*) Anakin
(*) Obi Wan
(*) Robert

La seule condition est de mettre * sur un paramètre situé après tous les autres. * est toujours en dernier, et il n’apparait qu’une seule fois. Enfin, il existe une convention pour le nom de cet argument : *args.

Bonne nouvelle, on peut utiliser aussi **. Comme on peut s’y attendre, il permet de récupérer aussi une infinité de paramètres, mais sous forme de dictionnaire. Cela signifie qu’il ne récupère que les paramètres nommés :

>>> def afficher_recette(recette, **ingredients): # ingrédients sera un dictionnaire
...     print(recette)
...     for ingredient in ingredients.items():
...         print(" - %s: %s" % ingredient)
...
>>> afficher_recette("moukraines à la glaviouse",
...                  creme="trop", # on doit donner le nom de ce paramètre
...                  moukraines= "suffisamment",
...                  glaviouse="si disponible") # mais l'ordre des paramètres importe peu
moukraines à la glaviouse
 - glaviouse : si disponible
 - creme : trop
 - moukraines : suffisamment

Il faut également mettre ** après tous les autres arguments. La convention pour nommer ce paramètre est **kwargs, pour « keyword arguments ». Enfin, on peut mélanger tout ça d’un coup :

>>> def affichage_hybride(parametre_normal,
...                       parametre_avec_default="valeur par défaut",
...                       *args,
...                       **kwargs):
...     print(parametre_normal)
...     print(parametre_avec_default)
...     print(args)
...     print(kwargs)
...
>>> affichage_hybride("param1", "param2", "infini1", "infini2", kwinfini1=1, kwinfini2=2)
param1
param2
('infini1', 'infini2')
{'kwinfini1': 1, 'kwinfini2': 2}

On doit absolument mettre les paramètres dans cet ordre :

  1. paramètres normaux et obligatoires;
  2. paramètres normaux facultatifs (valeur par défaut);
  3. paramètres dynamiques;
  4. paramètres dynamiques nommés.

En plus, cela permet en effet de faire jouer les valeurs par défaut de manière très souple :

>>> affichage_hybride("param tout seul")
param tout seul
valeur par défaut
()
{}

Enfin, on peut définir des paramètres qui ne peuvent être passés qu’en spécifiant leur nom. On les appelle les « keyword only parameters ». Pour ce faire, il faut mettre au moins un *, et tout ce qu’il y a après sans étoile ne peut plus être passé comme argument positionnel :

>>> def coooool(normal, *args, keyword_only):
...    print(normal, args, keyword_only)
>>> coooool("yeah", "cool", "man")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: coooool() missing 1 required keyword-only argument: 'keyword_only'
>>> coooool("yeah", "cool", keyword_only="man")
yeah ('cool',) man

Si vous n’avez pas un *args à placer, on peut mettre l’étoile toute seule :

>>> def coooool(normal, *, keyword_only):
...    print(normal, keyword_only)
>>> coooool("yeah", "man")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: coooool() takes 1 positional argument but 2 were given
>>> coooool("yeah", keyword_only="man")
yeah man

Python 3.5

Je mets à jour cet article un mois avant la sortie annoncée de Python 3.5, qui parmi ses nouvelles fonctionnalités, rajoute encore des super pouvoirs à l’opérateur splat.

On peut faire de l’unpacking directement dans les littéraux :

>>> [1, 2, *range(3), *range(2)]
[1, 2, 0, 1, 2, 0, 1]
>>> (*range(1), 4)
(0, 4)
>>> d = {1: 2}
>>> d2 = {3: 4}
>>> {**d, **d2}
{1: 2, 3: 4}

On peut utiliser plusieurs fois l’unpacking des arguments dans un même appel :

>>> def pouet(a, b, c, d):
...    print(a, b, c, d)
>>> couleurs = ('ocre', 'moutarde')
>>> pouet(*couleurs, *range(2)) # double étoile !
ocre moutarde 0 1

Jouons un peu

Si vous vous sentez à l’aise avec tout ça, vous pouvez mélanger plusieurs usages de * d’un coup. Je vous laisse donc en guise de conclusion un petit combo qui utilise un code précédent :

>>> def multiply(*args):
...     res = 1
...     for i in args:
...         res = res * i
...     return res
...
>>> print(multiply(*([2]*6)) == 2**6)
True

42 thoughts on “L’opérateur splat (l’étoile: *) en Python

  • Sam Post author
    (15:40:56) Max: tin je retiendrais jamais les **
    (15:41:55) Sam: t'as pas à tout retenir
    (15:41:59) Sam: juste 2 trucs
    (15:42:27) Max: faut pratiquer
    (15:42:29) Sam: "quand je veux que ma fonction accepte un nombre infini d'arguments, il faut que j'utilise des étoiles quelque part, je vais regarder dans l'article"
    (15:42:32) Sam: et
    (15:43:11) Sam: "quand je veux utiliser le contenu d'une liste ou d'un dico directement comme argument, il faut que j'utilise des étoiles quelques part, je vais regarder dans l'article"
    (15:43:23) Sam: seul le cas d'utilisation est à retenir
    (15:43:27) Sam: le reste, le pourquoi du comment
    (15:43:30) Sam: osef
    (15:44:07) Max: c mieux dit là comme ça :)
    (15:44:37) Sam: je vais coller ce chat en commentaire
    (15:44:41) Sam: ça en aidera d'autres
  • feth

    Joli inventaire :)
    Mes 2 cents: on réécrira avantageusement

    res = res * i

    par

    res *= i

  • fero14041

    Peut-être une typo dans la dernière partie relative à l’unpacking? Le dernier élément du dictionnaire elements est attribué à une clef déjà présente, elem3; il serait peut-être préférable de le mapper à une nouvelle clef, par ex. elem4?

    >>> elements = {"elem1": "eau", "elem2": "feu", "elem3": "air", "elem4": "terre"}
    

    Du coup, Python (2.7.3 et 3.2.3) râle aussi avec les éléments surnuméraires d’un dico:

    >>> afficher_trois_elements(**elements)
    TypeError: afficher_trois_elements() got an unexpected keyword argument 'elem4'
    
    • Sam Post author

      Oui, c’est une typo et une erreur de logique de ma part. Merci de l’avoir signalé.

  • GM

    Petite précision : si le dictionnaire passé en guise de **kw contient une clef qui correspond déjà à un paramètre passé en argument, Python va râler.

    Ca peut arriver quand on utilise des arguments nommés dans une fonction, et plus tard on remplit le dictionnaire dynamiquement.

    >>> drapeau = {“gauche”:”bleu”, “milieu”:”blanc”, “droite”:”rouge”}
    >>> def affiche(gauche=”vert”, *args, **kw):
    … print gauche
    … for a in args:
    … print a
    … for key, value in kw.items():
    … print key, value
    …
    >>> affiche()
    vert
    >>> affiche(**drapeau)
    bleu
    droite rouge
    milieu blanc
    >>> affiche(“marron”, **drapeau)
    Traceback (most recent call last):
    File “”, line 1, in ?
    TypeError: affiche() got multiple values for keyword argument ‘gauche’
  • Recher

    Bonjour,

    petit message d’introduction, car c’est le premier commentaire que je vous envoie : “j’aime bien votre blog”.

    Je sais que c’est pas le sujet de cet article, mais une petite fonction reduce, au lieu de la boucle qui multiplie, c’est plus classe, et plus “pythoniesque”.

    A la place de :
    res = 1
    for i in elements_a_multiplier:
    res = res * i
    return res

    On peut mettre juste une ligne :
    return reduce(lambda x, y: x * y, elements_a_multiplier)

    Mais je suppose que vous connaissiez déjà. Et que vous ne l’avez pas mis pour pas tout embrouiller.

    Et sinon, le “*”, on peut aussi le mettre dans les import. Mais ça c’est le mal absolu, il paraît.

    • Sam Post author

      :-) Vous avez tout compris.

      Cela dit je n’irai pas jusqu’à dire que “import *” c’est le mal, mais il y a très peu de cas ou les bénéfices de son usage dépassent les inconvénients à long terme.

      Je suis formateur, et je ne l’enseigne pas, même à des professionels. Je l’explique uniquement si on me pose la question. En devenant plus chevroné, on apprend tout seul à les rares situations ou c’est pertinent.

      Dans le reste des cas, une petites astuce pour les imports longs: on peut aller à la ligne en utilisant des parenthèses. Ex:

      from module import (machin, bidule, truc, chose, thing, stuff,
                          cosa, abstraction_conceptuelle_neo_generique)
  • Sam Post author

    y a des mecs qui arrives par:

    c’est quoi **kwargs sous python

    Sur d’autres articles du blog. C’est l’article de cette page qui répond à cette question. J’espère que ce petit com donneras un indice à ceux qui cherchent ça.

  • François

    J’ai l’impression que le paragraphe “On doit absolument mettre les paramètres dans cet ordre :” est incomplet.
    Dès qu’on mets un ou des paramètres normaux facultatifs, on ne peut plus mettre *args après. Ca lève un SyntaxError: non-keyword arg after keyword arg
    On est alors obligé de récupérer un dictionnaire (kwarg).
    En résumé :
    fun(a, b, c)
    fun(a, b=1, c=2)
    fun(a, *arg)
    fun(a, b=1, **arg)
    sont valides alors que
    fun(a, b=1, *arg)
    SyntaxError: non-keyword arg after keyword arg

    Dites moi si un truc m’a échappé.

  • Sam Post author

    O_o

    >>> def fun(a, b=1, *arg): pass
    ... 
    >>>

    Marche parfaitement pour moi.

    Quelle est votre version de Python ?

  • Sam Post author

    Dans le snippet fournit, la seule erreur est à la dernière ligne:

    fun3(1, b=42, 4242)

    Python n’autorise pas 4242 après b=42. Ca n’est pas lié à *args ou **kwargs, c’est comme ça pour toutes les fonctions.

  • François

    Tout à fait. Donc, la signature suivante
    fun3(a, b=1, *arg) n’a pas vraiment de sens puisque *arg ne pourra rien recevoir ou bien il faut laisser b à la valeur par défaut si on veut passer des choses à *arg.
    Je ne vois pas comment on peut passer un b différent et des valeurs à *arg…

  • Sam Post author

    La signature dit:

    – j’ai un seul argument obligatoire;
    – j’ai un argument facultatif dont la valeur par défaut est 1;
    – j’accepte un nombre infini d’arguments.

    On peut donc appeler fun3 ainsi:

    – fun3(1)
    – fun3(1, 2)
    – fun3(1, 2, 3, …)

    C’est un signature tout à fait sensée, et souple pour une fonction: elle assure un minimum de deux arguments, force l’utilisateur à décider le premier argument et autorise l’infini.

  • Syl

    Merci les mecs, j’ai enfin compris l’utilité de *args et **kwargs!

    Jusqu’à maintenant, je n’étais tombé que sur des explications plus ou moins bidons, là au moins, c’est clair!

  • Julien

    ENFIN une explication claire, le **kwargs et le *args à la portée du commun des mortels !

  • Policier Moustachu

    Merci pour ce site, vous êtes en train de refaire mon éducation python.

    Une petite précision: l’unpacking ne marche que pour unpacker une liste dans les paramètres d’une fonction.
    On ne peut pas l’utiliser pour unpacker une liste dans une assignation de variable.

    exemple :
    couleurs = [jaune, blanc, bleu]
    plus_de_couleurs = [rouge, *couleurs]

    Il est impossible d’exécuter ce code !
    Tout du moins en Jython. Je n’ai pas vérifié en python.

  • Sam Post author

    On ne peut pas unpacker une liste dans une liste.

    Ce code peut néanmoins être réécrit ainsi:

    plus_de_couleurs = [rouge] + couleurs
  • furankun

    merci pour les explications, et merci pour le premier commentaire, ça décomplexe vachement! Je me sens très proche de Max pour le coup (mais c’est pas vraiment une surprise).

  • lens

    il y a une erreur sur le dernier exemple, element_a_multiplier doit etre remplacer par args

  • Ica

    Y a une erreur je crois dans cette portion de code, au niveau des resultats :

    def multiply(tous_les_elements): # on ne change pas grand chose, on rajoute juste

    ... res = 1

    ... for i in tous_les_elements):

    ... res = res * i

    ... return res

    ...

    multiply(1, 2, 3)

    # mais plus besoin d'une séquence !

    26

    multiply(1, 2, 3, 4, 5)

    120

  • FredWarning

    Franchement chapeau ! top top top !

    vivement un projet pour concevoir une application de A à Z avec reflexion projet, partir d’une page blanche.

    je n’ai pas d’idée mais ça serait vraiment cool.

    en conception OO of course.

    Vous êtes géniaux les mecs!

    Mille Merci!!!

  • Un mec

    Merci pour votre boulot et votre pédagogie les mecs.

    Plus j’avance en Python et plus mes questions atterrissent sur votre site.

    Ca en dit long sur la qualité de ce que vous faites, et c’est pas des trucs où on doit s’enfiler 15 pages de tuto pour avoir une réponse claire et nette. De l’utile donc.

    En plus de ça, le style de rédaction est agréable à lire (je trouve).

    Continuez comme ça c’est super.

  • Romain

    En python 3, on peut aussi faire ça, ce qui peut être intéressant :

    In [1]: l = [1, 2, 3, 4]
    In [2]: head, *tail = l
    In [3]: head
    Out[3]: 1
    In [4]: tail
    Out[4]: [2, 3, 4]
    
  • Xavier Combelle

    Juste un point de détail, les keyword only tuple date de python 3.0 si je ne m’abuse

  • Sam Post author

    @Romain: c’est l’exemple de l’article : couleur1, *autres_couleurs = drapeau

    @Xavier Combelle : en effet, il faut que j’édite l’article.

  • Anne Onyme

    Quelques erreurs dans le texte:

    – “grace à” -> “grâce à”;

    – “Pour se faire” -> “Pour ce faire”;

    – “Je met à jour” -> “Je mets à jour”;

    – “si i il y a” -> “si il y a”.

    Paramètre vs argument (source: http://sametmax.com/la-difference-entre-parametres-et-arguments/ pour les nouveaux venus):

    – “Faisons une petite fonction de test qui ne fait qu’afficher chacun de ses paramètres” -> “Faisons une petite fonction de test qui ne fait qu’afficher chacun de ses arguments” (affiche la valeur passée donc l’argument);

    – “Passer drapeau affiche logiquement le tuple comme premier paramètre, et ensuite les valeurs par défaut du premier et du second paramètre.” -> “Passer drapeau affiche logiquement le tuple comme premier argument, et ensuite les valeurs par défaut du premier et du second argument.” (idem);

    – “soient passées individuellement comme autant de paramètres” -> “soient passées individuellement comme autant d’arguments” (idem);

    – “Les valeurs du dictionnaire deviennent les valeurs des paramètres” -> “Les valeurs du dictionnaire deviennent les valeurs des arguments” (valeur passée donc argument);

    – “c’est comme si vous passiez une liste en paramètres contenant les clés” -> “c’est comme si vous passiez une liste en argument contenant les clés” (idem);

    – “une fonction qui accepte un nombre infini de paramètres” -> “une fonction qui accepte un nombre infini d’arguments”;

    – “si on veut rajouter un troisième paramètre” -> “si on veut rajouter un troisième argument”;

    – “il permet de récupérer aussi une infinité de paramètres” -> “il permet de récupérer aussi une infinité d’arguments” (idem);

    – “il ne récupère que les paramètres nommés” -> “il ne récupère que les arguments nommés”

    En revanche:

    – “chaque clé du dictionnaire doit correspondre à un nom de paramètre”,

    – “Si vous donnez moins de valeurs qu’il n’y a de paramètres”,

    – “C’est simple, tous les arguments sont automatiquement stockés dans une liste, et cette liste est le paramètre que l’on a désigné par *”,

    – “La convention pour nommer ce paramètre est”

    – …

    sont pour moi corrects car on parle ici de la définition de la fonction.

    Bon, si les propositions de corrections “paramètre vs argument” ne vous intéressent pas, merci de me le signaler pour les prochains articles. ;-)

  • Sam Post author

    Merci comme d’habi :) Ouai, t’embête pas avec “arguments vs params”, je vais continuer à utiliser les deux comme des synonymes. L’article est juste pour la culture G.

  • Anne Onyme

    C’est votre blog, c’est vous qui décidez.^^

    Ça m’aura au moins fait pratiquer et donc retenir dans quel cas utiliser lequel (même si y a des cas pour lesquels c’est pas clair).

  • lechevalier denis

    Un commentaire sur le post de Romain .

    Merci à l’avance .

  • Siltaar

    Bonjour,

    Je pense qu’un autre commentaire a déjà tenté de signaler la coquille, mais puisqu’elle est toujours présente :

    multiply(1, 2, 3)

    mais plus besoin d’une séquence !

    26

    1 * 2 * 3 == 6 (et pas 26)

  • Antoine

    J’ai beaucoup apprécié ces explications sur splat.

    En python 3.5.2 (avant je ne sais pas), pour une fonction, on peut avoir tous les paramètres: normaux, *args, keyword_only, **kwargs. Par exemple:

    def myfonct( x, y = 2, *args, z, t = 4, **kwargs ):

    …. print(‘x =’,x, ‘; y =’,y)

    …. print(‘args =’,args)

    …. print(‘z =’,z, ‘; t =’,t)

    …. print(‘kwargs =’,kwargs)

    …. print( “kwargs.get(‘h’,12) -> h =”, kwargs.get(‘h’,12))

    et on peut même appeler myfonct avec des arguments dans le désorde (sauf…):

    myfonct(t=4, w=7, y=2, x=1, z=3, u=5) # sans *args

    myfonct(1, z=3) # sans *args

    myfonct(1, 2, 13,14,15, w=7, z=3, u=5, v=6)

    myfonct(1, 2, [color.rgb.blue], z=3, u=5, v=6, t=4)

    myfonct(x=2, y=1, 13,14,15, z=3, u=5, v=6) # refus

  • Antoine

    Autre usage de * (splat):

    xyz = (1,2,3)

    x,_,z = xyz

    print(‘Python 2.7.12 et 3.5.2: xyz=’, xyz, ‘-> x=’, x,’, z=’,z)

    s = “Marche avec Python 3.5.2 mais pas avec Python 2.7.12 même si from future”

    a,*_,z= s # première et dernière lettre de s

    print(‘s=’, s, ‘\n-> first=’, a, ‘, last=’,z)

  • Antoine

    Autre usage de **kwargs: altération de paramètres par défaut (Python 3.2.2)

    def draw(a, dx = 3, dy = 6, rayon = 1, # arbre, écarts,x/y, rayon (commun)

    nfc=yellow, nec=default, nlc=black, nlfs=12, # noeud, face/edge/label color/fontsize

    ffc=white, fec=default, flc=black, flfs=12, # feuille, face/edge/label color/fontsize

    lc = black, # ligne, color

    fun = lambda a: str(a.label), # traitement racine et string édition

    title = ”, titlec = black, titlefs = 14, # titre, color/fontsize

    frame=False, straight=True): # cadre, vertical (si fils unique)

    draw dessine le graphe d’un arbre (class Arbre), avec des paramètres par défaut, en particulier:

    — la fonction fun qui définit le label de noeud comme chaine d’affichage.

    — le booleen straigth qui indique si le trait père-fils unique doit être vertical

    Pour un arbre binaire de recherche (class ABR, descendant de Arbre), il y a dans les noeuds, en plus du label, l’information de clé (key) et on souhaite avoir, par défaut,

    — fun qui donne la valeur de la clé (en plus ou à la place du label).

    — straitgth = False pour que, même en cas de fils unique, le fils gauche soit à gauche, le fils droit à droite.

    D’où une spécialisation de draw, avec des valeurs par défaut adaptée aux ABR:

    def drawABR(a, **kwargs):

    “””Appelle draw, avec les mêmes paramètres et valeurs par défaut, sauf pour

    fun et straight dont les valeurs par défaut sont modifiées”””

    def abrfun(t):

    if t.isleaf(): return str(t.key) + “\n” +str(t.label) # feuille (noeud terminal)

    else: return str(t.key) # noeud interne

    if not ‘fun’ in kwargs: kwargs[‘fun’] = abrfun

    if not ‘straight’ in kwargs: kwargs[‘straight’] = False

    draw(a, **kwargs)

  • Antoine

    Désolé pour le non décalage des instructions dans le post précédent (Autre usage de **kwargs). Post à supprimer et je renvoie un post + lisible.

  • Antoine

    Autre usage de **kwargs: altération de paramètres par défaut (Python 3.5.2).

    def draw(a, dx = 3, dy = 6, rayon = 1,

    …nfc = yellow, nec = default, nlc = black, nlfs = 12,

    …ffc = white, fec = default, flc = black, flfs = 12,

    …lc = black,

    …fun = lambda a: str(a.label),

    …title = ”, titlec = black, titlefs = 14,

    …frame = False, straight = True):

    Pour info, les paramètres ont le sens suivant (pour chaque ligne):

    —># arbre, écarts,x/y, rayon (commun)

    —># noeud, face/edge/label color/fontsize

    —># feuille, face/edge/label color/fontsize

    —># ligne, color

    —># traitement racine et string édition

    —># titre, color/fontsize

    —># cadre, vertical (si fils unique)

    draw dessine le graphe d’un arbre (class Arbre), avec des paramètres par défaut, en particulier:

    …– la fonction fun qui définit le label de noeud comme chaine d’affichage.

    …– le booleen straigth qui indique si le trait père-fils unique doit être vertical

    Pour un arbre binaire de recherche (class ABR, descendant de Arbre), il y a dans les noeuds, en plus du label, l’information de clé (key) et on souhaite avoir, par défaut,

    …– fun qui donne la valeur de la clé (en plus ou à la place du label).

    …– straitgth = False pour que, même en cas de fils unique, le fils gauche soit à gauche, le fils droit à droite.

    D’où une spécialisation de draw, avec des valeurs par défaut adaptée aux ABR:

    def drawABR(a, **kwargs):

    …”””Appelle draw, mêmes paramètres et valeurs par défaut, sauf pour

    …fun et straight dont les valeurs par défaut sont modifiées”””

    …def abrfun(t):

    …….if t.isleaf():

    ………..return str(t.key) + “\n” +str(t.label) # feuille (noeud terminal)

    …….else: return str(t.key) # noeud interne

    …if not ‘fun’ in kwargs: kwargs[‘fun’] = abrfun

    …if not ‘straight’ in kwargs: kwargs[‘straight’] = False

    …draw(a, **kwargs)

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.