69

I want to limit a number to be within a certain range. Currently, I am doing the following:

minN = 1
maxN = 10
n = something() #some return value from a function
n = max(minN, n)
n = min(maxN, n)

This keeps it within minN and maxN, but it doesn't look very nice. How could I do it better?

PS: FYI, I am using Python 2.6.

0

5 Answers 5

131
def clamp(n, minn, maxn):
    return max(min(maxn, n), minn)

or functionally equivalent:

clamp = lambda n, minn, maxn: max(min(maxn, n), minn)

now, you use:

n = clamp(n, 7, 42)

or make it perfectly clear:

n = minn if n < minn else maxn if n > maxn else n

even clearer:

def clamp(n, minn, maxn):
    if n < minn:
        return minn
    elif n > maxn:
        return maxn
    else:
        return n
Sign up to request clarification or add additional context in comments.

2 Comments

def clamp(n, minn, maxn): return min(max(n, minn), maxn) slightly improves readability with arguments in the same order.
The fastest solution (at least in my tests on multiple single random values against the more readable contender np.clip()).
88

Simply use numpy.clip() (doc):

n = np.clip(n, minN, maxN)

It also works for whole arrays:

my_array = np.clip(my_array, minN, maxN)

1 Comment

Thanks. Was having a heck of a time trying not to write a custom calculation twice and not wanting to write my own function. :-)
59

If you want to be cute, you can do:

n = sorted([minN, n, maxN])[1]

6 Comments

This will require more comparisons than the other approaches.
That's why I called it "cute" and not "practical." ;) However, it's highly unlikely that the inefficiency of this code will cause a meaningful performance problem in most cases.
Woah, that really is cute! I also like how it is invariant under interchange of minN and maxN. This is definitely my favorite clamp function. +1 ^_^
if someone is interested what version works faster: both are fast but min(max(...)...) is about 1.4 times faster. Details: python -m timeit -s "min_n = 10; max_n = 15" "for x in range(30): max(min(x, max_n), min_n)":7.28 usec per loop. python -m timeit -s "min_n = 10; max_n = 15" "for x in range(30): sorted([min_n, x, max_n])[1]": 10.2 usec per loop. "min_n = 1000; max_n = 15000" "for x in range(-15000, 30000): ...": 11 msec per loop, "min_n = 1000; max_n = 15000" "for x in range(-15000, 30000): ...": 14.8 msec per loop
no points for readability though :)
|
4

Define a class and have a method for setting the value which performs those validations.

Something vaguely like the below:

class BoundedNumber(object):
    def __init__(self, value, min_=1, max_=10):
        self.min_ = min_
        self.max_ = max_
        self.set(value)

    def set(self, newValue):
        self.n = max(self.min_, min(self.max_, newValue))

# usage

bounded = BoundedNumber(something())
bounded.set(someOtherThing())

bounded2 = BoundedNumber(someValue(), min_=8, max_=10)
bounded2.set(5)    # bounded2.n = 8

8 Comments

Well, it's extra development time to create, but it's SO REUSABLE! :-P
i am sure it can even be extended to check for invalid input numbers like NaN or +/-inf.
Yeah, and of course it could also be configured to have different bounds as well. :-)
and it can be plugged into a user interface for automatic input validation ! the possibilities are endless... you definitely should patent such an invention.
Thanks. Downvoter: Is it because this doesn't feel very "Pythonic" or do you have an ACTUAL issue with my answer?
|
0

Could you not string together some one-line python conditional statements?

I came across this question when looking for a way to limit pixel values between 0 and 255, and didn't think that using max() and min() was very readable so wrote the following function:

def clamp(x, minn, maxx):
   return x if x > minn and x < maxx else (minn if x < minn else maxx)

I would be interested to see how someone more experienced than me would find this way of clamping a value. I assume it must be less efficient than using min() and max(), but it may be useful for someone looking for a more readable (to me at least) function.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.