Tracks
/
Python
Python
/
Exercises
/
Guido's Gorgeous Lasagna
Guido's Gorgeous Lasagna

Guido's Gorgeous Lasagna

Learning Exercise

Introduction

Python is a dynamic and strongly typed programming language. It employs both duck typing and gradual typing via type hints.

While Python supports many different programming styles, internally everything in Python is an object. This includes numbers, strings, lists, and even functions.

We'll dig more into what all of that means as we continue through the track.

This first exercise introduces 4 major Python language features:

  1. Name Assignment (variables and constants),
  2. Functions (the def keyword and the return keyword),
  3. Comments, and
  4. Docstrings.

Note

In general, content, tests, and analyzer tooling for the Python track follow the style conventions outlined in PEP 8 and PEP 257 for Python code style, with the additional (strong) suggestion that there be no single letter variable names or variables named "_".

On the Python track, variables are always written in snake_case, and constants in SCREAMING_SNAKE_CASE.


Name Assignment (Variables & Constants)

Programmers can bind names (also called variables) to any type of object using the assignment = operator: <name> = <value>. A name can be reassigned (or re-bound) to different values (different object types) over its lifetime.

>>> my_first_variable = 1  #<-- my_first_variable bound to an integer object of value one.
>>> my_first_variable = 2  #<-- my_first_variable re-assigned to integer value 2.

>>> print(type(my_first_variable))
<class 'int'>

>>> print(my_first_variable)
2

>>> my_first_variable = "Now, I'm a string." #<-- You may re-bind a name to a different object type and value.
>>> print(type(my_first_variable))
<class 'str'>

>>> my_first_variable = 'You can call me "str".' #<-- Strings can be declared using single or double quote marks.
>>> print(my_first_variable)
You can call me "str".

Constants

Constants are names meant to be assigned only once in a program. They should be defined at a module (file) level, and are typically visible to all functions and classes in the program. Using SCREAMING_SNAKE_CASE signals that the name should not be re-assigned, or its value mutated.

Functions

The def keyword begins a function definition. Each function can have zero or more formal parameters in () parentheses, followed by a : colon. Statements for the body of the function begin on the line following def and must be indented in a block.

# The body of a function is indented by 2 spaces, & prints the sum of the numbers.
def add_two_numbers(number_one, number_two):
  total = number_one + number_two
  print(total)  

>>> add_two_numbers(3, 4)
7


# Inconsistent indentation in your code blocks will raise an error.
>>> def add_three_numbers_misformatted(number_one, number_two, number_three):
...     result = number_one + number_two + number_three   # This was indented by 4 spaces.
...    print(result)     #this was only indented by 3 spaces
...
...
  File "<stdin>", line 3
    print(result)
    ^
IndentationError: unindent does not match any outer indentation level

Functions explicitly return a value or object via the return keyword:

# Function definition on first line, explicit return used on final line.
>>> def add_two_numbers(number_one, number_two):
        return number_one + number_two   


# Calling the function in the Python shell returns the sum of the numbers.
>>> add_two_numbers(3, 4)
7

# Assigning the function call to a variable and printing it 
# will also return the value.
>>> sum_with_return = add_two_numbers(5, 6)
>>> print(sum_with_return)
11

Functions that do not have an explicit expression following a return will implicitly return the None object. The details of None will be covered in a later exercise. For the purposes of this exercise and explanation, None is a placeholder that represents nothing, or null:


# This function will return `None`
def square_a_number(number):
    square = number * number
    return # <-- note that this return is not followed by an expression

# Calling the function in the Python shell appears 
# to not return anything at all.
>>> square_a_number(2)
>>>


# Using print() with the function call shows that 
# the function is actually returning the **None** object.
>>> print(square_a_number(2))
None

Functions that omit return will also implicitly return the None object. This means that if you do not use return in a function, Python will return the None object for you.

# This function omits a return keyword altogether.
def add_two_numbers(number_one, number_two):
  result = number_one + number_two

>>> add_two_numbers(5, 7)
>>> print(add_two_numbers(5, 7))
None

# Assigning the function call to a variable and printing 
# the variable will also show None.
>>> sum_without_return = add_two_numbers(5, 6)
>>> print(sum_without_return)
None

Calling Functions

Functions are called or invoked using their name followed by (). Dot (.) notation is used for calling functions defined inside a class or module.

>>> def raise_to_power(number, power):
...     return number ** power
...

>>> raise_to_power(3,3) # <--Invoking the function with the arguments 3 and 3.
27


# A mismatch between the number of parameters and the number of arguments will raise an error.
>>> raise_to_power(4,)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: raise_to_power() missing 1 required positional argument: 'power'


