Wie klemme ich eine ganze Zahl auf einen bestimmten Bereich?

91

Ich habe folgenden Code:

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]

Grundsätzlich berechne ich einen neuen Index und verwende diesen, um ein Element aus einer Liste zu finden. Um sicherzustellen, dass der Index innerhalb der Grenzen der Liste liegt, musste ich diese 2 ifAnweisungen in 4 Zeilen schreiben . Das ist ziemlich ausführlich, ein bisschen hässlich ... Ich wage zu sagen, es ist ziemlich unpythonisch .

Gibt es eine andere einfachere und kompaktere Lösung? (und mehr pythonisch )

Ja, ich weiß, dass ich if elsein einer Zeile verwenden kann, aber es ist nicht lesbar:

new_index = 0 if new_index < 0 else len(mylist) - 1 if new_index >= len(mylist) else new_index

Ich weiß auch, dass ich verketten max()und min()zusammen kann. Es ist kompakter, aber ich finde es etwas dunkel und schwieriger, Fehler zu finden, wenn ich es falsch schreibe. Mit anderen Worten, ich finde es nicht sehr einfach.

new_index = max(0, min(new_index, len(mylist)-1))
Denilson Sá Maia
quelle
2
Wenn es sich "irgendwie dunkel" anfühlt, machen Sie eine Funktion daraus?
Santa
1
Ja, ich kann eine Funktion schreiben, aber darum geht es nicht. Die Frage ist, wie das implementiert werden kann (entweder inline oder in einer Funktion).
Denilson Sá Maia
clamp = lambda value, minv, maxv: max(min(value, maxv), minv)Verwenden der API von arma.sourceforge.net/docs.html#clamp
Dima Tisnek

Antworten:

118

Das ist eigentlich ziemlich klar. Viele Leute lernen es schnell. Sie können einen Kommentar verwenden, um ihnen zu helfen.

new_index = max(0, min(new_index, len(mylist)-1))
S.Lott
quelle
12
Obwohl ich der Meinung bin, dass es nicht so pythonisch ist, wie es sein sollte, denke ich auch, dass dies die beste Lösung ist, die wir jetzt haben.
Denilson Sá Maia
45
def clamp(n, smallest, largest): return max(smallest, min(n, largest))
CSL
2
@csl Leute bieten immer diese kleinen Hilfsfunktionen an, aber ich weiß nie, wo ich sie ablegen soll. helperFunctions.py? Ein separates Modul? Was ist, wenn dies mit verschiedenen "Hilfsfunktionen" für ganz andere Dinge übersät ist?
Mateen Ulhaq
1
Ich weiß nicht, aber wenn Sie viele davon sammeln und in sinnvolle Module einteilen, warum nicht GitHub aufsetzen und daraus ein PyPi-Paket erstellen? Würde wahrscheinlich populär werden.
CSL
@MateenUlhaqutils.py
Wouterr
82
sorted((minval, value, maxval))[1]

beispielsweise:

>>> 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
John La Rooy
quelle
10
+1 für die kreative Nutzung von sorted()eingebauten. Sehr kompakt, aber nur ein bisschen dunkel. Wie auch immer, es ist immer schön, andere kreative Lösungen zu sehen!
Denilson Sá Maia
10
Sehr kreativ und eigentlich ungefähr so ​​schnell wie der min(max())Bau. Sehr etwas schneller, wenn die Anzahl im Bereich liegt und keine Swaps erforderlich sind.
Kindall
1
Dies sieht aus wie eine Code-Golf-Lösung und ich habe absolut kein Problem damit
senox13
39

viele interessante Antworten hier, alle ungefähr gleich, außer ... welche ist schneller?

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 hat es!, benutze einfach ol 'python. Die numpy Version ist, vielleicht nicht überraschend, die langsamste von allen. Wahrscheinlich, weil nach Arrays gesucht wird, bei denen die anderen Versionen nur ihre Argumente anordnen.

