On Wed, Jul 8, 2020 at 8:37 AM David Mertz <mertz@gnosis.cx> wrote:
This class or closure to clamp at specific bounds is nice. But I want to clamp based on runtime values for upper/lower fairly often. A function is much better for that use.

I suppose it comes down to whether you set the bounds once, and use them multiple times, or set new bounds each time you use it.

If the latter, then this looks a lot like Java-style OO-heavy code, where you create an instance of a class simply to use it once.

This reminds me of Jack Dietrich's talk: "Stop Writing Classes" :

"If your class has only two methods, and one of them is __init__ ... You don't have a class"

If we had a clamp function, and you DID have a use case where you set the bounds once and used them multipel times, you could do:

bounds = (a, b)
...
something = clamp(something, *bounds)

Or use functools.partial to make a custom clamping function.

Either of those seems more Pythonic to me.

-CHB


 

On Wed, Jul 8, 2020, 11:22 AM Jonathan Fine <jfine2358@gmail.com> wrote:
Hi All


In Python, lists don't have a join method. Instead, it's strings that have the join method. Hence we have:
    >>> ', '.join('abcde')
    'a, b, c, d, e'

The intermediate method we can save and store and use again.
    >>> joiner = ', '.join
    >>> joiner('fghijk')
    'f, g, h, i, j, k'

We can do something similar when clamping, clipping or trimming a value. For example, suppose we want limits on the room temperature, that the thermostat cannot override.
    >>> aircon_clipper = Clamper(50, 80)

    >>> thermostat_temp = 40
    >>> target_temp = aircon_temp_clipper(thermostat_temp)
    >>> target_temp
    50

What I like best about this is that the name of the function -- aircon_temp_clipper -- gives the reason for the clipping (or clamping). And this name can be chosen to suit the domain. I also like that it wraps the high and low values (50 and 80) as data used by a method.

Here's an implementation of Clamper.

    class Clamper:
        def __init__(self, lo, hi):

            if not lo <= hi:
                raise ValueError
            self._lo = lo
            self._hi = hi

        def __call__(self, num):
            lo, hi = self._lo, self._hi

            at_least_lo = (lo <= num)
            at_most_hi = (hi >= num)

            if at_least_lo and at_most_hi:
                return num
            elif at_least_lo:
                return hi
            elif at_most_hi:
                return lo
            else:
                raise ValueError

If performance is important to you, you could instead use a closure to create a function. This would also allow you to provide a docstring.

I hope this helps at least some of the people some of the time.

-- 
Jonathan
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/SXECQDXUJ7J5J4YOIFYJL3VNS757PTNI/
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IWVMIURULLW5IOZGFHIDNEIYBN5IJIWM/
Code of Conduct: http://python.org/psf/codeofconduct/


--
Christopher Barker, PhD

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython