Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In python, is there some kind of mapping to return the "False value" of a type?

I am looking for some kind of a mapping function f() that does something similar to this:

f(str) = ''
f(complex) = 0j
f(list) = []

Meaning that it returns an object of type that evaluates to False when cast to bool.

Does such a function exist?

like image 395
MattS Avatar asked Mar 15 '18 08:03

MattS


People also ask

Can Python function return true or false?

Python bool() function is used to return or convert a value to a Boolean value i.e., True or False, using the standard truth testing procedure.

What is type map in Python?

Python's map() is a built-in function that allows you to process and transform all the items in an iterable without using an explicit for loop, a technique commonly known as mapping. map() is useful when you need to apply a transformation function to each item in an iterable and transform them into a new iterable.

What evaluates to false in Python?

First, we look at what kind of values evaluate to "True" or "False" in python. Anything that is "empty" usually evaluates to False, along with the integer 0 and the boolean value of False. Objects that are not empty evaluate to "True", along with numbers not equal to 0, and the boolean value True.


4 Answers

No, there is no such mapping. Not every type of object has a falsy value, and others have more than one. Since the truth value of a class can be customized with the __bool__ method, a class could theoretically have an infinite number of (different) falsy instances.

That said, most builtin types return their falsy value when their constructor is called without arguments:

>>> str()
''
>>> complex()
0j
>>> list()
[]
like image 77
Aran-Fey Avatar answered Oct 28 '22 07:10

Aran-Fey


Nope, and in general, there may be no such value. The Python data model is pretty loose about how the truth-value of a type may be implemented:

object.__bool__(self)

Called to implement truth value testing and the built-in operation bool(); should return False or True. When this method is not defined, __len__() is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither __len__() nor __bool__(), all its instances are considered true.

So consider:

import random
class Wacky:
    def __bool__(self):
        return bool(random.randint(0,1))

What should f(Wacky) return?

like image 24
juanpa.arrivillaga Avatar answered Oct 28 '22 08:10

juanpa.arrivillaga


This is actually called an identity element, and in programming is most often seen as part of the definition of a monoid. In python, you can get it for a type using the mzero function in the PyMonad package. Haskell calls it mempty.

like image 6
Karl Bielefeldt Avatar answered Oct 28 '22 06:10

Karl Bielefeldt


Not all types have such a value to begin with. Others may have many such values. The most correct way of doing this would be to create a type-to-value dict, because then you could check if a given type was in the dict at all, and you could chose which value is the correct one if there are multiple options. The drawback is of course that you would have to somehow register every type you were interested in.

Alternatively, you could write a function using some heuristics. If you were very careful about what you passed into the function, it would probably be of some limited use. For example, all the cases you show except complex are containers that generalize with cls().

complex actually works like that too, but I mention it separately because int and float do not. So if your attempt with the empty constructor fails by returning a truthy object or raising a TypeError, you can try cls(0). And so on and so forth...

Update

@juanpa.arrivillaga's answer actually suggests a clever workaround that will work for most classes. You can extend the class and forcibly create an instance that will be falsy but otherwise identical to the original class. You have to do this by extending because dunder methods like __bool__ are only ever looked up on the class, never on an instance. There are also many types where such methods can not be replaced on the instance to begin with. As @Aran-Fey's now-deleted comment points out, you can selectively call object.__new__ or t.__new__, depending on whether you are dealing with a very special case (like int) or not:

def f(t):
    class tx(t):
        def __bool__(self):
            return False
    try:
        return object.__new__(tx)
    except TypeError:
        return tx.__new__(tx)

This will only work for 99.9% of classes you ever encounter. It is possible to create a contrived case that raises a TypeError when passed to object.__new__ as int does, and does not allow for a no-arg version of t.__new__, but I doubt you will ever find such a thing in nature. See the gist @Aran-Fey made to demonstrate this.

like image 5
Mad Physicist Avatar answered Oct 28 '22 08:10

Mad Physicist