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 = {
	"title": "Breaking Bad",
	"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 = {
	"title": "Breaking Bad",
	"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.