2010-11-03 3 views
65

Je le code suivant:Comment fixer un entier à une certaine plage? (En Python)

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] 

Fondamentalement, je calcule un nouvel indice et l'utiliser pour trouver un élément dans une liste. Afin de m'assurer que l'index est dans les limites de la liste, j'ai dû écrire ces 2 instructions if réparties en 4 lignes. C'est assez verbeux, un peu moche ... Oserais-je dire, c'est assez un-pythonic.

Existe-t-il une autre solution plus simple et plus compacte? (et plus pythonique)

Oui, je sais que je peux utiliser if else en une ligne, mais il est illisible:

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îne max() et min() ensemble. C'est plus compact, mais je trouve que c'est un peu obscur, plus difficile de trouver des bugs si je tape mal. En d'autres termes, je ne trouve pas cela très simple.

new_index = max(0, min(new_index, len(mylist)-1)) 
+1

Si elle se sent "un peu obscur", faire une fonction hors de lui? – Santa

+1

Oui, je peux écrire une fonction, mais ce n'est pas le but. La question est de savoir comment implémenter cela (soit en ligne ou dans une fonction). –

+0

'pince = valeur lambda, minv, maxv: max (min (valeur, maxv), minv)' Utilisation de l'API de http://arma.sourceforge.net/docs.html#clamp –

Répondre

71

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)) 
+12

Bien que je ne le pense pas Aussi pythonique que cela devrait être, je pense aussi que c'est la meilleure solution que nous avons maintenant. –

+21

'def pince (n, plus petit, plus grand): return max (plus petit, min (n, plus grand))' – csl

+2

@csl Les gens fournissent toujours ces petites fonctions d'aide, mais je ne sais jamais où les mettre. 'helperFunctions.py'? Un module séparé? Que faire si cela est jonché de diverses "fonctions d'aide" pour des choses complètement différentes? –

5

Si votre code semble trop difficile à manier, une fonction peut aider:

def clamp(minvalue, value, maxvalue): 
    return max(minvalue, min(value, maxvalue)) 

new_index = clamp(0, new_index, len(mylist)-1) 
12

Quelle que soit arrivé à mon bien-aimé langage Python lisible? :-)

Sérieusement, il suffit de 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 il suffit d'appeler avec quelque chose comme:

val = addInRange (val, 7, 0, 42) 

Ou un simple, plus flexible, solution où vous faites 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 le min/max une liste de sorte qu'il semble plus "m athematically pur ":

x = restrict (val+7, [0, 42]) 
+5

Le mettre dans une fonction est bien (et conseillé, si vous le faites beaucoup), mais je pense que 'min' et' max' sont beaucoup plus clairs qu'un tas de conditions. (Je ne sais pas à quoi sert 'add' - il suffit de dire 'clamp (val + 7, 0, 42)'.) –

12

max() et Chaînage min() ensemble est l'idiome normale que j'ai vu. Si vous avez du mal à lire, écrire une fonction d'assistance pour encapsuler l'opération:

def clamp(minimum, x, maximum): 
    return max(minimum, min(x, maximum)) 
59
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 
+4

+1 pour une utilisation créative de 'builted()' built-in. Très compact, mais c'est juste un peu obscur. En tout cas, c'est toujours agréable de voir d'autres solutions créatives! –

+7

Très créatif, et en fait à peu près aussi rapide que la construction 'min (max())'. Très légèrement plus rapide dans le cas où le nombre est dans la gamme et aucun échange n'est nécessaire. – kindall

34

Voir numpy.clip:

index = numpy.clip(index, 0, len(my_list) - 1) 
+0

[Les docs] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.clip.html) indiquent que le premier paramètre de 'clip' est' a', un "tableau contenant des éléments à couper ". Donc, vous devrez écrire 'numpy.clip ([index], ...', pas 'numpy.clip (index, ...'. –

+8

@ RoryO'Kane: Avez-vous essayé? –

+1

Pandas permet également cela sur Series et DataFrames, –

2

Évitez les fonctions d'écriture pour de telles petites tâches, sauf si vous les appliquez souvent, car cela encombrera 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) 
26

beaucoup de réponses intéressantes ici, tout sur les mêmes, sauf que l'on ... plus rapide de?

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 a !, il utilise python « ol plaine. La version numpy est, peut-être pas étonnamment, la plus lente du lot. Probablement parce qu'il cherche des tableaux, où les autres versions ordonnent simplement leurs arguments.

+0

La performance de Numpy est étonnamment mauvaise –

+6

@LenarHoyt ce n'est pas si surprenant, étant donné que les performances de Numpy sont conçues autour de grands tableaux, pas de nombres uniques, et qu'elle doit d'abord convertir l'entier en un type de données interne. Il est probable que vous ayez besoin de beaucoup de temps pour déterminer le type d'entrée et la nature de la conversion. Vous obtiendrez de meilleures performances de Numpy si vous le placez dans un tableau (de préférence pas une liste ou un tuple). convertit en premier) de plusieurs milliers de valeurs – blubberdiblub

+0

Python est trois ordres de grandeur plus lents 783 ns = 783 000 μs J'ai fait la même erreur dans le passé La notation est subtile. –

2

Celui-ci me semble plus pythonique à:

>>> def clip(val, min_, max_): 
...  return min_ if val < min_ else max_ if val > max_ else val 

Quelques essais:

>>> clip(5, 2, 7) 
5 
>>> clip(1, 2, 7) 
2 
>>> clip(8, 2, 7) 
7 
Questions connexes