SingleNegationElimination
quelle
6
@LenarHoyt ist es nicht so überraschend, wenn man bedenkt, dass Numpys Leistung auf großen Arrays basiert, nicht auf einzelnen Zahlen. Außerdem muss die Ganzzahl zuerst in einen internen Datentyp konvertiert werden. Da mehrere verschiedene Arten von Eingaben akzeptiert werden, dauert es wahrscheinlich einige Zeit, um herauszufinden, welcher Typ die Eingabe ist und in was sie konvertiert werden soll. Sie werden eine viel bessere Numpy-Leistung sehen, wenn Sie ihm ein Array (vorzugsweise keine Liste oder ein Tupel, das zuerst konvertiert werden muss) mit mehreren tausend Werten zuführen.
blubberdiblub
Python ist drei Größenordnungen langsamer. 783 ns = 783.000 us. Ich habe in der Vergangenheit den gleichen Fehler gemacht. Die Notation ist subtil.
Dustin Andrews
4
@DustinAndrews du hast das rückwärts. 1 µs ist 10 ^ -6 Sekunden, 1 ns ist 10 ^ -9 Sekunden. Das Python-Beispiel vervollständigt 1 Schleife in 0,784 µs. Zumindest auf der Maschine, auf der ich es getestet habe. Dieses Mikrobenchmark ist ungefähr so ​​nützlich wie jedes andere Mikrobenchmark. Es kann Sie von wirklich schlechten Ideen abbringen, wird Ihnen aber wahrscheinlich nicht viel dabei helfen, den schnellsten Weg zu finden, nützlichen Code zu schreiben .
SingleNegationElimination
Der Aufruf von Funktionen ist mit einem geringen Aufwand verbunden. Ich habe nicht die Benchmarks getan, aber es ist durchaus möglich , dass mm_clipund py_clipgleich schnell sein, wenn Sie JIT - Compiler verwenden, wie PyPy. Nur dass Ersteres besser lesbar ist und Lesbarkeit in Pythons Philosophie wichtiger ist als ein geringfügiger Leistungsgewinn die meiste Zeit.
Highstaker
38

Siehe numpy.clip :

index = numpy.clip(index, 0, len(my_list) - 1)
Neil G.
quelle
Die docs sagen der erste Parameter clipist a, wird ein „Array - Elemente enthält , Clip“. Sie müssten also numpy.clip([index], …nicht schreiben numpy.clip(index, ….
Rory O'Kane
12
@ RoryO'Kane: Hast du es versucht?
Neil G
1
Pandas erlaubt dies auch für Serien- und DataFrames sowie Panels.
Nour Wolf
17

Verketten max()und min()zusammen ist die normale Redewendung, die ich gesehen habe. Wenn Sie Schwierigkeiten beim Lesen haben, schreiben Sie eine Hilfsfunktion, um die Operation zu kapseln:

def clamp(minimum, x, maximum):
    return max(minimum, min(x, maximum))
Laurence Gonsalves
quelle
14

Was ist mit meiner geliebten lesbaren Python-Sprache passiert? :-)

Im Ernst, machen Sie es einfach zu einer Funktion:

def addInRange(val, add, minval, maxval):
    newval = val + add
    if newval < minval: return minval
    if newval > maxval: return maxval
    return newval

dann nenne es einfach mit so etwas wie:

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

Oder eine einfachere, flexiblere Lösung, bei der Sie die Berechnung selbst durchführen:

def restrict(val, minval, maxval):
    if val < minval: return minval
    if val > maxval: return maxval
    return val

x = restrict(x+10, 0, 42)

Wenn Sie möchten, können Sie das Min / Max sogar zu einer Liste machen, damit es "mathematisch reiner" aussieht:

x = restrict(val+7, [0, 42])
paxdiablo
quelle
5
Es ist in Ordnung, es in eine Funktion einzufügen (und empfohlen, wenn Sie es oft tun), aber ich denke minund maxes ist viel klarer als eine Reihe von Bedingungen. (Ich weiß nicht, wofür add- sagen Sie einfach clamp(val + 7, 0, 42).)
Glenn Maynard
1
@GlennMaynard. Ich bin mir nicht sicher, ob ich zustimmen kann, dass Min und Max sauberer sind. Der springende Punkt bei der Verwendung ist, dass mehr in eine Zeile eingefügt werden kann, wodurch der Code weniger lesbar wird.
Mad Physicist
9

Dieser scheint mir pythonischer:

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

Einige Tests:

>>> clip(5, 2, 7)
5
>>> clip(1, 2, 7)
2
>>> clip(8, 2, 7)
7
Jens
quelle
Nett! Keine Funktionsaufrufe.
Martineau
7

Wenn Ihr Code zu unhandlich erscheint, kann eine Funktion helfen:

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

new_index = clamp(0, new_index, len(mylist)-1)
Greg Hewgill
quelle
2

Vermeiden Sie es, Funktionen für so kleine Aufgaben zu schreiben, es sei denn, Sie wenden sie häufig an, da dies Ihren Code überladen wird.

für einzelne Werte:

min(clamp_max, max(clamp_min, value))

für Wertelisten:

map(lambda x: min(clamp_max, max(clamp_min, x)), values)
Jetze Schaafsma
quelle