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))
Vaelus
  • 1,065
  • 10
  • 27
  • 1
    As far as I know, there is no *builtin* function, but you can easily define your own higher order function for that... – Willem Van Onsem Mar 02 '17 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)]` – Cleb Mar 02 '17 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. – Willem Van Onsem Mar 02 '17 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](https://docs.python.org/3/library/functions.html#filter), I don't spot the difference either (apart from list vs. iterator). – Cleb Mar 02 '17 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. – Willem Van Onsem Mar 02 '17 at 17:31
  • @WillemVanOnsem: Ok, thanks for the explanation. Then I of course fully agree. – Cleb Mar 02 '17 at 17:33

4 Answers4

17

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.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 1
    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. – kyrill Feb 07 '20 at 10:24
  • 1
    What is the use of `@wraps` here? – cs95 Dec 18 '20 at 22:43
  • @cs95: ensure that the parameter data is the same as `f`, otherwise it is just `*args` and `**kwargs`. – Willem Van Onsem Dec 18 '20 at 22:46
  • @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`. – Willem Van Onsem Dec 18 '20 at 22:50
6

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

Ken Williams
  • 22,756
  • 10
  • 85
  • 147
lukeg
  • 4,189
  • 3
  • 19
  • 40
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
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
lelabo_m
  • 509
  • 8
  • 21
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.