web-dev-qa-db-fra.com

Y a-t-il un besoin de portée (len (a))?

On trouve fréquemment des expressions de ce type dans les questions python sur SO. Soit juste pour accéder à tous les éléments de l'itérable

for i in range(len(a)):
    print(a[i])

Ce qui n’est qu’une manière lourde d’écrire:

for e in a:
    print(e)

Ou pour assigner aux éléments de l'itérable:

for i in range(len(a)):
    a[i] = a[i] * 2

Qui devrait être le même que:

for i, e in enumerate(a):
     a[i] = e * 2
# Or if it isn't too expensive to create a new iterable
a = [e * 2 for e in a]

Ou pour filtrer les index:

for i in range(len(a)):
    if i % 2 == 1: continue
    print(a[i])

Ce qui pourrait être exprimé comme ceci:

for e in a [::2]:
    print(e)

Ou quand vous avez juste besoin de la longueur de la liste, et non de son contenu:

for _ in range(len(a)):
    doSomethingUnrelatedToA()

Ce qui pourrait être:

for _ in a:
    doSomethingUnrelatedToA()

En python, nous avons enumerate, slicing, filter, sorted, etc ... Comme les constructions python for sont destinées à itérer sur des itérables et pas seulement des plages d'entiers, existe-t-il des cas d'utilisation réels dans lesquels vous avez besoin de in range(len(a))?

54
Hyperboreus

Si vous devez utiliser les index d'une séquence, alors oui - vous l'utilisez ... par exemple pour l'équivalent de numpy.argsort ...:

>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]
8
Jon Clements

Réponse courte: mathématiquement, non, concrètement oui, par exemple pour la programmation intentionnelle.

Techniquement, je pense que la réponse mathématiquement correcte serait "non, ce n'est pas nécessaire" car il est exprimable en utilisant d'autres constructions, c'est-à-dire qu'il est équivalent à d'autres constructions ... quelque chose comme si un langage est complet, il ne le fait pas. Il importe vraiment de savoir quelle construction syntaxique/paradigmatique il a, car tout peut être exprimé de toute façon.

Mais dans la pratique, j'utilise for i in range(len(a) (ou for _ in range(len(a)) si je n'ai pas besoin de l'index) pour indiquer explicitement que je souhaite itérer autant de fois qu'il y a d'éléments dans une séquence sans avoir besoin de les utiliser dans quoi que ce soit.

Donc, pour répondre à la "Y a-t-il un besoin?" La partie —I un besoin exprime le sens/l'intention du code à des fins de lisibilité.

Voir aussi: https://en.wikipedia.org/wiki/Programming_Intentional

P.S. mais en même temps, ce qui suit semble être sémantiquement équivalent, d’un point de vue programmation intentionnelle:

for _ in a:
    ...

ou

b = ["hello" for _ in a]

... dans l’ensemble, je suppose que la différence est de savoir si vous voulez être vraiment explicite à propos de "répéter AS BEAUCOUP DE TEMPS car il existe des éléments dans a" par opposition à "pour chaque élément dans a, quel que soit le paramètre choisi. le contenu de a " ... donc juste une nuance de programmation intentionnelle à la fin.

6
Erik Allik

Que faire si vous devez accéder à deux éléments de la liste simultanément?

for i in range(len(a[0:-1])):
    something_new[i] = a[i] * a[i+1]

Vous pouvez utiliser ceci, mais c'est probablement moins clair:

for i, _ in enumerate(a[0:-1]):
     something_new[i] = a[i] * a[i+1]

Personnellement, je ne suis pas 100% satisfait non plus!

5
Giswok

J'ai un cas d'utilisation que je crois qu'aucun de vos exemples ne couvre.

boxes = [b1, b2, b3]
items = [i1, i2, i3, i4, i5]
for j in range(len(boxes)):
    boxes[j].putitemin(items[j])

Je suis relativement nouveau sur Python, mais très heureux d’apprendre une approche plus élégante.

2
Jim

Parfois, matplotlib nécessite range(len(y)), par exemple, alors que y=array([1,2,5,6]), plot(y) fonctionne bien, mais pas scatter(y). Il faut écrire scatter(range(len(y)),y). (Personnellement, je pense qu'il s'agit d'un bogue dans scatter; plot et ses amis scatter et stem devraient utiliser autant que possible les mêmes séquences d'appel.)