# Calling methods or functions in classes and modules.
>>> start_text = "my silly sentence for examples."
>>> str.upper(start_text)  # <--Calling the upper() method from the built-in str class on start_text.
'MY SILLY SENTENCE FOR EXAMPLES.'


# Importing the math module
>>> import math

>>> math.pow(2,4)  # <--Calling the pow() function from the math module.
16.0

Comments

Comments in Python start with a # that is not part of a string, and end at line termination. Unlike many other programming languages, Python does not support multi-line comment marks. Each line of a comment block must start with the # character.

Docstrings

The first statement of a function body can optionally be a docstring, which concisely summarizes the function or object's purpose. Docstrings are declared using triple double quotes (""") indented at the same level as the code block:


# An example from PEP257 of a multi-line docstring
# reformatted to use Google style non-type hinted docstrings.
# Some additional details can be found in the Sphinx documentation:
# https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#getting-started

def complex(real=0.0, imag=0.0):
    """Form a complex number.

    Keyword Arguments:
        real (float): The real part of the number (default 0.0)
        imag (float): The imaginary part of the number (default 0.0)
        
    """

    if imag == 0.0 and real == 0.0:
        return complex_zero

Docstrings are read by automated documentation tools such as Sphinx and are returned by calling the special attribute .__doc__ on the function, method, or class name. General docstring conventions are laid out in PEP257, but exact formats will vary by project and team. Exercism concept exercises try to follow the Google style for un-type hinted code.

Docstrings can also function as lightweight unit tests, which will be covered in a later exercise.

# An example on a user-defined function using a Google style docstring. 
>>> def raise_to_power(number, power):
    """Raise a number to an arbitrary power.

    Parameters:
        number (int): The base number.
        power (int): The power to raise the base number to.
    
    Returns:
        int: The number raised to the specified power.
    
    Takes a number and raises it to the specified power, returning the result.

    """

    return number ** power
...

# Calling the .__doc__ attribute of the function and printing the result.
>>> print(raise_to_power.__doc__)
Raise a number to an arbitrary power.

Parameters:
    number (int): The base number.
    power (int): The power to raise the base number to.

Returns:
    int: The number raised to the specified power.

Takes a number and raises it to the specified power, returning the result.

Instructions

You're going to write some code to help you cook a gorgeous lasagna from your favorite cookbook.

You have five tasks, all related to cooking your recipe.


Note

We have started the first function definition for you in the stub file, but you will need to write the remaining function definitions yourself. You will also need to define any constants yourself. Read the #TODO comment lines in the stub file carefully. Once you are done with a task, remove the TODO comment.


1. Define expected bake time in minutes as a constant

Define the EXPECTED_BAKE_TIME constant that represents how many minutes the lasagna should bake in the oven. According to your cookbook, the Lasagna should be in the oven for 40 minutes:

>>> print(EXPECTED_BAKE_TIME)
40

2. Calculate remaining bake time in minutes

Complete the bake_time_remaining() function that takes the actual minutes the lasagna has been in the oven as an argument and returns how many minutes the lasagna still needs to bake based on the EXPECTED_BAKE_TIME constant.

>>> bake_time_remaining(30)
10

3. Calculate preparation time in minutes

Define the preparation_time_in_minutes() function that takes the number_of_layers you want to add to the lasagna as an argument and returns how many minutes you would spend making them. Assume each layer takes 2 minutes to prepare.

>>> def preparation_time_in_minutes(number_of_layers):
        ...
        ...
        
>>> preparation_time_in_minutes(2)
4

4. Calculate total elapsed time (prepping + baking) in minutes

Define the elapsed_time_in_minutes() function that takes two parameters as arguments:

  • number_of_layers (the number of layers added to the lasagna)
  • elapsed_bake_time (the number of minutes the lasagna has spent baking in the oven already).

This function should return the total minutes you have been in the kitchen cooking β€” your preparation time layering + the time the lasagna has spent baking in the oven.

>>> def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time):
        ...
        ...
        
>>> elapsed_time_in_minutes(3, 20)
26

5. Update the recipe with notes

Go back through the recipe, adding "notes" in the form of function docstrings.

def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time):
    """Calculate the elapsed cooking time.
    
    Parameters:
        number_of_layers (int): The number of layers in the lasagna.
        elapsed_bake_time (int): Time the lasagna has been baking in the oven.
    
    Returns:
        int: The total time elapsed (in minutes) preparing and baking.

    This function takes two integers representing the number of lasagna 
    layers and the time already spent baking the lasagna. It calculates 
    the total elapsed minutes spent cooking (preparing + baking).
    
    """
Edit via GitHub The link opens in a new window or tab
Python Exercism

Ready to start Guido's Gorgeous Lasagna?

Sign up to Exercism to learn and master Python with 17 concepts146 exercises, and real human mentoring, all for free.