Two small functional-style-related improvements

Hello. IMHO it'd be nice... 1. ...to add: * operator.is_none -- equivalent to (lambda x: x is None)) * operator.is_not_none -- equivalent tolambda x: x is not None)) ...making using 'is None'/'is not None' tests with any(), all(), filter(), itertools.takewhile/dropwhile() more convenient and readable (possibly also optimised for speed). 2. ...to add: * operator.anti_caller (or e.g. functools.negator?) -- equivalent to: def anti_caller(func): def call_and_negate(*args, **kwargs): return not func(*args, **kwargs) return call_and_negate What do you think? *j

Jan Kaliszewski <zuo@chopin.edu.pl> writes:
Why so specific? What's wrong with ‘operator.is_(x, None)’ and ‘operator.is_not(x, None)’? Those both work today.
This one seems overkill for the standard library. -- \ “When cryptography is outlawed, bayl bhgynjf jvyy unir | `\ cevinpl.” —Anonymous | _o__) | Ben Finney

Ben Finney dixit (2011-03-27, 14:14):
None is quite specific (and widely used in different contexts) and is None/is not None tests are very common.
What's wrong with ‘operator.is_(x, None)’ and ‘operator.is_not(x, None)’? Those both work today.
But cannot be used quickly with all(), any(), filter(), takewhile(), dropwhile() etc. without ugly lambda or playing with partial(). Which one of the following do you prefer? * filter((lambda x: x is None), iterable) * filter(functools.partial(operator.is_, None), iterable) * filter(None, (x is None for x in iterable)) * filter(operator.is_none, iterable)
But itertools.filterfalse() was added -- which is counterpart for filter(). Why not to cover also all(), any(), takewhile(), dropwhile() etc. with only one additional function? Regards. *j

On Sun, Mar 27, 2011 at 01:53:26PM +0200, Jan Kaliszewski wrote:
I prefer [x for x in iterable if x is not None] Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

Jan Kaliszewski <zuo@chopin.edu.pl> writes:
But anyway, I'd prefer filter(is_none, iterable) :)
Feel free to write such a function for your code, then. I don't see how it's important enough to be in the Python standard library. -- \ “Ridicule is the only weapon which can be used against | `\ unintelligible propositions.” —Thomas Jefferson, 1816-07-30 | _o__) | Ben Finney

On 3/27/2011 7:53 AM, Jan Kaliszewski wrote:
These two produce an iterable of Nones, one for each None in the original iterable. I have trouble imagining a real use case for this. I can see a use cases for dropping everything but None. This can, of course, always be done at the point of use: for ob in iterable: if ob is not None: do_something(ob) If you want to separate the conditional from the action and hide the conditional, then write a trivial, specific, filter generator: def dropNone(it): for ob in it: if ob is not None: yield ob for ob in dropNone(iterable): do_something(ob) For repeated use, I prefer dropNone to any filter version, including your proposal.
* filter(None, (x is None for x in iterable))
This is not the same thing as the above as it instead produces an iterable of Trues, one for each None in iterable. -- Terry Jan Reedy

On 2011-03-27, at 05:14 , Ben Finney wrote:
predicate_user(operator.is_(None), collection) and have it behave as predicate_user(lambda value: operator.is_(value, None), collection) (note: order is important, I think the most common use case for non-commutative operator sections is to fix the second operand). This would not only obviate the purported need for an ``is_none`` operator method, it would make operator far more interesting for all higher-order tasks.

