# Day 12 Exercise Solutions Here are our solutions for the day 12 exercises in the 30 Days of Python series. Make sure you try the exercises yourself before checking out the solutions!

### 1) Define four functions: `add`, `subtract`, `divide`, and `multiply`. Each function should take two arguments, and they should print the result of the arithmetic operation indicated by the function name.

There are a couple of additional details we have to keep in mind for this problem.

First, for some operations, the order of the operands is important. `12 / 4` is not the same thing as `4 / 12`, after all, while `5 + 7` and `7 + 5` are identical operations. In cases where the order of the operands is important, we should treat the first argument to the function and the left operand, and the second argument as the right operand.

This means that `12 / 4` will be called as `divide(12, 4)`.

The other thing we have to keep in mind is that division by `0` is going to produce an exception. If we try, we're going to get a `ZeroDivisionError`:

``````Traceback (most recent call last):
File "main.py", line 1, in <module>
8 / 0
ZeroDivisionError: division by zero
``````

There's nothing wrong with letting this error bubble up to the surface, but in our case we're going to check if the second operand for division is `0`, and we're going to print a message to the user rather than crashing the program.

Let's start by defining the `add` function. You can use whatever parameter names you like for these functions. I'm just going to call the operands `a` and `b`.

``````def add(a, b):
print(a + b)
``````

Here we've defined two parameters, which means `add` requires two arguments. These argument values get assigned to our parameters when we call the function, and we can refer to these values using the parameter names in our function body.

We're going to assume the user passes us numbers, so we can just print the result of the expression, `a + b`.

`subtract` and `multiply` are very much the same, but we have to be careful to keep the order of the operands correct for `subtract`:

``````def add(a, b):
print(a + b)

def subtract(a, b):
print(a - b)

def multiply(a, b):
print(a * b)
``````

Don't forget to put two empty lines between the function definitions!

`divide` is ever so slightly more complicated, because we have to check that the second parameter is not `0`. We can accomplish this with a simple conditional statement:

``````def add(a, b):
print(a + b)

def subtract(a, b):
print(a - b)

def multiply(a, b):
print(a * b)

def divide(a, b):
if b == 0:
print("You can't divide by 0!")
else:
print(a / b)
``````

You might be thinking, what if `b` is `0.0`? The condition will still work in this case. Python is smart enough to know that `0.0` and `0` are the same thing.

Make sure to test all of your functions work correctly, and make sure you test both a zero and non-zero value for `divide`.

### 2) Define a function called `print_show_info` that has a single parameter. The argument passed to it will be a dictionary with some information about a T.V. show. The `print_show_info` function should print the information stored in the dictionary, in a nice way.

The goal of this exercise is to define a function called `print_show_info` that can take in a dictionary like this:

``````tv_show = {
"seasons": 5,
"initial_release": 2008
}
``````

And output a string like this:

``````Breaking Bad (2008) - 5 seasons
``````

Our `print_show_info`, and it's going to take a single parameter, which I'm going to call `show`. `show` is going to expect a dictionary in the format above.

``````def print_show_info(show):
pass
``````

Here I've written `pass`, which just means "do nothing". It's useful for when we're creating structures in our code that require a body, but we don't know what it should be yet. If we leave it blank, Python will complain, so `pass` is a very useful placeholder.

In this case, we want our function to print the show data in a nice format, and I'm going to use the example above as a template. We can just access the values in the provided dictionary by accessing keys of `show`.

``````def print_show_info(show):
print(f"{show['title']} ({show['initial_release']}) - {show['seasons']} season(s)")
``````

Now we just have to call our function!

``````def print_show_info(show):
print(f"{show['title']} ({show['initial_release']}) - {show['seasons']} season(s)")

tv_show = {
"seasons": 5,
"initial_release": 2008
}

print_show_info(tv_show)
``````

### 3) Below you’ll find a list containing details about multiple TV series. Use your function, print_show_info, and a for loop, to iterate over the series list, and call your function once for each iteration, passing in each dictionary.

``````series_data = [
{"title": "Breaking Bad", "seasons": 5, "initial_release": 2008},
{"title": "Fargo", "seasons": 4, "initial_release": 2014},
{"title": "Firefly", "seasons": 1, "initial_release": 2002},
{"title": "Rick and Morty", "seasons": 4, "initial_release": 2013},
{"title": "True Detective", "seasons": 3, "initial_release": 2014},
{"title": "Westworld", "seasons": 3, "initial_release": 2016},
]
``````

Since we already have our function defined, there's not much for us to do other than write the actual loop. I'm going to use `show` as a loop variable, so my loop definition is going to look like this:

``````for show in series_data:
pass
``````

Now we just have to call our function in the loop body, passing in `show` as an argument, since one of our dictionaries is going to get assigned to `show` for each iteration of the loop:

``````for show in series_data:
print_show_info(show)
``````

The full solution therefore looks like this:

``````def print_show_info(show):
print(f"{show['title']} ({show['initial_release']}) - {show['seasons']} season(s)")

series_data = [
{"title": "Breaking Bad", "seasons": 5, "initial_release": 2008},
{"title": "Fargo", "seasons": 4, "initial_release": 2014},
{"title": "Firefly", "seasons": 1, "initial_release": 2002},
{"title": "Rick and Morty", "seasons": 4, "initial_release": 2013},
{"title": "True Detective", "seasons": 3, "initial_release": 2014},
{"title": "Westworld", "seasons": 3, "initial_release": 2016},
]

for show in series_data:
print_show_info(show)
``````

This should give us output like this:

``````Breaking Bad (2008) - 5 season(s)
Fargo (2014) - 4 season(s)
Firefly (2002) - 1 season(s)
Rick and Morty (2013) - 4 season(s)
True Detective (2014) - 3 season(s)
Westworld (2016) - 3 season(s)
``````

### 4) Create a function to test if a word is a palindrome. A palindrome is a string of characters that are identical whether read forwards or backwards.

This exercise is a little bit tricky, and it's actually a fairly common interview question. We have a few different ways of going about this, and I'll show off a few different methods.

First things first, we need to define our function, which is going to take in a `word`. We're going to want to clean this string up a bit, by removing any whitespace and changing all the characters to the same case.

``````def is_palindrome(word):
word = word.strip().lower()
``````

From here, we can take a number of approached. The first approach I want to show is using lists and the `reversed` function.

We have to be a little careful with `reversed`, because it's going to give us a lazy type, and its value is not going to be equivalent to a string in any case. We're going to have get the characters out of the `reversed` object. We're going to do this by converting it to a list in this case.

``````def is_palindrome(word):
word = word.strip().lower()
reversed_word = reversed(word)

if list(word) == list(reversed_word):
print(True)
else:
print(False)
``````

Note that for the comparison we also have to convert the `word` to a list. That's because Python doesn't consider something like `"hannah"` to be the same as `["h", "a", "n", "n", "a", "h"]`.

We can test our function with a few cases like so:

``````def is_palindrome(word):
word = word.strip().lower()
reversed_word = reversed(word)

if list(word) == list(reversed_word):
print(True)
else:
print(False)

is_palindrome("Hannah")  # True
is_palindrome("Fred")    # False
``````

That all seems to be working.

Instead of using lists, we could also use `join` to create a string from the `reversed` object:

``````def is_palindrome(word):
word = word.strip().lower()
reversed_word = reversed(word)

if word == "".join(reversed_word):
print(True)
else:
print(False)

is_palindrome("Hannah")  # True
is_palindrome("Fred")    # False
``````

Here our `join` string is empty, which means we want nothing to go between the items in `word` when we create our new string.

As you can see, the result is the same.

A similar approach uses the `reverse` method for list. In this case, we turn `word` to a list, and we create a copy of it. We can then reverse this copy and compare the lists.

We have to be careful with this approach though, because we just want a copy of the values. If we try to do something like this:

``````def is_palindrome(word):
word = list(word.strip().lower())
reversed_word = word
reversed_word.reverse()
``````

Both names refer to the same list. If we reverse that list, both names now refer to the same reversed list.

There are a couple of ways around this. First, we can pass the `word` to `list` when performing the `reversed_word` assignment:

``````def is_palindrome(word):
word = list(word.strip().lower())
reversed_word = list(word)
reversed_word.reverse()
``````

`list` creates a new list, so we create a second list with identical values. Lists also have a method called `copy` for doing this as well:

``````def is_palindrome(word):
word = list(word.strip().lower()):
reversed_word = word.copy()
reversed_word.reverse()
``````

Either of these is fine. We can then perform a direct comparison of the lists:

``````def is_palindrome(word):
word = list(word.strip().lower())
reversed_word = word.copy()
reversed_word.reverse()

if word == reversed_word:
print(True)
else:
print(False)

is_palindrome("Hannah")  # True
is_palindrome("Fred")    # False
``````

The final method I want to show you is using slices, as they offer an extremely elegant solution in this case:

``````def is_palindrome(word):
word = word.strip().lower()

if word == word[::-1]:
print(True)
else:
print(False)

is_palindrome("Hannah")  # True
is_palindrome("Fred")    # False
``````

The `[::-1]` is covered in another blog post, but it basically means, "give me the whole sequence in reverse order".

This means we can check `word` against the reverse of `word` without needing to do any type conversions, or using `join`.

If you prefer, we could use variable names to better describe what this value means:

``````def is_palindrome(word):
word = word.strip().lower()
reversed_word = word[::-1]

if word == reversed_word:
print(True)
else:
print(False)

is_palindrome("Hannah")  # True
is_palindrome("Fred")    # False
``````

If you want to have a go at a more complicated version of this exercise, check out our post on finding palindromes. We walk through a lot of these solutions in more detail, and we talk about finding palindromes when punctuation is involved, or when we're dealing with full sentence palindromes.