"""
## 1. Arithmetic operators (follows PEMDAS rule)
+,-,*,/,%,**,//
## 2. Assignment operators
=,+=,-=,/*=,/=,%=,//=,**=,&=,|=,^=,>>=,<<=
## 3. Comparison operators
==,!=,>,<,>=,<=
## 4. Bitwise Operators
&,|,^,~,<<,>>
## 5. Logical Operators
not,and,or
## 6. Identity Operators
is,is not
## 7. Membership Operators
in,not in
"""
## not: To negate the underlying condition (it reverses the condition).
a = 30
# here the underlying condition is 'a > 10', so normally the 'if' condition
# executes when the underlying condition is returns 'True' right?, but by applying
# 'not' to it the 'if' condition is only satisfied when the output is 'False'
if not a > 10:
print("not printed")
if not a > 40:
print("printed")
# here 'a' is not greater than 40, so the second condition is executed
## 'and' is similar to '&&' in C/C++/Java: Both conditions should be satisfied.
a = 20
b = 30
if a > 10 and b < 50:
print("printed")
if a > 42 and b < 50:
print("not printed")
## 'or' is similar to '||' in C/C++/Java: Either of conditions should
# be satisfied.
if a > 10 or b < 10:
print("printed")
if a > 20 or b < 30:
print("not printed")
## There is one more thing called short circuit evaluation/logic,
# it means if first condition is not satisfied next one doesn't matter
# Here's a example, we'll set 'r' to 'None', so 'if r' will return 'False'
# and the next condition will not be executed
r = None
if r and p:
print("something")
# Notice: 'p' is not even defined here, we should get a 'NameError',
# but as 'if r' gave 'False', 'and p' was not executed,
# so no error, try changing 'r's value to a number
## is: Checks if two objects have the same identity.
my_var1 = 42
my_var2 = 42
# checking values with '=='
if my_var1 == my_var2:
print("printed")
# now checking if they are referring to same object
if my_var1 is my_var2:
print("printed")
# Another example
x = 500
y = 500
# let's check with 'is' operator
if x is y:
print("not printed")
# oops?
# When a object is created the Python Interpreter assigns it a unique number,
# known as its identity. Note: ids can vary on each machine.
# This id is what the 'is' operator checks, we can print the id of a object
# using 'id()' built-in function. let's check the id of 'my_var1' and 'my_var2'
print(id(my_var1)) # 1991186280016
print(id(my_var2)) # 1991186280016
# they have same ids, that is why 'if my_var1 is my_var2' condition was executed
# now let's check id for 'x' & 'y'
print(id(x)) # 140544545318224
print(id(y)) # 140544545318416
# they don't have same ids,
# This is because the Python interpreter creates values in range -5 to 256
# at the beginning of the program and so when you create a variable in this
# range (like we did, 42) you're only referencing them.
# So they are the same objects having the same ids each time. This is not the
# case with 500, they are not from that range so each time they are
# re-created as new objects. Python does this because values in this range
# are frequently used, so this helps to gain some performance boost
## is not: Negate the 'is' condition, working is similar as we saw above.
a = 3
b = 4
if a is not b:
print("printed")
# or similarly
if not a is b:
print("printed")
## in: Used to check if a value is inside a sequence or not,
# returns a boolean value.
# Consider a list containing some values, we'll learn about lists
# in details later, for now think of it as an array
my_list = [23, 5, 32, 65, 20]
# check with 'in' operator if 'my_list' contains the value 20,
# yes so 'True' is returned
if 20 in my_list:
print("printed")
# or can do
print(20 in my_list) # True
# similarly check if it contains 30, nope so 'False'
print(30 in my_list) # False
## not in: Negate the 'in' condition, same as above but returns opposite values.
# consider the same list, we'll check values again
# checking if 10 'not in' 'my_list'
print(10 not in my_list) # True
# this spells as 10 is not in 'my_list', which is 'True'
# checking if 32 'not in' 'my_list', which is 'False'
print(32 not in my_list) # False