Jan Kaliszewski wrote:
How is import operator any(map(operator.is_none, iterable)) more convenient and readable than: any(x is None for x in iterable) ? I'm asking this as someone who likes map and other functional tools. Now that we have generator expressions and list comprehensions in the language, wrapping trivial expressions in a function is far less common. Likewise, how could a function call that includes 'x is None' be faster than 'x is None' alone? The overhead of calling the function would have to be negative! We can see this with the existing operator.is_ function: [steve@sylar ~]$ python3 -m timeit -r 15 -s "from operator import is_" "is_(42, None)" 1000000 loops, best of 15: 0.224 usec per loop [steve@sylar ~]$ python3 -m timeit -r 15 "42 is None" 1000000 loops, best of 15: 0.117 usec per loop For comparison purposes: [steve@sylar ~]$ python3 -m timeit -r 15 -s "def f(x): x is None" "f(42)" 1000000 loops, best of 15: 0.378 usec per loop and just for completeness: [steve@sylar ~]$ python3 -m timeit -r 15 -s "from operator import is_; from functools import partial; f = partial(is_, None)" "f(42)" 1000000 loops, best of 15: 0.301 usec per loop The possible time saving compared to a pure-Python function is very small, and there's rarely a need to use a function when you can just use an expression.
This seems to be a decorator, so I don't believe it belongs in the operator module. Either way, I don't see the point to it. Why would you use this @functools.negator def spam(x): return something instead of just this? def spam(x): return not something What is your use-case for this function? -- Steven

Steven D'Aprano dixit (2011-03-27, 14:41):
It isn't indeed -- but I'd prefer e.g.: filter(is_not_none, iterable) ...rather than: filter(None, (x is not None for x in iterable)) (not saying about more complex cases, when additional inner for-loop makes the expression much less readable). And --what is maybe more important-- such functions as itertools.dropwhile, itertools.takewhile, itertools.groupby (together with .sort/sorted) do not have their generator expression counterparts (at least resonably simple ones). Ane then, if we wish to stay in the functional-style we must use ugly lambdas/partial for now.
Yes, maybe rather: functools.negated
I'd would like to be able to do: assert any(dropwhile(negated(str.isalnum), takewhile(negated(str.isspace), my_names))) ...instead of: assert any(dropwhile(lambda name: not name.isalnum(), takewhile(lambda name: not name.isspace(), my_names))) Regards. *j

On 27 March 2011 14:05, Jan Kaliszewski <zuo@chopin.edu.pl> wrote:
Do you honestly find *either* of those readable? I can't even work out what they do well enough to try to come up with an alternative version using generator expressions... If I needed an assertion like that, I'd write a function with a name that explains what's going on, which does the whole of that dropwhile-takewhile routine (probably using multiple lines, with comments) and then use that as assert(any(whatever_this_is(my_names))). -1 on putting anything in the stdlib which encourages obfuscated code like that. (Note: If that style suits you, then writing your own functions in your codebase to support it is fine, I just don't see it as something the stdlib should be doing). Paul.

Jan Kaliszewski <zuo@chopin.edu.pl> writes:
Why so specific? What's wrong with ‘operator.is_(x, None)’ and ‘operator.is_not(x, None)’? Those both work today.
This one seems overkill for the standard library. -- \ “When cryptography is outlawed, bayl bhgynjf jvyy unir | `\ cevinpl.” —Anonymous | _o__) | Ben Finney

Ben Finney dixit (2011-03-27, 14:14):
None is quite specific (and widely used in different contexts) and is None/is not None tests are very common.
What's wrong with ‘operator.is_(x, None)’ and ‘operator.is_not(x, None)’? Those both work today.
But cannot be used quickly with all(), any(), filter(), takewhile(), dropwhile() etc. without ugly lambda or playing with partial(). Which one of the following do you prefer? * filter((lambda x: x is None), iterable) * filter(functools.partial(operator.is_, None), iterable) * filter(None, (x is None for x in iterable)) * filter(operator.is_none, iterable)
But itertools.filterfalse() was added -- which is counterpart for filter(). Why not to cover also all(), any(), takewhile(), dropwhile() etc. with only one additional function? Regards. *j

On Sun, Mar 27, 2011 at 01:53:26PM +0200, Jan Kaliszewski wrote:
I prefer [x for x in iterable if x is not None] Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

Jan Kaliszewski <zuo@chopin.edu.pl> writes:
But anyway, I'd prefer filter(is_none, iterable) :)
Feel free to write such a function for your code, then. I don't see how it's important enough to be in the Python standard library. -- \ “Ridicule is the only weapon which can be used against | `\ unintelligible propositions.” —Thomas Jefferson, 1816-07-30 | _o__) | Ben Finney

