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).
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.
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".
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.
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 ~~
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.
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
returns the value
5 (whatever the value of ʻa
)5
is TruthyAnd 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).
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.
False
0
0.0
None
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,
None
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.
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.
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