Como prender um inteiro a algum alcance?
tenho o seguinte código:
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]
basicamente, eu calculo um novo índice e uso-o para encontrar algum elemento de uma lista. Para ter certeza de que o índice está dentro dos limites da lista, eu precisava escrever essas declarações 2 if
espalhadas em 4 linhas. Isso é bastante verboso, um pouco feio... Atrevo-me a dizer que não é pythonic.
existe outra solução mais simples e mais compacta? (e mais pythonic)
Sim, eu sei que posso usar if else
numa linha, mas não é legível:
new_index = 0 if new_index < 0 else len(mylist) - 1 if new_index >= len(mylist) else new_index
Também sei que posso acorrentar max()
e min()
juntos. É mais compacto, mas sinto que é um pouco obscuro, mais difícil de encontrar bugs Se eu digitar mal. Por outras palavras, não me parece muito simples.
new_index = max(0, min(new_index, len(mylist)-1))
9 answers
new_index = max(0, min(new_index, len(mylist)-1))
sorted((minval, value, maxval))[1]
Por exemplo:
>>> 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
Ver numpy.clip:
index = numpy.clip(index, 0, len(my_list) - 1)
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
Acorrentar {[[1]} e {[[2]} juntos é o idioma normal que eu vi. Se achar difícil de ler, escreva uma função auxiliar para encapsular a operação:
def clamp(minimum, x, maximum):
return max(minimum, min(x, maximum))
def addInRange (val, add, minval, maxval):
newval = val + add
if newval < minval: return minval
if newval > maxval: return maxval
return newval
Então, liga - lhe com algo do género:
val = addInRange (val, 7, 0, 42)
Ou uma solução mais simples, mais flexível, onde você mesmo faz o cálculo:
def restrict (val, minval, maxval):
if val < minval: return minval
if val > maxval: return maxval
return val
x = restrict (x+10, 0, 42)
Se quisesses, podias até fazer do min/max uma lista para parecer mais "matematicamente puro":
x = restrict (val+7, [0, 42])
Se o seu código parece demasiado pesado, uma função pode ajudar:
def clamp(minvalue, value, maxvalue):
return max(minvalue, min(value, maxvalue))
new_index = clamp(0, new_index, len(mylist)-1)
>>> def clip(val, min_, max_):
... return min_ if val < min_ else max_ if val > max_ else val
Alguns testes:
>>> clip(5, 2, 7)
5
>>> clip(1, 2, 7)
2
>>> clip(8, 2, 7)
7
Evite escrever funções para tarefas tão pequenas, a menos que as aplique com frequência, pois isso irá confundir o seu código.
Para valores individuais:
min(clamp_max, max(clamp_min, value))
Para as listas de valores:
map(lambda x: min(clamp_max, max(clamp_min, x)), values)