[Python] Is the zero but non-empty object Truthy or Fallsy?

I was curious about Truthy and Fallsy in Python, so this is an article to chat about Truthy and False while researching. If you are only interested in the title, please skip from [here](#python% E3% 81% AB% E3% 81% 8A% E3% 81% 91% E3% 82% 8Btruthy--falsy).

Chat about Truthy and Farsy

What is Truthy / Falsy

The terms Truthy and Farsy appear in JavaScript, but the concept itself appears in many languages, so we'll generalize it here.

Truthy is an adjective that indicates a value that is treated as true when logically evaluated. Falsy is an adjective that indicates a value that is treated as false when logically evaluated.

Depending on the person, only the values that are treated as true / false when logically evaluated, excluding the "representative value", may be called Truthy / Falsy, but here, according to the definition in JavaScript, all Truthy. And False.

Stance on Truthy / Falsy

I personally think that the stance on the language Truthy / Falsy can be divided into the following three patterns. In this paper, pattern A will be referred to as "pure authenticity", and pattern B and pattern C will be referred to as "extended authenticity".

Pattern A: Only one Truthy and one Fallsy

This type of language has a type dedicated to truth values, and limits the possible values to 2 types. Alternatively, it may have a "true-only object" or a "fake-only object" as a singleton class. Attempting to logically evaluate values other than those will result in an error.

Java is a typical example. However, in the case of Java, there are primitive truth values and wrapped truth values, so it can be said that this is not exactly the case.

Pattern B: All values are either Truthy or Fallsy

Any value can be logically evaluated in this type of language. In some cases it may even not have a type dedicated to truth values. However, the existence of a "representative value" is required as a return value for comparison operations and logical negation.

A typical example would be JavaScript. ~~ So you're saying that Java and JavaScript are different ~~

Pattern C: There are multiple Truthy / Falsy, but some values are neither

Personally, I don't have a good impression of either. However, if the "exception" is null, it may be a valid design in a sense (described later).

This is the C language by design. This is because the C language uses integer types for logical operations, etc., and considers everything except 0 to be Truthy. On the contrary, only integer type can be logically evaluated.

Advantages / Disadvantages of Extended Authenticity Language

Extended authenticity languages have several benefits.

One is that conditional expressions can be simplified.

For example, to avoid division by zero, you might write code like this:

if (b !== 0) {
    console.log(a / b)
} else {
    console.log('It will not break at zero.')
}

However, since 0 is False,

if (b) {
    console.log(a / b)
} else {
    console.log('It will not break at zero.')
}

You can see that is enough.

The other is that some logical operations can be generalized.

For example, in classical logic, OR is an operation that "returns false when both the left and right sides are false, and returns true otherwise", but this is "when the left side is Truthy, the left side is false, and the left side is False." It can be expanded without changing the existing truth table by interpreting "when the right side is returned".

You can take advantage of this to simplify some logic. For example, the logic of putting the default string when there is no input (empty string) is as follows when trying to write it straightforwardly.

if (!name) {
    name = 'The guests';
}

/*Or use the ternary operator*/
name = name ? 'The guests' : name

However, taking advantage of the empty string being False, we can write the following using the generalized OR.

name = name || 'The guests'

This is because if name is Truthy (that is, if it is not an empty string), the left side is returned as it is, and if it is False (that is, if it is an empty string), the right side is returned.

However, these also have disadvantages.

For example, in the former case, there is no guarantee that the number contained in b is actually a numerical value. If a non-numeric Truthy value is entered, it will bypass the conditional expression and generate a run-time error.

These pitfalls are prominent in dynamically typed languages, but even with static typing, there are many languages in which only Null can be assigned exceptionally (so-called Null is not safe), and if Null can also be logically evaluated, unexpected behavior will occur. It can happen.

Rather, I think people who use dynamically typed languages are willing to handle type integrity themselves (and are already used to it because they often get run-time errors about types), so I prefer a statically typed language that is left to compilation. It can be as dangerous as the person using it.

This specification also became the one parent who created the infamous Yoda notation. The other parent is the "specification that the assignment operator returns a value". result,

if (a == 5) {
    /* ... */
}

I intended to write

if (a = 5) {
    /* ... */
}

Even if it is

  1. The expression ʻa = 5 returns the value 5 (whatever the value of ʻa)
  2. 5 is Truthy
  3. Execute the contents of the if block!

And created a hotbed of bugs. To avoid this

if (5 == a) {
    /* ... */
}

That's why a strange custom was born.

This remains in your organization's legacy coding rules, and even in situations where you don't need it (that is, a language that doesn't return a value for an extortion or assignment operator, or a compiler that warns you that you're making an assignment in an if conditional expression. It creates a situation where you are forced (even if you are using it). The customs that were born out of necessity eventually forget their meaning and become meaningless "manners" that are just shapes ... as is often the case in the real world.

Quiet talk break. In this way, Truthy / Falsy's stance has advantages (efficiency) and disadvantages (pitfalls) back to back, so when you first use a language that you come into contact with in your work, it is either a pure authenticity language or an extended authenticity language. It is important to be aware of this. Later, when it comes to "I didn't know that ...", it's painful (experience story).

Truthy / Falsy in Python

What decides Farsy

Well, finally the title. From here, we will talk about Python2 for a while. In case of 3, I will touch it at the end.

Python belongs to the extended authenticity language. All non-Falsy values are Truthy. Among the general values, the following values are False.

By the way, the bool type in Python is actually a subclass of the int type, and True and False are actually equivalent to 1 and 0, respectively. Based on this,

It seems that it can be generalized, right?

Aside from the special value None, Python defines special methods that indicate" zero or not "and" collection length ". In other words, by implementing these, you can control whether your own object is Truthy or False.

Let's try it right away. The first is when neither is implemented.

class MyObjA:
    def __init__(self):
        pass


my_obj = MyObjA()
print('Truthy' if my_obj else 'Falsy')
Truthy

You can see that it is treated as Truthy because there is no element that is Falsy.

Next, let's say "zero in numerical terms". Implement the __nonzero__ method.

class MyObjB:
    def __init__(self, nz):
        self.nz = nz

    def __nonzero__(self):
        return self.nz


my_obj = MyObjB(True)
print('Truthy' if my_obj else 'Falsy')
my_obj = MyObjB(False)
print('Truthy' if my_obj else 'Falsy')
Truthy
Falsy

As you can see, if __nonzero__ returns a False (to be exact, it returns a value of type Int, which is Falsey), then the object itself is treated as False.

Next, let's try the "empty collection". Implement the __len__ method.

class MyObjC:
    def __init__(self, ln):
        self.ln = ln

    def __len__(self):
        return self.ln


my_obj = MyObjC(10)
print('Truthy' if my_obj else 'Falsy')
my_obj = MyObjC(0)
print('Truthy' if my_obj else 'Falsy')
Truthy
Falsy

As you can see, if __len__ returns 0 (to be exact, it returns a value of type int), you can see that the object itself is treated as Farsy.

In case of contradiction

But what if these conditions are inconsistent with each other? That's what the title means.

When I actually try it ...

class MyObjD:
    def __init__(self, nz, ln):
        self.nz = nz
        self.ln = ln

    def __nonzero__(self):
        return self.nz

    def __len__(self):
        return self.ln


my_obj = MyObjD(True, 10)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(False, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(True, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Truthy
Falsy

As you can see, "zero or not" is prioritized regardless of the length. The reason for this can be read from the error message when __len__ returns an invalid value.

class MyObjC:
    def __init__(self, ln):
        self.ln = ln

    def __len__(self):
        return self.ln


my_obj = MyObjC(0.0)
print('Truthy' if my_obj else 'Falsy')
Traceback (most recent call last):
  File "Main.py", line 10, in <module>
    print('Truthy' if my_obj else 'Falsy')
TypeError: __nonzero__ should return an int

" __Nonzero__ should return an int type, "was angry. In other words, it can be inferred that the default __nonzero__ implementation is calling __len__. Let's check. __nonzero__ is called by the built-in function bool.

class MyObjE:
    def __init__(self, ln):
        self.ln = ln

    def __len__(self):
        print('__len__ called!')
        return self.ln


my_obj = MyObjE(0)
b = bool(my_obj)
print(b)
print(type(b))
__len__ called!
False
<type 'bool'>

You're guessing! The default __nonzero__ implementation is calling __len__, so what looked like a check for __len__ in MyObjC was actually just calling __nonzero__ ... that is, both Even if you implement it, you're only looking at __nonzero__. That's why "zero but not empty objects" are Farsy.

In Python3

By the way, the discrepancy between the special method name __nonzero__ and the built-in function name bool has been corrected in Python3 and is now __bool__. Therefore, it becomes like this.

class MyObjD:
    def __init__(self, nz, ln):
        self.nz = nz
        self.ln = ln

    def __nonzero__(self):
        return self.nz

    def __len__(self):
        return self.ln


my_obj = MyObjD(True, 10)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(False, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(True, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Falsy
Truthy
class MyObjF:
    def __init__(self, nz, ln):
        self.nz = nz
        self.ln = ln

    def __bool__(self):
        return self.nz

    def __len__(self):
        return self.ln


my_obj = MyObjF(True, 10)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjF(False, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjF(True, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjF(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Truthy
Falsy

And, as the name suggests, __bool__ only allows return values of type bool.

class MyObjF:
    def __init__(self, nz, ln):
        self.nz = nz
        self.ln = ln

    def __bool__(self):
        return self.nz

    def __len__(self):
        return self.ln


my_obj = MyObjF(1, 10)
print('Truthy' if my_obj else "Falsy")
Traceback (most recent call last):
  File "Main.py", line 14, in <module>
    print('Truthy' if my_obj else "Falsy")
TypeError: __bool__ should return bool, returned int

__bool__ now only allows type bool, but what about the spec that the default implementation calls __len__?

class MyObjE:
    def __init__(self, ln):
        self.ln = ln

    def __len__(self):
        print('__len__ called!')
        return self.ln


my_obj = MyObjE(0)
b = bool(my_obj)
print(b)
print(type(b))
__len__ called!
False
<class 'bool'>

There seems to be no change in the specification that the default __bool__ implementation calls __len__. It seems that there is no problem because the return value of __len__ is cast to bool type from the beginning.

Recommended Posts

[Python] Is the zero but non-empty object Truthy or Fallsy?
Python is painful. But use
[Python] Which is executed first, the class variable or __init__?
[Python] What is @? (About the decorator)
Which is better, PyPy or Python?
What is the python underscore (_) for?
Python does not output errors or output just because the indent is misaligned
[Python] Determine if any coordinate point is inside or outside the polygon
OR the List in Python (zip function)
Where is the python instantiation process written?
[Python3] Rewrite the code object of the function
What is "mahjong" in the Python library? ??
[python] [meta] Is the type of python a type?
Which is faster, Python shuffle or sample?