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))
Author: martineau, 2010-11-04

9 answers

Isto é bastante claro, na verdade. Muita gente aprende depressa. Você pode usar um comentário para ajudá-los.
new_index = max(0, min(new_index, len(mylist)-1))
 89
Author: S.Lott, 2010-11-03 23:21:03
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
 66
Author: John La Rooy, 2010-11-03 23:43:50

Ver numpy.clip:

index = numpy.clip(index, 0, len(my_list) - 1)
 35
Author: Neil G, 2011-05-17 03:22:55
Muitas respostas interessantes aqui, todas iguais, excepto... Qual é mais rápido?
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 já o tem! usa o python. A versão numpy é, talvez não surpreendentemente, a mais lenta do lote. Provavelmente porque ele está procurando arrays, onde as outras versões apenas Ordem seus argumentos.

 32
Author: SingleNegationElimination, 2017-05-23 12:26:12

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))
 13
Author: Laurence Gonsalves, 2010-11-03 23:25:32
O que aconteceu à minha amada linguagem Python legível? :-) A sério, faz com que funcione.
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])
 13
Author: paxdiablo, 2010-11-04 02:15:36

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)
 5
Author: Greg Hewgill, 2010-11-03 23:22:25
Este parece-me mais pitónico.
>>> 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
 4
Author: Jens, 2016-05-02 19:09:56

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)
 2
Author: Jetze Schaafsma, 2011-10-04 10:05:08