An alternative to using a clamp / clip / trim function
Hi All This is related to discussion https://mail.python.org/archives/list/python-ideas@python.org/thread/KWAOQFS... 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
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. On Wed, Jul 8, 2020, 11:22 AM Jonathan Fine <jfine2358@gmail.com> wrote:
Hi All
This is related to discussion https://mail.python.org/archives/list/python-ideas@python.org/thread/KWAOQFS...
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/SXECQD... Code of Conduct: http://python.org/psf/codeofconduct/
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
This is related to discussion https://mail.python.org/archives/list/python-ideas@python.org/thread/KWAOQFS...
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/SXECQD... 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/IWVMIU... 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
Hi Thank you all for the helpful comments. For me, the key question is as follows. There are two ways of asking for clamping, clipping or trimming (call it what you will). They are: aaa(lo, hi, num) bbb(lo, hi)(num) The question is then, which to provide? This is, in part, a syntactic question. There's precedent in the standard library for providing both!
str.join(',', 'abcde') 'a,b,c,d,e' >>> ','.join('abcde') 'a,b,c,d,e'
>>> import struct >>> struct.pack('II', 34, 55) b'"\x00\x00\x007\x00\x00\x00' >>> from struct import Struct >>> Struct('II').pack(34, 55) b'"\x00\x00\x007\x00\x00\x00' >>> import re >>> re.split('[a|b]+', 'AaababaBaaaCbabbD') ['A', 'B', 'C', 'D'] >>> from re import compile >>> compile('[a|b]+').split('AaababaBaaaCbabbD') ['A', 'B', 'C', 'D'] It's worth noting that for the re module "compiled version of the most recent patterns ... are cached", and that struct has a _clearcache function (which clears the internal cache). Others have noted that the bbb form can be obtained from the aaa form by using functools.partial. Similarly, the aaa form can be obtained from the aaa form, by a small piece of code. These observations by themselves don't I think help us decide whether to provide one, the other, both or neither. They simply show an equivalence of semantic power. One of the key questions, as someone else has noted, is how often is there need for using the same (lo, hi) pair with different values of num. I think that's enough for now. -- Jonathan
On Wed, Jul 08, 2020 at 04:19:32PM +0100, Jonathan Fine wrote:
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
Here you go: aircon_clipper = functools.partial(math.clamp, lower=50, upper=80) Relevent: https://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html -- Steven
On 08.07.20 17:19, Jonathan Fine wrote:
Hi All
This is related to discussion https://mail.python.org/archives/list/python-ideas@python.org/thread/KWAOQFS...
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
You could also use `functools.partial` for that purpose: aircon_clipper = partial(clamp, min=50, max=80) So a (builtin) function serves this purpose as well.
participants (5)
-
Christopher Barker
-
David Mertz
-
Dominik Vilsmeier
-
Jonathan Fine
-
Steven D'Aprano