On 3/27/2011 7:53 AM, Jan Kaliszewski wrote:
These two produce an iterable of Nones, one for each None in the original iterable. I have trouble imagining a real use case for this. I can see a use cases for dropping everything but None. This can, of course, always be done at the point of use: for ob in iterable: if ob is not None: do_something(ob) If you want to separate the conditional from the action and hide the conditional, then write a trivial, specific, filter generator: def dropNone(it): for ob in it: if ob is not None: yield ob for ob in dropNone(iterable): do_something(ob) For repeated use, I prefer dropNone to any filter version, including your proposal.
* filter(None, (x is None for x in iterable))
This is not the same thing as the above as it instead produces an iterable of Trues, one for each None in iterable. -- Terry Jan Reedy

On 2011-03-27, at 05:14 , Ben Finney wrote:
predicate_user(operator.is_(None), collection) and have it behave as predicate_user(lambda value: operator.is_(value, None), collection) (note: order is important, I think the most common use case for non-commutative operator sections is to fix the second operand). This would not only obviate the purported need for an ``is_none`` operator method, it would make operator far more interesting for all higher-order tasks.

Jan Kaliszewski wrote:
How is import operator any(map(operator.is_none, iterable)) more convenient and readable than: any(x is None for x in iterable) ? I'm asking this as someone who likes map and other functional tools. Now that we have generator expressions and list comprehensions in the language, wrapping trivial expressions in a function is far less common. Likewise, how could a function call that includes 'x is None' be faster than 'x is None' alone? The overhead of calling the function would have to be negative! We can see this with the existing operator.is_ function: [steve@sylar ~]$ python3 -m timeit -r 15 -s "from operator import is_" "is_(42, None)" 1000000 loops, best of 15: 0.224 usec per loop [steve@sylar ~]$ python3 -m timeit -r 15 "42 is None" 1000000 loops, best of 15: 0.117 usec per loop For comparison purposes: [steve@sylar ~]$ python3 -m timeit -r 15 -s "def f(x): x is None" "f(42)" 1000000 loops, best of 15: 0.378 usec per loop and just for completeness: [steve@sylar ~]$ python3 -m timeit -r 15 -s "from operator import is_; from functools import partial; f = partial(is_, None)" "f(42)" 1000000 loops, best of 15: 0.301 usec per loop The possible time saving compared to a pure-Python function is very small, and there's rarely a need to use a function when you can just use an expression.
This seems to be a decorator, so I don't believe it belongs in the operator module. Either way, I don't see the point to it. Why would you use this @functools.negator def spam(x): return something instead of just this? def spam(x): return not something What is your use-case for this function? -- Steven

Steven D'Aprano dixit (2011-03-27, 14:41):
It isn't indeed -- but I'd prefer e.g.: filter(is_not_none, iterable) ...rather than: filter(None, (x is not None for x in iterable)) (not saying about more complex cases, when additional inner for-loop makes the expression much less readable). And --what is maybe more important-- such functions as itertools.dropwhile, itertools.takewhile, itertools.groupby (together with .sort/sorted) do not have their generator expression counterparts (at least resonably simple ones). Ane then, if we wish to stay in the functional-style we must use ugly lambdas/partial for now.
Yes, maybe rather: functools.negated
I'd would like to be able to do: assert any(dropwhile(negated(str.isalnum), takewhile(negated(str.isspace), my_names))) ...instead of: assert any(dropwhile(lambda name: not name.isalnum(), takewhile(lambda name: not name.isspace(), my_names))) Regards. *j

On 27 March 2011 14:05, Jan Kaliszewski <zuo@chopin.edu.pl> wrote:
Do you honestly find *either* of those readable? I can't even work out what they do well enough to try to come up with an alternative version using generator expressions... If I needed an assertion like that, I'd write a function with a name that explains what's going on, which does the whole of that dropwhile-takewhile routine (probably using multiple lines, with comments) and then use that as assert(any(whatever_this_is(my_names))). -1 on putting anything in the stdlib which encourages obfuscated code like that. (Note: If that style suits you, then writing your own functions in your codebase to support it is fine, I just don't see it as something the stdlib should be doing). Paul.
participants (8)
-
Ben Finney
-
Jan Kaliszewski
-
Masklinn
-
MRAB
-
Oleg Broytman
-
Paul Moore
-
Steven D'Aprano
-
Terry Reedy