0
votes

Déballage de tuple lors de l'indexation

Cela marche:

from typing import Tuple

NUM_DIMENSIONS = 2  # Might change at a later point in time

# Should be equivalent to Tuple[float ,float]
Result = Tuple[*([float] * NUM_DIMENSIONS)]

def get() -> Result:
    ...

mais ce n'est pas le cas:

import numpy as np

def lookup(a: np.ndarray, coordinates: tuple) -> float:
    return a[*coordinates]

a1 = np.zeros((2, 2))
print(lookup(a1, (0, 1))  # Should print 0

a2 = np.zeros(2, 2, 2))
print(lookup(a2, (0, 0, 1))  # Should print 0

Comment décompresser les tuples lors de l'indexation?

Voici deux exemples où j'aimerais utiliser cette approche, mais je suis plus intéressé à comprendre pourquoi * -unpacking ne semble pas être pris en charge dans l'indexation en général.

x = ['foo', 'bar']
y[*x]  # raises SyntaxError (not NameError!)

ou

x = ['foo', 'bar']
y = [*x]
print(y)  # prints ['foo', 'bar']


6 commentaires

Dans votre deuxième extrait, y n'a pas été introduit comme variable, donc y[*x] ne signifie rien, d'où l'erreur de syntaxe.


Comment pensez-vous que l'indexation fonctionne avec ['foo', 'bar'] ici? L'indexation nécessite un entier individuel, pas une liste de chaînes


Pouvez-vous montrer un exemple avec les sorties souhaitées pour les entrées?


@quamrana: Non, une déclaration de variable manquante déclenche une NameError , pas une SyntaxError . Essayez not_declared[0] .


@Chris_Rands: pour les listes qui peuvent être vraies. Mais les tableaux NumPy, les Pandas DataFrames et typing génériques de typing ont une interface d'indexation beaucoup plus compliquée et supportent des choses comme foo[a, b, c] . Voir mes exemples.


@Chris_Rands J'ai mis à jour mes exemples avec le comportement attendu


3 Réponses :


0
votes

En vous référant à votre exemple:

>> a[[1, 1, 0], [0]]
array([[4, 5],
       [4, 5],
       [0, 1]])

NumPy accepte déjà l'indexation comme a[coordinates] où les coordinates sont un tuple, sans avoir besoin de l'opérateur étoile:

>>> a = np.arange(8).reshape(2, 2, 2)
>>> a[(1, 1, 0)]
6

Et si vous indexez avec des listes, vous obtenez un autre type de comportement utile:

import numpy as np

def lookup(a: np.ndarray, coordinates: tuple) -> float:
    return a[*coordinates]

a1 = np.zeros((2, 2))
print(lookup(a1, (0, 1))

a2 = np.zeros(2, 2, 2))
print(lookup(a2, (0, 0, 1))


1 commentaires

Merci pour votre participation. Comme je l'ai écrit, je suis moins soucieux de faire fonctionner les exemples et plus de comprendre pourquoi mon approche ne fonctionne pas (même si je serais intéressé par une solution de contournement pour mon exemple de typing ).



0
votes

L'indexation de Python a déjà un support intégré pour les tuples en général (pas seulement pour NumPy), il n'est donc pas nécessaire de décompresser ici.

En général, foo[x] est le sucre syntaxique pour le type(foo).__getitem__(foo, x) . Voyons comment cela fonctionne en détail:

x = 1, 2
print(repr(x))  # prints (1, 2)

Si nous indexons dans foo avec une seule valeur, alors il est passé à __getitem__ inchangé, qu'il s'agisse d'un scalaire, d'une liste ou d'un tuple:

foo[0, 1]  # prints (0, 1)

Le cas intéressant est ce qui se passe lorsque nous fournissons plusieurs valeurs directement lors de l'indexation (sans les envelopper dans un tuple ou une liste):

foo[0]       # prints 0
foo[(0, 1)]  # prints (0, 1)
foo[[0, 1]]  # prints [0, 1]

Ainsi, plusieurs valeurs sont automatiquement enveloppées dans un tuple! foo ne peut pas faire la distinction entre foo[0, 1] et foo[(0, 1)] . En effet, dans la grammaire Python , l'index est une expression (ou une tranche, mais cela ne s'applique pas ici) - et dans une expression, a , forme un tuple:

class Foo: 
    def __getitem__(self, key): 
        print(repr(key)) 

foo = Foo()

Par conséquent, l'analyse des arguments dans l'indexation fonctionne différemment des appels de fonction (où les virgules séparent les arguments au lieu de former des tuples).

Donc, dans l'ensemble, il n'est pas nécessaire de décompresser l'itérateur lors de l'indexation. Convertissez simplement votre itérateur en tuple et utilisez-le comme index.


0 commentaires

-1
votes
>>> x = ['foo', 'bar']
>>> print(*x)
foo bar
>>> print(x)
['foo', 'bar']
>>> y[*x]
  File "<stdin>", line 1
    y[*x]
      ^
SyntaxError: invalid syntax
>>> y[foo bar]  #this is y[*x]
  File "<stdin>", line 1
    y[foo bar]
            ^
SyntaxError: invalid syntax

5 commentaires

Votre erreur pour le type(*x) est que x a le mauvais nombre d'éléments. La décompression de l'itérateur dans les arguments de fonction fonctionne bien pour type : type(*[1]) renvoie int . La première partie de votre deuxième exemple est ce que je demande (et vous ne donnez aucune explication ici), et il manque une virgule dans la deuxième partie de votre deuxième exemple.


type (* ['foo', 'bar']) ou type (* [1,2]) vous donnerait l'erreur que j'ai spécifiée. De plus, il n'y a pas de virgule manquante, mais le * x lui-même affiche foo bar plutôt que ['foo', 'bar'] à moins que je ne vous ai mal compris.


Oui, type(*['foo', 'bar']) donnerait également la même erreur, mais je ne vois pas ce que cela a à voir avec la syntaxe de décompression de tuple qui n'est pas prise en charge dans les opérations d'indexation. Comme je l'ai dit, le déballage est bien pris en charge dans les appels de fonction. Concernant la virgule manquante: je ne vois pas en quoi y[foo bar] est censé être une syntaxe Python valide et ce que cela a à voir avec le déballage - votre commentaire this is y[*x] n'a pas beaucoup de sens, car y[*x] n'est pas défini (c'est le sujet de ma question) et s'il était défini, il ne serait sûrement pas étendu à quelque chose de syntaxiquement invalide comme y[foo bar] .


y = [* x], je me suis référé à votre question et y [* x] vous donne l'erreur de syntaxe (comme pour la question), donc mon point d'explication était que y [* x] vous donnera une erreur de syntaxe et vous obtenez cela parce qu'accéder à y [* x] accède à y [foo bar] qui est syntaxiquement incorrecte comme prévu.


Je ne pense pas que " y[*x] accède à y[foo bar] " ait un sens. Que signifie "accéder"? Comme vous pouvez le voir dans ma réponse, il s'agit de savoir comment exactement y[x] désucre en type(y).__getitem__(y, x) et sur quel type d'entité grammaticale est attendue entre [ et ] (par exemple, une expression vs. une liste de paramètres).