web-dev-qa-db-fra.com

Déballage, déballage étendu et déballage étendu imbriqué

Considérez ces expressions ... Soyez patient ... c'est une LONGUE liste ...

(Remarque: certaines expressions sont répétées - il s'agit simplement de présenter un "contexte")

a, b = 1, 2                          # simple sequence assignment
a, b = ['green', 'blue']             # list asqignment
a, b = 'XY'                          # string assignment
a, b = range(1,5,2)                  # any iterable will do


                                     # nested sequence assignment

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z' 

(a,b), c = "XYZ"                     # ERROR -- too many values to unpack
(a,b), c = "XY"                      # ERROR -- need more than 1 value to unpack

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'
(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack


                                     # extended sequence unpacking

a, *b = 1,2,3,4,5                    # a = 1, b = [2,3,4,5]
*a, b = 1,2,3,4,5                    # a = [1,2,3,4], b = 5
a, *b, c = 1,2,3,4,5                 # a = 1, b = [2,3,4], c = 5

a, *b = 'X'                          # a = 'X', b = []
*a, b = 'X'                          # a = [], b = 'X'
a, *b, c = "XY"                      # a = 'X', b = [], c = 'Y'
a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

a, b, *c = 1,2,3                     # a = 1, b = 2, c = [3]
a, b, c, *d = 1,2,3                  # a = 1, b = 2, c = 3, d = []

a, *b, c, *d = 1,2,3,4,5             # ERROR -- two starred expressions in assignment

(a,b), c = [1,2],'this'              # a = '1', b = '2', c = 'this'
(a,b), *c = [1,2],'this'             # a = '1', b = '2', c = ['this']

(a,b), c, *d = [1,2],'this'          # a = '1', b = '2', c = 'this', d = []
(a,b), *c, d = [1,2],'this'          # a = '1', b = '2', c = [], d = 'this'

(a,b), (c, *d) = [1,2],'this'        # a = '1', b = '2', c = 't', d = ['h', 'i', 's']

*a = 1                               # ERROR -- target must be in a list or Tuple
*a = (1,2)                           # ERROR -- target must be in a list or Tuple
*a, = (1,2)                          # a = [1,2]
*a, = 1                              # ERROR -- 'int' object is not iterable
*a, = [1]                            # a = [1]
*a = [1]                             # ERROR -- target must be in a list or Tuple
*a, = (1,)                           # a = [1]
*a, = (1)                            # ERROR -- 'int' object is not iterable

*a, b = [1]                          # a = [], b = 1
*a, b = (1,)                         # a = [], b = 1

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
(a,b), *c = 1,2,3                    # ERROR - 'int' object is not iterable
(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]


                                     # extended sequence unpacking -- NESTED

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

*(a,b) = 1,2                         # ERROR -- target must be in a list or Tuple
*(a,b), = 1,2                        # a = 1, b = 2

*(a,b) = 'XY'                        # ERROR -- target must be in a list or Tuple
*(a,b), = 'XY'                       # a = 'X', b = 'Y'

*(a, b) = 'this'                     # ERROR -- target must be in a list or Tuple
*(a, b), = 'this'                    # ERROR -- too many values to unpack
*(a, *b), = 'this'                   # a = 't', b = ['h', 'i', 's']

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

*(a,*b), = 1,2,3,3,4,5,6,7           # a = 1, b = [2, 3, 3, 4, 5, 6, 7]

*(a,*b), *c = 1,2,3,3,4,5,6,7        # ERROR -- two starred expressions in assignment
*(a,*b), (*c,) = 1,2,3,3,4,5,6,7     # ERROR -- 'int' object is not iterable
*(a,*b), c = 1,2,3,3,4,5,6,7         # a = 1, b = [2, 3, 3, 4, 5, 6], c = 7
*(a,*b), (*c,) = 1,2,3,4,5,'XY'      # a = 1, b = [2, 3, 4, 5], c = ['X', 'Y']

*(a,*b), c, d = 1,2,3,3,4,5,6,7      # a = 1, b = [2, 3, 3, 4, 5], c = 6, d = 7
*(a,*b), (c, d) = 1,2,3,3,4,5,6,7    # ERROR -- 'int' object is not iterable
*(a,*b), (*c, d) = 1,2,3,3,4,5,6,7   # ERROR -- 'int' object is not iterable
*(a,*b), *(c, d) = 1,2,3,3,4,5,6,7   # ERROR -- two starred expressions in assignment


*(a,b), c = 'XY', 3                  # ERROR -- need more than 1 value to unpack
*(*a,b), c = 'XY', 3                 # a = [], b = 'XY', c = 3
(a,b), c = 'XY', 3                   # a = 'X', b = 'Y', c = 3

*(a,b), c = 'XY', 3, 4               # a = 'XY', b = 3, c = 4
*(*a,b), c = 'XY', 3, 4              # a = ['XY'], b = 3, c = 4
(a,b), c = 'XY', 3, 4                # ERROR -- too many values to unpack

Comment comprenez-vous une telle complexité et confusion? Comment on peut toujours avoir raison quand on calcule les résultats de telles expressions à la main. Ou, lorsque je lis le code de quelqu'un d'autre, dois-je simplement l'ignorer et ne jamais essayer de comprendre ce que fait réellement l'expression?

91
treecoder

Mes excuses pour la longueur de ce post, mais j'ai décidé d'opter pour l'exhaustivité.

Une fois que vous connaissez quelques règles de base, il n'est pas difficile de les généraliser. Je ferai de mon mieux pour expliquer avec quelques exemples. Puisque vous parlez d'évaluer ces "à la main", je vous suggère quelques règles de substitution simples. Fondamentalement, vous pourriez trouver plus facile de comprendre une expression si tous les itérables sont formatés de la même manière.

À des fins de déballage uniquement, les substitutions suivantes sont valides sur le côté droit du = (c'est-à-dire pour rvalues):

'XY' -> ('X', 'Y')
['X', 'Y'] -> ('X', 'Y')

Si vous trouvez qu'une valeur n'est pas décompressée, vous annulerez la substitution. (Voir ci-dessous pour plus d'explications.)

De plus, lorsque vous voyez des virgules "nues", faites comme s'il y avait un tuple de niveau supérieur. Faites cela à gauche et à droite (c'est-à-dire pour lvalues et rvalues):

'X', 'Y' -> ('X', 'Y')
a, b -> (a, b)

Avec ces règles simples à l'esprit, voici quelques exemples:

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z'

En appliquant les règles ci-dessus, nous convertissons "XY" à ('X', 'Y'), et couvrir les virgules nues en parens:

((a, b), c) = (('X', 'Y'), 'Z')

La correspondance visuelle ici rend assez évident le fonctionnement de la tâche.

Voici un exemple erroné:

(a,b), c = "XYZ"

En suivant les règles de substitution ci-dessus, nous obtenons ce qui suit:

((a, b), c) = ('X', 'Y', 'Z')

C'est clairement erroné; les structures imbriquées ne correspondent pas. Voyons maintenant comment cela fonctionne pour un exemple un peu plus complexe:

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'

En appliquant les règles ci-dessus, nous obtenons

((a, b), c) = ((1, 2), ('t', 'h', 'i', 's'))

Mais maintenant, il ressort clairement de la structure que 'this' ne sera pas décompressé, mais assigné directement à c. Nous annulons donc la substitution.

((a, b), c) = ((1, 2), 'this')

Voyons maintenant ce qui se passe lorsque nous enveloppons c dans un tuple:

(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack

Devient

((a, b), (c,)) = ((1, 2), ('t', 'h', 'i', 's'))

Encore une fois, l'erreur est évidente. c n'est plus une variable nue, mais une variable à l'intérieur d'une séquence, et donc la séquence correspondante à droite est décompressée dans (c,). Mais les séquences ont une longueur différente, il y a donc une erreur.

Maintenant, pour un déballage étendu en utilisant le * opérateur. C'est un peu plus complexe, mais c'est quand même assez simple. Une variable précédée de * devient une liste, qui contient tous les éléments de la séquence correspondante qui ne sont pas affectés aux noms de variables. En commençant par un exemple assez simple:

a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

Cela devient

(a, *b, c) = ('X', '.', '.', '.', 'Y')

La façon la plus simple d'analyser cela est de travailler à partir des extrémités. 'X' est affecté à a et 'Y' est affecté à c. Les valeurs restantes de la séquence sont placées dans une liste et affectées à b.

Lvaleurs comme (*a, b) et (a, *b) ne sont que des cas particuliers de ce qui précède. Vous ne pouvez pas avoir deux * opérateurs dans une séquence lvalue car elle serait ambiguë. Où iraient les valeurs dans quelque chose comme ça (a, *b, *c, d) - dans b ou c? J'examinerai le cas imbriqué dans un instant.

*a = 1                               # ERROR -- target must be in a list or Tuple

Ici, l'erreur est assez explicite. La cible (*a) doit être dans un tuple.

*a, = (1,2)                          # a = [1,2]

Cela fonctionne parce qu'il y a une virgule nue. Appliquer les règles ...

(*a,) = (1, 2)

Puisqu'il n'y a pas de variables autres que *a, *a accélère toutes les valeurs de la séquence rvalue. Et si vous remplacez le (1, 2) avec une seule valeur?

*a, = 1                              # ERROR -- 'int' object is not iterable

devient

(*a,) = 1

Encore une fois, l'erreur ici s'explique d'elle-même. Vous ne pouvez pas déballer quelque chose qui n'est pas une séquence et *a a besoin de quelque chose à déballer. Nous l'avons donc mis dans une séquence

*a, = [1]                            # a = [1]

Qui est équivalent à

(*a,) = (1,)

Enfin, c'est un point commun de confusion: (1) est le même que 1 - vous avez besoin d'une virgule pour distinguer un Tuple d'une instruction arithmétique.

*a, = (1)                            # ERROR -- 'int' object is not 

Maintenant pour la nidification. En fait, cet exemple n'était pas dans votre section "NESTED"; peut-être ne saviez-vous pas qu'il était imbriqué?

(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]

Devient

((a, b), *c) = (('X', 'Y'), 2, 3)

La première valeur du tuple de niveau supérieur est affectée et les valeurs restantes du tuple de niveau supérieur (2 et 3) sont assignés à c - comme nous devrions nous y attendre.

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

J'ai déjà expliqué ci-dessus pourquoi la première ligne génère une erreur. La deuxième ligne est idiote mais voici pourquoi cela fonctionne:

(*(a, b), c) = (1, 2, 3)

Comme expliqué précédemment, nous travaillons à partir des extrémités. 3 est affecté à c, puis les valeurs restantes sont affectées à la variable avec le * le précédant, dans ce cas, (a, b). C'est donc l'équivalent de (a, b) = (1, 2), qui fonctionne parce qu'il y a le bon nombre d'éléments. Je ne peux penser à aucune raison pour laquelle cela apparaîtrait dans le code de travail. De même,

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

devient

(*(a, *b), c) = ('t', 'h', 'i', 's')

Travailler des extrémités, 's' est affecté à c et ('t', 'h', 'i') est affecté à (a, *b). Travailler à nouveau depuis les extrémités, 't' est affecté à a et ('h', 'i') est affecté à b en tant que liste. Ceci est un autre exemple stupide qui ne devrait jamais apparaître dans le code de travail.

101
senderle

Je trouve le Python 2 Tuple déballage assez simple. Chaque nom à gauche correspond soit à une séquence entière, soit à un seul élément dans une séquence à droite. Si les noms correspondent à des éléments uniques de n'importe quelle séquence , il doit y avoir suffisamment de noms pour couvrir tous les éléments.

Le déballage prolongé, cependant, peut certainement être source de confusion, car il est si puissant. La réalité est que vous ne devriez jamais faire les 10 derniers exemples valides ou plus que vous avez donnés - si les données sont structurées, elles devraient être dans un dict ou une instance de classe, pas des formes non structurées comme des listes.

De toute évidence, la nouvelle syntaxe peut être utilisée abusivement. La réponse à votre question est que vous ne devriez pas lire des expressions comme ça - ce sont de mauvaises pratiques et je doute qu'elles soient utilisées .

Ce n'est pas parce que vous pouvez écrire des expressions arbitrairement complexes que vous le devriez. Vous pouvez écrire du code comme map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables)) mais vous ne pas.

7
agf

Si vous pensez que votre code peut être trompeur, utilisez un autre formulaire pour l'exprimer.

C'est comme utiliser des crochets supplémentaires dans les expressions pour éviter les questions sur la priorité des opérateurs. C'est toujours un bon investissement pour rendre votre code lisible.

Je préfère utiliser le déballage uniquement pour des tâches simples comme l'échange.

3
Michał Šrajer