2
Charles Boncelet

En me basant sur les commentaires et l'expérience personnelle, je dis non, il n'y a pas de besoin pour range(len(a)). Tout ce que vous pouvez faire avec range(len(a)) peut être fait d’une autre manière (généralement bien plus efficace).

Vous avez donné de nombreux exemples dans votre message, je ne les répéterai donc pas ici. Au lieu de cela, je donnerai un exemple à ceux qui disent: "Et si je veux juste la longueur de a, pas les éléments?". C'est l'une des seules fois où vous pourriez envisager d'utiliser range(len(a)). Cependant, même ceci peut être fait comme suit:

>>> a = [1, 2, 3, 4]
>>> for _ in a:
...     print True
...
True
True
True
True
>>>

Clements answer (comme indiqué par Allik) peut également être retravaillé pour supprimer range(len(a)):

>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]
>>> # Note however that, in this case, range(len(a)) is more efficient.
>>> [x for x, _ in sorted(enumerate(a), key=lambda i: i[1])]
[2, 3, 1, 5, 4, 0]
>>>

Donc, en conclusion, range(len(a)) n'est pas nécessaire. Son seul avantage est la lisibilité (son intention est claire). Mais ce n'est que préférence et style de code.

2
iCodez

C'est bien d'avoir quand vous avez besoin d'utiliser l'index pour une sorte de manipulation et que l'élément actuel ne suffit pas. Prenons par exemple un arbre binaire qui est stocké dans un tableau. Si vous avez une méthode qui vous demande de renvoyer une liste de n-uplets contenant chaque noeud aux enfants directs, vous avez besoin de l'index.

#0 -> 1,2 : 1 -> 3,4 : 2 -> 5,6 : 3 -> 7,8 ...
nodes = [0,1,2,3,4,5,6,7,8,9,10]
children = []
for i in range(len(nodes)):
  leftNode = None
  rightNode = None
  if i*2 + 1 < len(nodes):
    leftNode = nodes[i*2 + 1]
  if i*2 + 2 < len(nodes):
    rightNode = nodes[i*2 + 2]
  children.append((leftNode,rightNode))
return children

Bien sûr, si l'élément sur lequel vous travaillez est un objet, vous pouvez simplement appeler une méthode get children. Mais oui, vous n'avez vraiment besoin de l'index que si vous faites une sorte de manipulation.

2
CleoR

Parfois, vous êtes vraiment ne vous souciez pas de la collection elle-même. Par exemple, créer une ligne d'ajustement de modèle simple pour comparer une "approximation" aux données brutes:

fib_raw = [1, 1, 2, 3, 5, 8, 13, 21] # Fibonacci numbers

phi = (1 + sqrt(5)) / 2
phi2 = (1 - sqrt(5)) / 2

def fib_approx(n): return (phi**n - phi2**n) / sqrt(5)

x = range(len(data))
y = [fib_approx(n) for n in x]

# Now plot to compare fib_raw and y
# Compare error, etc

Dans ce cas, les valeurs de la séquence de Fibonacci elle-même n'étaient pas pertinentes. Tout ce dont nous avions besoin ici était la taille de la séquence d’entrée avec laquelle nous comparions.

1
Mateen Ulhaq

Mon code est:

s=["9"]*int(input())
for I in range(len(s)):
    while not set(s[I])<=set('01'):s[i]=input(i)
print(bin(sum([int(x,2)for x in s]))[2:])

C'est un additionneur binaire mais je ne pense pas que la plage len ou l'intérieur puisse être remplacée pour la rendre plus petite/meilleure.

0
Matt GSM MattGSM

Si vous devez parcourir les premiers éléments len(a) d'un objet b (qui est supérieur à a), vous devriez probablement utiliser range(len(a)):

for i in range(len(a)):
    do_something_with(b[i])
0
alexpirine

Exemple très simple:

def loadById(self, id):
    if id in range(len(self.itemList)):
        self.load(self.itemList[id])

Je ne peux pas penser à une solution qui n'utilise pas rapidement la composition range-len.

Mais probablement plutôt cela devrait être fait avec try .. except pour rester Pythonic je suppose ..

0
IARI