13

A python boolean function can easily be negated with lambda functions, but it's a bit verbose and hard to read for something so basic, for example:

def is_even(n):
    return n % 2 == 0

odds_under_50 = filter(lambda x: not is_even(x), range(50))

I'm wondering if there is a function to do this in the standard library, which might look like:

odds_under_50 = filter(negate(is_even), range(50))
6
  • 1
    As far as I know, there is no builtin function, but you can easily define your own higher order function for that... Commented Mar 2, 2017 at 17:21
  • Not to my knowledge; in this case I would just go with a list comprehension: [xi for xi in range(50) if not is_even(xi)] Commented Mar 2, 2017 at 17:23
  • @Cleb: not that in Python-3.x filter and this list comprehension are not equivalent since filter is lazy: you can filter an infinite generator whereas you cannot do that with list comprehension. Commented Mar 2, 2017 at 17:27
  • @WillemVanOnsem: What exactly would be the differences in practical terms? Might be tricky to discuss in the comments but I currently don't see the difference. When I read the documentation, I don't spot the difference either (apart from list vs. iterator). Commented Mar 2, 2017 at 17:29
  • if you import itertools and then do [xi for xi in itertools.repeat(1) if not is_even(xi)] this will run out of memory. filter will evaluate lazily and thus not consume CPU/memory at all. Commented Mar 2, 2017 at 17:31

4 Answers 4

16

As far as I know there is no builtin function for that, or a popular library that does that.

Nevertheless, you can easily write one yourself:

from functools import wraps

def negate(f):
    @wraps(f)
    def g(*args,**kwargs):
        return not f(*args,**kwargs)
    g.__name__ = f'negate({f.__name__})'
    return g

You can then use:

odds_under_50 = filter(negate(is_even), range(50))

The negate function works for an arbitrary amount of parameters of the given function: if you would have defined is_dividable(x,n=2). Then negate(is_dividable) is a function with two arguments (one optional) that would also accept these parameters.

Sign up to request clarification or add additional context in comments.

4 Comments

Here it's worth noting that the negated function will have the same docstring and name as the original function, which may not be a good idea.
What is the use of @wraps here?
@cs95: ensure that the parameter data is the same as f, otherwise it is just *args and **kwargs.
@cs95: probably we can use g.__name__ = f'negate({f.__name__})' and thus retain the parameter data and change the name of the function to negate(f), that would make it better since then we get for help negate(f)(a, b) if f has parameters a and b.
6

In case of filter you can use ifilterfalse (or filterfalse in Python 3.x) from itertools.

2 Comments

That does not really answers the question.
3

You can create a decorator:

def negate(function):
    def new_function(*args, **kwargs):
       return not function(*args, **kwargs)
    return new_function


def is_even(x):
    return x % 2 == 0

print is_even(1)
print is_even(2)

is_odd = negate(is_even)
print is_odd(1)
print is_odd(2)

This decorator can also be used with @negate.

@negate
def is_odd(x):
    return x % 2 == 0

Comments

1

With funcy's or toolz's compose function you can negate the function like that:

import operator

import funcy

is_odd = funcy.compose(operator.not_, is_even)

If you want to make it more readable:

def negate(func):
    return funcy.compose(operator.not_, func)

is_odd = negate(is_even)

# or without creating the function directly
print(negate(is_even)(5))

The funcy library has a lot of other useful functions for functional programming.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.