soup_is_beautiful = True
soup_is_bad = False

"""
Explanation: Next Steps with Python
You've now worked through first steps with Python and are ready to advance your Python chops. This notebook will help you accomplish this goal by taking you through a number of the next most important skills in Python programming:

Booleans
Conditionals
Dictionaries
Functions

Let's begin!
Data Type: Booleans
Just like strings and lists, booleans are another basic data type in Python. Booleans are arguably the simplest of all data types, because they can only be True or False. Let's see some booleans in action:
End of explanation
"""


a = 7
b = 9
print(a > b)

"""
Explanation: In the examples above, we explicitly set a variable to True or False. Sometimes though, we need to check to see if something is true, such as the following example:
End of explanation
"""


a = 'soup'
b = 'soup'

print(a == b)

"""
Explanation: We can see above that checking whether a is greater than b returns False, because 7 is not greater than 9.
Just as we can use greater than or less than syntax to check if something is True, we can also check to see if two items are equal, and those checks return a boolean as well:
End of explanation
"""


a = 'soup'
b = 'Soup'

print(a == b)

print(a == b.lower())

print(b == 's' + 'o' + 'u' + 'p')

# Note the capitalization of B!

"""
Explanation: Note that when we check for equality between two items, we must use two equals signs.
To check your understanding of booleans, see if you can predict whether the following cells will display True or False:
End of explanation
"""


if (9 > 7):
  print('9 is in fact bigger than 7!')

"""
Explanation: Conditionals
Conditionals let us change the code we execute depending on the state of some elements in our program. In general, a Python conditional looks like this:
if True:
  do_something()
Let's look at some examples:
End of explanation
"""


if (7 > 9):
  print('7 is bigger than 9')
else:
  print('7 is not bigger than 9')

"""
Explanation: If clauses can also contain else clauses. An else clause will run if the if clause to which it's connected does not evaluate to True:
End of explanation
"""


a = True
b = False

if a:
  print('a is true')
else:
  if b:
    print('a is false and b is true')
  else:
    print('a is false and b is false')

"""
Explanation: We can chain if and else blocks together to form complex sequences:
End of explanation
"""


a = 'soup'
b = 'Soup'

if a == b:
  print('a == b')
else:
  if a == b.lower():
    print('a == b.lower()')
  else:
    print('a != b and a != b.lower()')

"""
Explanation: Try changing the values of a and b above to see what gets printed!
Once you have a grasp on conditionals, try to predict what will print in the following example:
End of explanation
"""


cheshire_cat = {'is_grinning': True}

"""
Explanation: Data Type: Dictionaries
Dictionaries are another basic data type in Python, and are in fact among the most powerful of all basic data types because they allow us to express hierarchical data. Let's look at an example dictionary:
End of explanation
"""


cheshire_cat['is_grinning']

"""
Explanation: As we can see above, dictionaries start and end with "squiggly braces" (also called "curly braces"). Inside those squiggly braces, the value to the left of the colon is known as a key in the dictionary, and the value to the right is known as the value of that key. 
We can access the value assigned to some key with the following syntax:
End of explanation
"""


cheshire_cat = {'is_grinning': True, 'is_mischievous': True}

"""
Explanation: In some cases we might have a dictionary with multiple keys:
End of explanation
"""


if 'is_grinning' in cheshire_cat:
  print('The dictionary cheshire_cat in fact contains the key is_grinning')

"""
Explanation: Furthermore, we can check if a key exists in a dictionary with the in operator:
End of explanation
"""


cheshire_cat.keys()

"""
Explanation: If instead we wish to obtain a list of all keys in the dictionary, we can do so using the .keys() method:
End of explanation
"""


cheshire_cat = {
  'is_grinning': True, 
  'color': {
    'fur': 'pink', 
    'eyes': 'yellow',
  }
}

"""
Explanation: Importantly, the values inside of a dictionary may themselves be dictionaries. Consider the following example:
End of explanation
"""


fur_color = cheshire_cat['color']['fur']
print(fur_color)

"""
Explanation: To access the color of the fur attribute inside the cheshire_cat, we can use the following series of keys:
End of explanation
"""


# type your code here
eye_color = cheshire_cat['color']['eyes']
print(eye_color)

"""
Explanation: To test your understanding of dictionaries, see if you can access the eyes attribute inside the cheshire_cat below:
End of explanation
"""


def add_one(some_number):
  result = some_number + 1
  return result

"""
Explanation: Functions
Functions allow us to rewrite blocks of code into a single discrete action and then run that action as often as we might like. This means that functions allow us to significantly improve the "readability" of our code, and to organize our code quite nicely. Let's see a sample function:
End of explanation
"""


def add_one(some_number):
  result = some_number + 1
  return result

output = add_one(7)
print(output)

"""
Explanation: As we can see above, function declarations start with def followed by the function name itself. One then specifies the "arguments" (also called "parameters") that the function takes in parentheses. Any arguments/parameters specified between the parentheses are inputs that the user will provide to the function--we'll see this in action in just a moment. For now, we should note that inside the function, we can interact with the arguments the user provided. After we're done interacting with those arguments, we can return some value, which can be saved outside the function for subsequent use.
Let's make this more concrete with an example:
End of explanation
"""


def add_one(some_number):
  result = some_number + 1
  return result

output = add_one(23)
print(output)

"""
Explanation: When the lines above are evaluated, the value assigned to some_number inside the add_one function will be 7, as that is the value we pass into the add_one function. Inside the function, therefore, some_number will be equal to 7 in the example above. result is therefore 7 + 1. We return that result, which assigns the value 8 to output, as we can verify with the printed output.
Let's compare the example above with the one below:
End of explanation
"""


# type your code here
def subtract_one(some_number):
  result = some_number - 1
  return result

subtract_one(25)

"""
Explanation: Here we can see that providing a new value to the add_one function results in the same logic being applied to that input. We still get the input value + 1 provided as output from the function.
Using what you learned about functions above, see if you can write a function that subtracts one from a user-provided input below:
End of explanation
"""


authors = [
  {
    "first_name": "William",
    "last_name": "Shakespeare",
  },
  {
    "first_name": "Agatha",
    "last_name": "Christie",
  },
  {
    "first_name": "Lewis",
    "last_name": "Carroll",
    "publications": ["Alice's Adventures in Wonderland", "A Syllabus of Plane Algebraic Geometry"]
  }
]

# write your function here
def has_more_than_two_keys(dictionary):
  if len(dictionary.keys()) > 2:
    return True
  else:
    return False
  
for i in authors:
  print(has_more_than_two_keys(i)) # we expect False False True

"""
Explanation: <h3 style='color: green'>Review #3</h3>

Let's now test your understanding of the concepts in this notebook with the following challenge: 
In the example below, see if you can write a function that takes as input a single dictionary, and returns True if that dictionary contains more than 2 keys and False otherwise. Then test your function with the dictionaries in authors.
End of explanation
"""


