J'ai le code suivant:
new_index = index + offset
if new_index < 0:
new_index = 0
if new_index >= len(mylist):
new_index = len(mylist) - 1
return mylist[new_index]
En gros, je calcule un nouvel index et l’utilise pour trouver un élément dans une liste. Afin de m'assurer que l'index se trouve à l'intérieur des limites de la liste, j'avais besoin d'écrire ces 2 if
déclarations réparties sur 4 lignes. C'est assez verbeux, un peu moche ... J'ose le dire, c'est assez un-Pythonic .
Y a-t-il une autre solution plus simple et plus compacte? (et plus Pythonic )
Oui, je sais que je peux utiliser if else
Sur une ligne, mais ce n'est pas lisible:
new_index = 0 if new_index < 0 else len(mylist) - 1 if new_index >= len(mylist) else new_index
Je sais aussi que je peux chaîner max()
et min()
ensemble. C'est plus compact, mais je pense que c'est un peu obscur, plus difficile de trouver des bugs si je tape mal. En d'autres termes, je ne le trouve pas très simple.
new_index = max(0, min(new_index, len(mylist)-1))
C'est assez clair, en fait. Beaucoup de gens l'apprennent rapidement. Vous pouvez utiliser un commentaire pour les aider.
new_index = max(0, min(new_index, len(mylist)-1))
sorted((minval, value, maxval))[1]
par exemple:
>>> minval=3
>>> maxval=7
>>> for value in range(10):
... print sorted((minval, value, maxval))[1]
...
3
3
3
3
4
5
6
7
7
7
Voir numpy.clip :
index = numpy.clip(index, 0, len(my_list) - 1)
beaucoup de réponses intéressantes ici, toutes à peu près les mêmes, sauf ... laquelle est la plus rapide?
import numpy
np_clip = numpy.clip
mm_clip = lambda x, l, u: max(l, min(u, x))
s_clip = lambda x, l, u: sorted((x, l, u))[1]
py_clip = lambda x, l, u: l if x < l else u if x > u else x
>>> import random
>>> rrange = random.randrange
>>> %timeit mm_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 1.02 µs per loop
>>> %timeit s_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 1.21 µs per loop
>>> %timeit np_clip(rrange(100), 10, 90)
100000 loops, best of 3: 6.12 µs per loop
>>> %timeit py_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 783 ns per loop
paxdiablo l'a!, utilise un vieux python. La version numpy est, sans surprise, la plus lente du lot. Probablement parce qu'il recherche des tableaux, où les autres versions ordonnent simplement leurs arguments.
Qu'est-il arrivé à mon bien-aimé lisible Python? :-)
Sérieusement, juste en faire une fonction:
def addInRange(val, add, minval, maxval):
newval = val + add
if newval < minval: return minval
if newval > maxval: return maxval
return newval
alors appelez-le avec quelque chose comme:
val = addInRange(val, 7, 0, 42)
Ou une solution plus simple et plus flexible, dans laquelle vous effectuez le calcul vous-même:
def restrict(val, minval, maxval):
if val < minval: return minval
if val > maxval: return maxval
return val
x = restrict(x+10, 0, 42)
Si vous le souhaitez, vous pouvez même faire du min/max une liste pour qu’elle ait l’air plus "mathématiquement pur":
x = restrict(val+7, [0, 42])
Le chaînage max()
et min()
est l'idiome normal que j'ai vu. Si vous avez du mal à lire, écrivez une fonction d'assistance pour encapsuler l'opération:
def clamp(minimum, x, maximum):
return max(minimum, min(x, maximum))
Si votre code semble trop lourd, une fonction pourrait vous aider:
def clamp(minvalue, value, maxvalue):
return max(minvalue, min(value, maxvalue))
new_index = clamp(0, new_index, len(mylist)-1)
Celui-ci me semble plus pythonique:
>>> def clip(val, min_, max_):
... return min_ if val < min_ else max_ if val > max_ else val
Quelques tests:
>>> clip(5, 2, 7)
5
>>> clip(1, 2, 7)
2
>>> clip(8, 2, 7)
7
Évitez d’écrire des fonctions pour de telles petites tâches, à moins que vous ne les appliquiez souvent, car cela encombrerait votre code.
pour les valeurs individuelles:
min(clamp_max, max(clamp_min, value))
pour les listes de valeurs:
map(lambda x: min(clamp_max, max(clamp_min, x)), values)