Booleans: Boolean Operators - Lecture

A free video tutorial from Dr. Fred Baptiste
Professional Developer and Mathematician
5 courses
66,092 students
Learn more from the full course
Python 3: Deep Dive (Part 1 - Functional)
Variables, Functions and Functional Programming, Closures, Decorators, Modules and Packages
46:05:54 of on-demand video • Updated December 2022
An in-depth look at variables, memory, namespaces and scopes
A deep dive into Python's memory management and optimizations
In-depth understanding and advanced usage of Python's numerical data types (Booleans, Integers, Floats, Decimals, Fractions, Complex Numbers)
Advanced Boolean expressions and operators
Advanced usage of callables including functions, lambdas and closures
Functional programming techniques such as map, reduce, filter, and partials
Create advanced decorators, including parametrized decorators, class decorators, and decorator classes
Advanced decorator applications such as memoization and single dispatch generic functions
Use and understand Python's complex Module and Package system
Idiomatic Python and best practices
Understand Python's compile-time and run-time and how this affects your code
Avoid common pitfalls
English [Auto]
Hi. In this video, we're going to continue looking at Boolean operators, but in a little bit more detail in particular, we're going to consider Boolean operators in the context of truth values. So let's just bring back our regular truth table. We have these inputs for X and Y, and then we have the result of x and y and x or Y. So normally boolean operators are defined to operate on Boolean values and return Boolean values. In fact, it's called a Boolean algebra and and an or are just operators in that Boolean algebra. And that means that these operators are only defined for booleans and will always return a Boolean. So for example, if we have true or false, that will return true. We could even use comparisons like this. So in this case, when A equals two, B equals three, then A is greater than zero. It's true. And B is less than five. That is true. And then true and true will return true. So we deal with boolean inputs and we have Boolean outputs. But now consider that every object in Python has a truth value. We have this concept of truthiness. And so for any object we really could write the following. We can write bool X and bool Y. That is perfectly legal. Since X has a truth value, we can get the truth value by calling bool on x and we can get the truth value of y by using bool of Y and we can do the same thing with or as well. So the thing is that we actually don't have to write bool here. In fact we can just write x and Y or we could write x or y. That will work perfectly well. Python will use the truthiness of x and the truthiness of Y to get our result and to evaluate x and y and x or y. So the question then becomes is what is actually returned when we write this expression here where X and Y are not booleans, but just objects that have an associated truth value, what is being returned? Is that a boolean? And the answer is no, it's not. It's not a Boolean, so let's see what's going on and let's see how Python actually defines its and and or operators. So let's start with or and I'll just bring back the truth table for or right here. So x or Y is defined this way. If x is truthy, it will return x, otherwise it returns y. And Bob, here's a little puzzled. Right? What's going on? This isn't kind of the standard thing we think of when we think of the Or operator. It's not a traditional way of teaching it. So does it work as expected with just Boolean values? If we just use X and Y as booleans, does this at least seem to correspond? So let's take a look. Let's just run through the different options. So let's take X and Y. Both false. So the rule is x is false. So we are going to return Y because the rule is that if X is truthy, we return x, otherwise we return Y. So x is false. So we return y. Well, y is zero in this case. So we get false, right? And I'm using zero and one as true and false. So if x is true is false and y is true, then x is false. So we still return y. So in this case we return one. And so far we're matching right zero zero returns. 001. We have a one. So far so good. What about one zero where x is true and y is false? Well, x is true. So we need to return x, and so we return one. And this still matches our regular truth table. Finally, if we take both of them equal to true, well, x is true. So we still return x, which means we return. True. Okay. Now, one thing to note here is that if you look at what's happening, if X is truthy, we return X. Otherwise we return y. Did you notice that we never actually look at the value of Y? We never do right. And if you look at the truth table, that kind of makes sense, because if X is true, well, we're always going to return true. It doesn't matter what Y is. Now, if X is false, then we just return Y. We don't have to do another operation to find the Or. If X is false, we know that the result is going to then be totally dependent on Y. So a better way to define this in terms of what's actually happening is that if X is truthy, we return X. Otherwise, we evaluate Y and return it. And the reason why I want to write it this way is because now you'll notice that we don't actually evaluate Y unless we need to return it. And you remember this whole short circuiting thing we talked about in the last video. That's what's going on. That's why y is not getting evaluated unless it has to. Right. So if the first part of the the first operand is true, then we just return. True. But we never evaluate the second part. If the first operand is false, then we have to evaluate and return y because y doesn't have to be a pure number or a pure boolean. It could just be an expression, it could be a function call or something that is that returns either a boolean or an object that has an associated truth value. So that's the reason why we have this definition here, and it actually works. It's kind of pretty cool actually. And in fact, the Or and the and operators return an object. They return one of your operands, either X or Y, depending on whether X is truthy or not. So let's look at an and is defined very much in the same way. But if you think of the and well, if the X is false, we always return false. If x is true, then we have to return y. You'll notice that these 20101 match here and the 0000 match here. So if you think about it a little bit, you can probably come up with the definition yourself, which is if X is false, return X. Otherwise, return Y. All right. So let's take a look. So X and y. If X is false, we return x. Otherwise, return Y. So again, does this work as expected with Boolean values? So let's take a look. Let's just run through it very quickly. If we have false, well, X is false, right? So we return X, so our result is false. That matches next one. If x is false and y is true, well, it still doesn't matter. We should still return x, right? According to this rule and we return zero which matches what we have in the truth table. If x is true and y is false, well, x is true. So now we have to return y. So in this case we'll return zero, which again matches our truth table. And similarly, if both are true, then because X is true, we have to return Y and therefore we return true, which is exactly what we have in the truth table. So this checks out as well. Now, for the same reason that we discussed with the Or why doesn't actually get evaluated unless it gets returned. So again, we have this short circuiting because if X is false, then we are just going to get false back. And this Y will never get evaluated. So our definition really is if X is false, we return X, otherwise evaluate Y and return it. And again, we have the short circuiting and this will then return the objects. The operands that we pass in the operands could be booleans, in which case we get the usual normal results, or we could pass in any object. So let's look at the consequence of this with the Or operator first, or at least a consequence of it. So again, just bring back our definition. If X is truthy, we return X, otherwise we evaluate and return Y. So again, this does both the definition and also handles the short circuiting. So let's take a look at this example here. Let's say that X is none and Y is this string n, A, then x or Y. Well, X is Falsy So we're going to return Y, which means we return an A. Similarly, if X is an empty string and Y is an A while, the empty string is also Falsy. So we're going to return an A. On the other hand, if we take x equal to hello and y equal to n A well in this case, hello is a truthy string, right? So a truthy object. So we are going to return X, which means we return. Hello. So you'll notice that in both these cases here where we had a string that was either none or empty, we returned an A and when I say a string, we had a variable that was none or an empty string. We returned an A and in the case where our variable was a string with characters, we return the string itself. So if we write something like this A equals S or an A, then if S is none, we return n a if s is an empty string, we return na. If s is a string with characters, we just return s right and we assign that to a. So this is basically a way to set up default values for a variable, right? So we may be dealing with a string and we don't want to have an empty or none variable there. So we can do S equals S or an A, and this will ensure that A is always going to be a string, right? It's always going to be well, it's always going to have something in it. Right? This is making the assumption that's is a string or is pointing to a string. But anyways, you get the idea. So we can use this for setting up default values. So let's take a look at an example. Because we can expand this further. We can say a equals S1 or S2 or S3 or Na. And this will then be. So A will be equal to the first truth value. And it's the first truth value because as we mentioned before, in a case like this, we have these multiple or operators. Python will then evaluate them from left to right because these are all operators with equal precedence. So it will do a left to right evaluation. So essentially it's going to grab the first truth value and since the last one is always truthy, you're ensured that A is going to be at least an A or it's going to be the first non-empty string in S1, s2 S3 So you can expand that a little further. So that's really handy when you're working with variables, and especially if you're pulling data from places that you don't really know, like maybe a database that could have nullable string fields or nullable text fields or maybe it's from, you know, an API feed from somewhere, whatever it is, it can be really handy to do things like this. Let's take a look at another example. Let's say we have an integer variable that cannot be zero for some reason or other. We have this integer and we don't want it to be zero. And if it is zero, we want to set it to one. Now, obviously you could write an if statement. If A is equal equals zero, then a equals one you could do something like that. Or since zero is a falsy value, we can just write a equals A or one. It's going to be the same thing. It's going to return one if A is zero or it's going to return a if A is non-zero. So again, pretty handy to do. Let's look at some consequences of and now and what we can do with it. So again, just recall that X and y. If x is falsy, we return x. Otherwise we evaluate and return y. So let's look at this example. Let's say X has a is an integer with a value of ten and Y is 20 divided by x, then x and Y will return y. Right? Since x is truthy, we will return y. We will then evaluate y, which is 20 divided by two by ten, which is two, and that's what gets returned from the X and Y. On the other hand, let's say that x was zero and y is also 20 over x, then it's going to return zero because x is Falsy It's going to return that. It will not even try to evaluate 20 divided by x. So it looks like we were able to avoid this division by zero error by using the and operator. And this is a pretty standard way as well. We can say X equals A and total divided by a, let's say we're trying to calculate an average where we have maybe a sum like total and a count the number of samples, which is a and at any point in time, you know, we could have a count that A be zero. We don't know. Right. Maybe it's right at the beginning. We haven't entered data yet. We're trying to compute the average. Well, this will then return a, it will not try to do the division by zero. But if A is greater than zero, then it will do the actual computation. So again, very handy to have. So as an example, if we have a equals ten, well ten and total divided by ten, that's going to return total divided by ten. Right. But if A is equal to zero, then we have zero and total divided by zero that's going to return zero. Okay. So that avoids basically that division by zero error. Again, that's a pretty standard trick that can be used in Python. Let's take that example with the average that I was talking about. So let's say we have some and n so sometimes N is non zero, sometimes it is. In either case, the average is N and some divided by n, because if n is zero, we have no count right then the average is zero. So we want to return zero. On the other hand, if we do have values, so n is non zero, it's going to be some positive integer. Then we do want to take some divided by n that will be the average. Here's another example. Let's say you want to return the first character of a string s or an empty string if the string is none or empty. In other words, we have a string and we want to bring back the first character of that string. But of course, if the string is none or empty, we just want to bring back the empty string. So let's see how we could do that. Here's one option if s return s zero, otherwise return the empty string. Well, yeah, that will work, right? If s is none or if s is a string with length zero, it's going to be falsy. Right? So it's going to return this empty string here. But if x is a string with characters, then its length is greater than zero, which means that it's going to be truthy and we're going to return S zero. Option number two, which works equally well and actually works maybe well, actually works equally well. We can say return S and S zero. Now that's not option to return S and S zero. Well, that's kind of okay, right? Because if s is none or the empty string, then it's going to return s because it's going to be falsy otherwise it will return the first character. So this will not create an error. We won't have an exception. This will work just fine. In all the cases. Where's is none? S is an empty string or s is a string of characters. However, what is it going to return when s is none right? It doesn't work well with the non case. It's not going to do what we want it. We want it to return an empty string. If the string is none here, it's going to return none. If s is none. So how can we fix that? Well, we can take whatever this returns and we can order it with the empty string so that if the return of this thing here is falsy, which means none or the empty string, we're going to replace it with the empty string so we can do it this way. Return S and S is zero or the empty string. And that will do exactly what we want. In fact, you can make this default value now whatever you want, so you can have it will return the first character of the string. Or maybe it returns an A if you want, if it's an empty or none variable. And of course Bob thinks that's really cool, you know, I do too. But that's kind of a cool way of doing this. It's, you know, it's a one liner and once you get used to it, it's very easy to recognize this pattern and to understand it. And you don't have, you know, as worthy code as you have over here. So lastly, let's look at the Boolean, not operator. Now, the not is really a built in operator that returns a Boolean value. So it doesn't work quite the same way as and, and or where and and or were returning objects. Right. It doesn't do that. It actually returns a, you know, honest Boolean value. So if you do not X, then the way it works is that if x is falsy, then it will return. True, right? Because x is false. So we return not false, which is true. And it will return false. The boolean false if x is truthy, so it can still take any object here, but the not will actually return the not of the truth value of the object. So if we take an empty string, for example, which is falsy, then not an empty string, it's an empty list. It's still an empty sequence then not this empty sequence will return. True. On the other hand, if the sequence contains objects, it is truthy and therefore not of that will actually evaluate to false. Similarly, if none is falsy, then not none will be true. So let's go ahead and actually switch to some code and we'll take a look at all of that in action in the next video. So thanks for watching and I'll see you in a bit.