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

105

Considérez les expressions suivantes. Notez que certaines expressions sont répétées pour présenter le "contexte".

(c'est une longue liste)

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 déduire correctement le résultat de telles expressions à la main?

codeur d'arbres
la source
28
Honnêtement, la plupart d'entre eux sont beaucoup plus compliqués que ce que vous voyez dans le code tous les jours. Apprenez les bases du déballage de listes / tuples et tout ira bien.
Rafe Kettler
2
Notez que ceux-ci sont récursifs. Donc, si vous comprenez les premiers, vous pouvez tout gérer. Essayez de remplacer, par exemple, * (* a, b) par * x, déterminez ce que x décompresse puis rebranchez (* a, b) pour x, etc.
Peteris
4
@greengit Je considère que j'ai une connaissance avancée de Python et je connais juste les règles générales :) Vous n'êtes pas obligé de connaître tous les cas, il vous suffit parfois de lancer un interpréteur et de tester quelque chose.
Rafe Kettler
Wow super liste. Je ne connaissais vraiment pas le a, *b = 1, 2, 3type de déballage. Mais c'est Py3k non?
Niklas R

Réponses:

113

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 de les évaluer «à la main», je vais suggérer quelques règles de substitution simples. Fondamentalement, vous trouverez peut-être 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 valables sur le côté droit de =(c'est-à-dire pour les valeurs r ):

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

Si vous constatez 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 ceci à la fois du côté gauche et du côté droit (c'est-à-dire pour les valeurs l 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 dans parens:

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

La correspondance visuelle ici rend assez évident le fonctionnement de la mission.

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')

Ceci est clairement erroné; les structures imbriquées ne correspondent pas. Voyons maintenant comment cela fonctionne pour un exemple légèrement 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 est clair d'après la structure qui 'this'ne sera pas décompressée, mais assignée directement à c. Nous annulons donc la substitution.

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

Voyons maintenant ce qui se passe lorsque nous enveloppons cun 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. cn'est plus une variable nue, mais une variable à l'intérieur d'une séquence, et ainsi 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 à l'aide de l' *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é à aet 'Y'est affecté à c. Les valeurs restantes de la séquence sont placées dans une liste et affectées àb .

Les valeurs L comme (*a, b)et ne (a, *b)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 ce serait ambigu. Où iraient les valeurs dans quelque chose comme ça (a, *b, *c, d)- dans bou c? Je vais examiner 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 car il y a une virgule nue. Application des règles ...

(*a,) = (1, 2)

Puisqu'il n'y a aucune variable autre que *a, *aglisse toutes les valeurs dans la séquence rvalue. Et si vous remplacez le (1, 2)par une seule valeur?

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

devient

(*a,) = 1

Encore une fois, l'erreur ici est explicite. Vous ne pouvez pas décompresser quelque chose qui n'est pas une séquence et qui a *abesoin de quelque chose à décompresser. Alors on le met dans une séquence

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

Qui est équivalent à

(*a,) = (1,)

Enfin, c'est un point commun de confusion: (1)c'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 ne figurait pas dans votre section "NESTED"; peut-être n'aviez-vous pas réalisé 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 ( 2et 3) sont affectées à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. 3est affecté à c, puis les valeurs restantes sont affectées à la variable avec la *précédente, dans ce cas (a, b),. C'est donc l'équivalent de (a, b) = (1, 2), ce qui fonctionne parce qu'il y a le bon nombre d'éléments. Je ne vois 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 à partir des extrémités, 's'est affecté à cet ('t', 'h', 'i')est affecté à (a, *b). Travailler à nouveau à partir des extrémités, 't'est affecté à aet ('h', 'i')est affecté à b sous forme de liste. Ceci est un autre exemple idiot qui ne devrait jamais apparaître dans le code de travail.

expéditeur
la source
24
Puisque l'OP a donné une longue liste d'exemples, il est normal que vous donniez une longue liste d'explications.
John Y
7

Je trouve le déballage du tuple Python 2 assez simple. Chaque nom sur la gauche correspond à une séquence entière ou à un élément unique 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 étendu, cependant, peut certainement être déroutant, 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 aussi structurées, elles devraient être dans une dictinstance de classe ou une instance de classe, et non dans des formes non structurées comme des listes.

De toute évidence, la nouvelle syntaxe peut être abusée. La réponse à votre question est que vous ne devriez pas avoir à lire des expressions comme celle-là - 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 devriez le faire. Vous pouvez écrire du code comme map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))mais vous ne le faites pas .

agf
la source
Remarque: j'ai écrit du code comme ça, à l'exception de plusieurs niveaux plus complexes. Il était conçu uniquement comme un exercice et réalisé en toute connaissance de cause qu'après trois mois, cela n'aurait aucun sens pour moi et ne serait jamais compréhensible pour personne d'autre. Si je me souviens bien, il a implémenté le point dans le test de polygone, effectué des transformations de coordonnées et créé des SVG, du HTML et du JavaScript.
agf
3

Je pense que votre code peut être trompeur, utilisez une autre forme 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 le swap.

Michał Šrajer
la source