Chapter 4: Working With Variables

Notes

Variables in Python

  • Variables let us add memory to python
    • Can be viewed as an alias for a storage location
  • Variables in python are created by defining a name, and providing a value, e.g. the below defines a variable total and assigns it the value \(0\)
total = 0

block-beta
    columns 3
    space
    title["Anatomy of Variable Assignment"]
    space

    block:variable
    columns 1
        varName["total"]
        variablebody["variable"]
        variableDescr["(thing to which value assigned)"]
    end

    block:equals
    columns 1
        equalsSymbol["="]
        equalsName["equals"]
        equalsDescr["(means assign)"]
    end

    block:argumentblock
    columns 1
        zero["0"]
        zeroName["expression"]
        zeroDescr["(value to assign)"]
    end

classDef BG stroke:transparent, fill:transparent
class title BG
class variablebody BG
class variableDescr BG
class equalsName BG
class equalsDescr BG
class zeroName BG
class zeroDescr BG

  • The above diagrammatically breaks down variable assignment. On the left we have the variable, then = which means assignment, followed by the expression to assign
  • The assignment expression does not need to be made of simple primitives, e.g. total = us_sales + world_wide_sales defines the variable total to be equal to the sum of the variables us_sales and world_wide_sales

Make Something Happen: Working with Variables

Start up a python interpreter, then run the following commands in sequence

  1. total = 0

     total = 0
    • No output is generated, the assignment is performed silently
  2. total

     total
    0
    • 0 output
    • total has been assigned 0, putting the variable name into the terminal returns the value associated with this value
  3. total = total + 10

     total = total + 10
    • No output
    • Like with the first statement, variable assignment generates no output
    • Here we assign total the value given by the current value of total plus \(10\)
  4. total

     total
    10
    • 10 output
    • The new value of total is printed

Python Names

  • Variables require names
  • Python variable names must obey a number of rules
    1. Must start with a letter or _
    2. Can contain letters, numbers or _ characters
  • Some valid names are
    • total
    • xyz
    • t0tal
  • Some invalid names are
    • 2_be_or_not_to_be
      • Starts with a number which is not allowed
    • tot@l
      • @ is not a valid character for a variable name
  • Python names are case sensitive, i.e. FRED and fred are distinct variables
Tip

Create Meaningful Names

Don’t use silly or meaningless names like hello_mom. Use meaningful names that convey information about what they represent like length or length_in_centimetres. The PEP8 style guide, sets out how to write and format python programs. This includes a section on naming conventions

Some languages recommend using camel case for variable naming. In this format words in a variable are distinguished by capital letters, but the first letter of the variable is lower case, e.g. lengthInCentimetres. Camel case is called this because the capital letters look like the humps of a camel. Either standard works, but when writing python it is best to stick to the python style guides

  • Python does not limit the length of variable names
    • There should be no performance cost associated with long variable names
    • Longer names can be hard to read
    • Names should be no longer than they need to convey clear meaning

Code Analysis: Typing Errors and Testing

You’ve already seen that python can generate errors when it is unable to interpret an input. Answer the following questions to investigate how errors can arise in using variables. Assume that total has already been defined

  1. Can you identify an error in the statement below, which is supposed to add \(10\) to the variable total?

     # clearing saved variables in the notebook
     %reset
     total = 0
     Total = total + 10
    • Variable names are case-sensitive. Therefore Total and total are distinct variables. So this declares a new variable Total and assigns it the value of total + 10. The value of total itself is unchanged.
    • This is a logic error, it is a legal statement in python that generates the wrong behaviour
      • Logic errors are some of the hardest errors to deal with since they (typically) don’t stop a program running, they just produce incorrect behaviour
      • Python typically cannot warn you about them ahead of time
    • This variable mis-assignment is why python style guides recommend the use of all lowercase letters for variable names
    Note

    Identifying Errors

    Other languages with more strict type systems and variable declarations, may be able to identify logic errors like the one above.

  2. How do we prevent logic errors?

    • Testing
      • i.e checking that the actual behaviour of a program matches the expected behaviour
      • Run a program with input that will generate a known output when running correctly
        • Verify the program generates this output
    • Testing doesn’t guarantee a program is correct
      • Typically hard to check all inputs and use cases
      • Does however give confidence and can provide that a program isn’t correct
    • Typically tests should be written as the program is created or if using test-driven development the tests are written before the program itself is written
  3. The statement below also contains a misspelling of the variable total. However, this time the name on the right-hand side of the equals is misspelled. What will happen when this program runs?

     # Again clear the notebook variables
     %reset
     total = 0
     total = Total + 10
    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    Cell In[9], line 2
          1 total = 0
    ----> 2 total = Total + 10
    
    NameError: name 'Total' is not defined
    • In this case, Total is not defined as a variable, so there is no meaningful way to interpret a value. Hence we would expect an error. Since the error is that Total doesn’t exist, we see a NameError.

Make Something Happen: Self-Timer Party Game

Consider the Nerves of Steel exercise from Chapter 3. In the game, players must remain standing right up to the moment before they think a random timer will expire

One suggestion is that the game might provide more skill if the program told the players how long they had to stand. The game now functions as a “Self-Timer”, the winner is now arguably the person who can best keep track of the time

The “game” sequence is

  1. Set the time to remain standing to a random number
  2. Display time to remain standing
  3. Sleep for the time to remain standing
  4. Display a message for the winner

Observe that the time is required to be used in two places, displaying the time remaining and then sleeping for that period of time. This means we need to use a variable to store the value of the time remaining

# Example 4.1: Self Timer
#
# Extends the Nerves of Steel Game from Chapter 3, by adding a skill element
# with the players being informed of how long they have to stand for

import random
import time

print("Welcome to Self Timer")
print()  # just prints a newline
print("Everybody stand up")
print("Stay standing until you think the time has ended")
print("Then sit down")
print("Anyone still standing when the time expires loses")
print("The last person to sit down before the time ended will win")

stand_time = random.randint(5, 20)  # generate the time to stand for

print("Stay standing for", stand_time, "seconds.")  # display standing time
time.sleep(stand_time)  # sleep for the standing time
print("****TIMES UP, LAST TO SIT WINS!****")
Welcome to Self Timer

Everybody stand up
Stay standing until you think the time has ended
Then sit down
Anyone still standing when the time expires loses
The last person to sit down before the time ended will win
Stay standing for 18 seconds.
****TIMES UP, LAST TO SIT WINS!****

Most of the code is just text, but the key takeaway is the line stand_time = random.randint(5, 20) which assigns stand_time the result of random.randint(5, 20), i.e a random number from 5 to 20 inclusive. We then call stand_time is our call to print to display the time, and then sleep for the same amount of time. Observe we couldn’t just write the number into both functions explicitly since it’s randomly generated, we would need to know ahead of time what the value will be

Working with Text

  • Variables can also hold text, e.g. customer_name = 'fred' assigns the string 'fred' to customer_name
  • Can use a string variable anywhere we would use a string literal, e.g. message = 'the name is ' + customer_name performs the string concatenation of 'the name is' and the value of customer_name and assigns the result to message, which we can confirm below,
  customer_name = "fred"
  message = "the name is " + customer_name
  print(message)
the name is fred

Make Something Happen: Text and Numeric Variables

Python tracks the contents of each variable and only allows operations that make sense. Using the python interpreter experiment with the following combinations of string and number variables.

customer_age_in_years = 25
customer_name = 'fred'

After entering the above two lines in the interpreter, run the following line

customer_age_in_years + customer_name
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[13], line 1
----> 1 customer_age_in_years + customer_name

TypeError: unsupported operand type(s) for +: 'int' and 'str'

Here we are trying to add a number and a string, which we saw previously shouldn’t work. The terminal should provide some error output like the below

Now try the below line

customer_age_in_years = 'Fred'

One might expect the above to generate an error, after all we assign text to a variable that was just storing a number. Instead python simply discards the old numeric value of the variable, and now stores text in customer_age_in_years. You should generally avoid doing this, being able to reason about the type of a variable is typically an important part of safe and effective programming, and the weak typing of python is typically disliked outside of quick prototyping

Note

Weak vs Strong Typing

The above behaviour is an example of python as a what’s called a weakly typed language. Types are a quite developed part of programming theory that can be discussed later. In some languages, types are enforced to greater degrees of strictness. In some languages (like C) you can assign variables compatible types, i.e. ones that can be converted to the variable’s innate type. In other stricter languages a variable can only hold the exact specified type

The more strictly a language enforces its typing system typically the harder it is to generate errors at runtime, however they are often correspondingly more painful to actually write the code in the first place

Marking the Start and End of Strings

  • Python lets you use either single-quotes (’) or double-quotes (“)
    • Lets us include ’ or ” in a string, by using the alternate delimiter
  • For example, compare the two quote snippets,
print("It's a trap")
It's a trap
  • Whereas if we tried to just use single quotes,
print('It's a trap')
  Cell In[16], line 1
    print('It's a trap')
                      ^
SyntaxError: unterminated string literal (detected at line 1)
  • The single quote in It's ends the string, and we get a syntax error detecting what should be the closing single quote as opening a new string literal
  • How do we handle the case where we have mixed quotes in a string?
    • We use triple quotes, a series of three single or double quotes in a row, see the example below
print('''...and then Luke said "It's a trap"''')
...and then Luke said "It's a trap"
  • Triple quoted strings have the added advantage of capturing newlines. To see this, we could rewrite the intro string in Nerves of Steel as
print('''Welcome to Nerves of Steel

Everybody stand up
Stay standing as long as you dare.
Sit down just before you think the time will end. ''')
Welcome to Nerves of Steel

Everybody stand up
Stay standing as long as you dare.
Sit down just before you think the time will end. 
Caution

String Delimiters must Match

You must using matching delimiters, if we try to mix them we’ll get an error

print('hello")
  Cell In[19], line 1
    print('hello")
          ^
SyntaxError: unterminated string literal (detected at line 1)

Escape Characters in Text

  • Escape sequences are another way to include quote characters
  • Extends to other symbols with meaning other than their literal character glyph in a string
  • Denoted by the \ character
Escape Sequence Meaning Effect
\\ Backslash character (\) Enter a backslash into a string
\' Single Quote (') Enter a single quote into the string
\" Double Quote (") Enter a double quote into the string
\n ASCII Line Feed/New Line End this line and take a new one
\t ASCII Tab Move to the right to the next tab stop
\r ASCII Carriage Return Return the printing position to the start of the line
\a ASCII Bell Sound the bell on the terminal
Note

ASCII

ASCII (short for American Standard Code for Information Interchange) is an old format for character encoding that covers a small range of symbols including the latin alphabet and digits.

ASCII itself is less used today since it only covers around \(100\) characters, which is nowhere near enough to cover all modern languages, before you start adding in characters like emojis. However modern text encoding like unicode are typically backwards compatible with ASCII.

Not all ASCII escape sequences may work on a modern computer. \a was designed to ring a mechanical bell on old computers. Some modern computers may play a beep while others may do nothing. Similarly \r is supposed to return the print head of a computer back to the start of the line. This has very limited use cases, and may not be implemented on all systems.

The most common escape characters are newline \n and escaping quotes

Important

Newline in Python

Python uses \n as the newline character. Technically this is known as a line feed and means “go to the next line”. In Linux and similar operating systems this is equivalent to starting a new line, but in windows going to the start of a new line is achieved via \r\n, i.e. return to the start of the line, and then feed to the next. Luckily python handles the conversion between the conventions automatically and we can just use \n regardless of the operating system we are actually running on

Code Analysis: Investigating Escape Sequences

Start a python interpreter and answer the following questions

  1. What do you think the following quote would print?

    print('hello\nworld')
    • We would expect this print hello on one line, then world on the next
    hello
    world
  2. What do you think the following would print?

     print('Item\tSales\ncar\t50\nboat\t10')
    • We can see this prints a series of tab separated lines. So this is essentially a tab separated table (a similar format to the more familiar comma-separated table)
    Item Sales
    car  50
    boat 10
    • The exact spacing of tab characters can depend on the computer system, and it is quite common for text editors to convert tabs to spaces, so this format isn’t the best. We’ll see other ways to format strings later
  3. How could I use Python escape sequences to print out this message?

    and then Luke said “It’s a trap”

    • We saw how to do this with triple-delimited strings before. Instead we just have to remember to instead use a single-delimiter string and then escape the quotes that actually form the string contents. See,
     print('and then Luke said "It\'s a trap"')
    and then Luke said "It's a trap"
  • Since the string is delimited by single quotes we only have to delimit the one single quote in It's rather than the two double quotes

Read in Text using the input Function

  • We’ve seen how to output data with print
  • We can also input data with the appropriately named input
  • The code snippet below, takes input from the user and stores it in name
  name = input()
  • The program will pause until the user supplies the input, (Try it yourself in the interpreter!)
  • We can add a display prompt to the input statement
  name = input('Enter your name please: ')
  • The above should output something like the below when run
Enter your name please: 
  • As mentioned input passes what it receives into the receiving variable.
    • If we just immediately press enter this is the empty string i.e. a string containing no characters
  • input is another technique for delaying the end of a program, e.g. the below prints a prompt and then holds the program until some input is received
input('Press enter to continue...')
Make Something Happen: Use input to make a “greeter” Program

Use python to create a simple program that will issue a personalised greeting. Create a new program (greeter.py) with the following contents

name = input('Enter your name please: ')
print('Hello', name)

Save the program and execute it to see the output. For me the program would output,

Enter your name please: Felix

Hello Felix
Tip

I’ve used colour to emphases what is my input, versus the program’s output

Working with Numbers

Convert Strings into Integer Values

  • input returns a string
    • Fine if we want that, but what if we want to handle numbers?
    • e.g. if we wanted to have a user-specified egg timer
  • int is a function that converts the argument to a whole number e.g.
time_text = input('Enter the cooking time in seconds: ') #receive time in seconds
time_int = int(time_next) #perform conversion to number
Example: Configurable Egg Timer
  • The complete program would then look like,

      # Example 4.3: Configurable Egg Timer
      #
      # Reads in a user specified time to set the timer for
    
      import time
    
      time_text = input("Enter the cooking time in seconds: ")
      time_int = int(time_text)
    
      print("Put the egg in boiling water now")
      input("Press enter to continue once the egg is in...")
    
      time.sleep(time_int)
    
      print("Take the egg out now")
  • This basic pattern works just as well for an alarm as it does for a configurable timer

Code Analysis: Reading Numbers

Consider the previous example, and answer the following questions

  1. How many variables are used in the program above?

    • two, the first time_text stores the initial text input, while the second time_int contains the converted numeric representation
  2. Could you write the program without the time_text variable?

    • Yes, we could immediately pass the input result to int, e.g.
     time_int = int(input('Enter the cooking time in seconds: '))
    • It’s an open debate which format is clearer. In the second there’s more to grok on one line, but the whole workflow is there
  3. What do you think will happen if the user enters something other than a number?

    • Let us find out,
     x = int('kaboom')
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    Cell In[25], line 1
    ----> 1 x = int('kaboom')
    
    ValueError: invalid literal for int() with base 10: 'kaboom'
    • We get an error, int tries to convert the string 'kaboom' to an integer, which it clearly cant
    • The art of programming to handle invalid input like the above is called error-handling, we’ll see that later

Whole Numbers and Real Numbers

  • There are two types of numbers
    • Whole numbers or integers
      • Are always held accurately by the computer
    • Real numbers (or representations of) or floating point
      • Contain a fractional component
      • Must be truncated to be stored in memory, so cannot always be stored accurately
Code Analysis: Whole Numbers vs Real Numbers

Learn about the differences between whole numbers and real numbers through the following questions

  1. I’m building a device that can count the number of hairs on your head. Should this be stored as a whole or real number?

    • Integer, we generally wouldn’t count fractions of a hair
    • Alternatively, given how many hairs there are, and that we might not care about being precise, we might instead want to use a real number to store the approximate magnitude
  2. I want to use my hair-counting machine on \(100\) people and determine the average number of hairs on all their heads. Should I use this value as a whole or real number?

    • We expect that the average will not be a whole number, so we should store it as a real number
    • Alternatively, we may not care about getting the number exact down to the fraction, so we could use a whole number to round to the nearest number
  3. I want to keep track of the price of a product in my program. Should I use whole numbers or real numbers?

    • Naively we would a real number, however as we mentioned real numbers have some uncertainty stored in them
    • When dealing with financial values we need to maintain that precision
      • Instead we might then use whole cents
        • Works straightforward if we only care about the total
        • If we care about averages, or fractions of a total then we might have to reconsider
      • There are techniques used to control the error in a real number calculation
  • As you can see the argument of what numerical type to use, requires understanding both the nature of the value itself, and what you want to do with it. You can then consider the properties of the numeric representation and choose the most appropriate one
Important

The way you store a variable depends on what you want to do with it

As the discussion above highlights, sometimes the answer to how we want to store data is not the immediate first answer. It’s important to consider not just what the natural representation of a value is, but what it’s purpose in the code is.

For example as we discussed above, money is naturally expressed as real numbers. However due to the it’s use case, which requires high precision and also generally a limited range (values of even a trillion are rare, and we typically only consider down to whole cents) compared to the full range of a floating point number we might want to look at alternative representations

Real Numbers and Floating Point

  • Real numbers have a fractional part
    • Their representation may not align 1:1 with what was originally input
  • The most common way to store real numbers is called floating point
    • The floating means that the decimal point moves around in the representation, as opposed to a fixed point representation which has a set number of digits after the decimal point
    • Using more memory to store a float lets us store it with greater precision, but we can never accurately represent all floating point numbers
  • Real numbers can be defined by range and precision
    • Precision governs how precisely a number is stored e.g. a float may be able to store \(123456789.0\) or \(0.123456789\) but not \(123456789.987654321\) because the precision required is too great
    • Range determines how much we can slide the decimal point to represent large or small numbers e.g. we could store \(123456700\) or \(0.0001234567\)
    • In Python floats have \(15-16\) digits of precision and can range from \(10^{308}\) and \(10^{-324}\)
  • A quirk of floating point is that some apparently simple numbers like \(0.1\) can’t be stored exactly
Code Analysis: Floating Point Variables and Errors

Conduct the following experiments in the python shell to learn about floating points

  1. What happens if we try to store a value that can’t be held accurately as a floating-point value?

    0.1
    0.1
    • Above we said that \(0.1\) couldn’t be exactly represented, but that doesn’t match with what we just saw. The answer is because the error in the representation is very small, and print rounds off the answer
  2. Does the rounding really happen? Run the following and comment on the result

     0.1 + 0.2
    0.30000000000000004
    • We expected the answer to be \(0.3\) but instead we see a slight error. This is because there is an error accumulation from adding \(0.1\) to \(0.2\) and the underlying floating point representations
  • These issues are not python specific. They are an innate challenge of trying to represent real numbers on hardware. Modern floating-point numbers are also an internationally recognised standard which lets different programs and hardware talk to each other.
  • Python differs from some languages in only providing a single floating point type that is \(8\) bytes. In many languages this is referred to as a double-precision floating point or just a double and is one of several floating point types
Warning

Don’t confuse precision with accuracy

Numbers don’t become more accurate when they are stored with more precision. Scientists often measure values with a measure of uncertainty which captures how sure they are in the accuracy of their number. There is no point storing a value to \(15\) decimal places of precision, if the accuracy of a measurement is only to \(1-2\) places, e.g. if we measure with a ruler

Using higher precision can result in slower programs that use more memory

  • Python automatically creates variables for use in programs
  • The type of a variable is determined by what is stored in it
name = 'Rob'
age = 25
  • The above creates two variables
    • One name is a string type
    • The other age is an int or integer type
Code Analysis: Working with Floating Point Variables

Work through the following following questions to understand how floating-point variables work?

  1. How do you create a floating point variable?

    • You can create a floating point variable by assigning a floating point number e.g. the below creates a variable* x *and assigns it the floating point value \(1.5\)
     x = 1.5
     x
    1.5
  2. What happens if you assign an integer to a floating point variable?

    • The below assigns the integer value \(1.0\) to a variable y
    • If we print the value back we can see it keeps the decimal, indicating it is a floating point value
     y = 1.0
     y
    1.0
    • i.e. by writing the decimal point we coerce the type of y to be floating point
  3. What happens when we mix floating point and integers in calculations?

    • If we compare the two additions below, we can see that when we add floating points, the addition stays a floating point even if the result is integral
     2 + 2
    4
     2.0 + 2.0
    4.0
    • If we mix a floating point and integer type we can also see that the result is returned as a floating point (even when the final result is integral)
     2 + 2.0
    4.0
    • Lastly what happens if we divide two coprime integers?
    • We see that the result gives the appropriate floating point fraction
     1/2
    0.5
Warning

Integer Division

While the last point about division behaviour may seem obvious if you haven’t programmed before you would do well to be careful. In many common languages, division of two integers is treated as integer division. In this case the result is always an integer with the result being rounded according to some scheme. Three common schemes are towards zero, in which the result is always rounded towards the number \(0\); towards the nearest, where the result is rounded to the nearest whole number and floored division in which the number is rounded towards negative infinity.

Division Example Interpretation
towards zero \(-1/2 = 0\) Discard decimal digits
towards nearest \(3.2/2 = 2\) Minimise the difference between “true” division and result
floored division \(-1/2 = -1\) Round “down”

Python lets you perform integer division using floored division behaviour with the // operator e.g.

-1//2
-1

Converting string into float values

  • Converting string to float, works identically to as for int but we use float instead
    • e.g. We could rewrite the configurable egg timer with floats as,
time_text = input('Enter the cooking time in seconds: ')
time_float = float(time_text)
Example: Ultra-Precise Egg Timer
  • The complete program would then look like
# Example 4.4 Ultra-Precise Egg Timer
#
# A version of the Configurable Egg Timer using floating point for the input time

import time

time_text = input("Enter the cooking time in seconds: ")
time_float = float(time_text)

print("Put the egg in boiling water now")
input("Press enter to continue once the egg is in...")

time.sleep(time_float)

print("Take the egg out now")
  • The code is the same except we make the substitution int \(\to\) float

Perform Calculations

  • As mentioned before python is an expression evaluator
  • An expression consists of operators and operands
  • Python evaluates an expression left to right, and carries out operations according to their order of operations
    • Analogous to mathematical order of operations (and includes them) but must extend for programming specific syntax
    • As in maths, parentheses are used to enforce an evaluation order
Operator Use case
- Unary minus, denotes a negative number
* Multiplication (in-lieu of \(\times\))
/ Division
+ Addition
- binary minus or subtraction
  • Basic operators and their precedence
Code Analysis: Work out the Results

See if you can work out the values of a, b and c when the following statements are evaluated,

    a = 1
    b = 2
    c = a + b

    c = c * (a + b)
    b = a + b + c

The first three lines give a = \(1\), b = \(2\) and c = \(3\). Substituting those into the second evaluation for c,

a = 1
b = 2
c = a + b
c = 3 * (1 + 2)
c
9

So c is assigned \(9\). We then repeat for the second assignment to b to get,

b = 1 + 2 + 9

So the final values are, a = \(1\), b = \(12\), c = \(9\). If we execute the original cell we can confirm this

a = 1
b = 2
c = a + b

c = c * (a + b)
b = a + b + c

print('a: ', a)
print('b: ', b)
print('c: ', c)
a:  1
b:  12
c:  9
Caution

Dumb Calculations

Python won’t try and stop you if you do something that mathematically makes no sense like dividing a number by zero, instead an error is raised, but this won’t occur until your program runs! Therefore when using division you should always take care to make sure you either handling division by zero cases appropriately or preventing them from occurring

1/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[38], line 1
----> 1 1/0

ZeroDivisionError: division by zero

When using other languages, they may not raise and error, instead unexpected behaviour may occur including your program crashing.

Convert Between float and int

  • We saw floatcan be used to convert a string to a float.
    • It can also be used to convert an integer value to a floating point value, e.g.
z = float(1)
z
1.0
  • If we want to go the other way we can use int to convert a float to an int.
    • The number is rounded towards zero, i.e. fractional components are truncated (see the callout-box above about integer division)
i = int(2.9)
i
2
Make Something Happen: Calculating a Pizza Order

Consider the following program which is designed to calculate the number of pizzas needed for a hackathon with \(x\) number of participants using the heuristic that \(1\) pizza can satisfy \(1.5\) people

# Example 4.5: Pizza Order Calculator
#
# A basic pizza order calculator based on the heuristic that 1 pizza = 1.5 people fed

students_int = int(
    input("How many students are attending? ")
)  # read in string, convert to int and store
pizza_count = students_int / 1.5  # perform division int -> float
print("You will need", pizza_count, "pizzas")

Note, that I’ve modified the code to directly pass the input to int and then to a variable, to demonstrate that it’s possible.

The above program has the problem that for any number not divisible by \(1.5\), the program recommends ordering a fractional number of pizzas. This is generally not possible, so we need to convert the output to an integer.

  1. Modify the program to return an int by calling int directly during the calculation of pizza_count. What potential problems does this solution have?

     # Exercise 4.1.1: Pizza Order Calculator
     #
     # A basic pizza order calculator based on the heuristic that 1 pizza = 1.5 people fed
    
     students_int = int(
         input("How many students are attending? ")
     )  # read in string, convert to int and store
     pizza_count = students_int / 1.5  # perform division int -> float
     print("You will need", pizza_count, "pizzas")
    • This method has the disadvantage that it will tend to underestimate the number of pizzas needed.
    • For example if we needed to feed \(40\) people, the program would give us \(26\), which can satisfy, \(39\) people, meaning someone goes hungry
  2. Modify the program to return an int by calling int to convert the division to an int then adding \(1\). Explain how this changes the behaviour

     # Exercise 4.1.2: Pizza Order Calculator
     #
     # A basic pizza order calculator based on the heuristic that 1 pizza = 1.5 people fed
     # Converts the result to an integer using int on pizza_count then adding one
     # has the disadvantage it will tend to overestimate the number of pizzas needed
    
     students_int = int(
         input("How many students are attending? ")
     )  # read in string, convert to int and store
     pizza_count = int(students_int / 1.5) + 1  # perform division int -> float
     print("You will need", pizza_count, "pizzas")
    • This method will tend to add an additional spare pizza or overestimate the number of pizzas needed.
    • Since it’s generally better for there to be a little left over than someone go hungry, this behaviour is probably preferred.
Important

Never assume you know what a program is supposed to do

When faced with a choice like the above for the pizza order calculator for a product being made by a client, you should not decide what the expected behaviour is yourself. For example, in the above the customer may prefer to round down the number of pizzas to reduce cost.

Never assume you know what the program should do, ask the client

Make Something Happen: Converting Between Fahrenheit and Centigrade

Write a program to convert from Fahrenheit to Centigrade. The formula for this is

\[ \begin{align} \text{Centigrade} &= \frac{\text{Fahrenheit} - 32}{1.8} \end{align} \]

Our solution is very similar to the Pizza Calculator, but with some of the text changed. The other major difference is that we want to be able to accept a float value, and output a float value.

    # Exercise 4.2: Fahrenheit to Celsius
    #
    # Converts Fahrenheit to Celsius

    temperature_fahrenheit = float(
        input("Enter a temperature in Fahrenheit: ")
    )  # read in string, convert to float
    temperature_centigrade = (temperature_fahrenheit - 32) / 1.8
    print("The temperature is", temperature_centigrade, "degrees Celsius")

A sample output may look like,

Enter a temperature in Fahrenheit: 0

The temperature is -17.77777777777778 degrees Celsius
  • Later we’ll see how we can format the output to be a bit more presentable

Weather Snaps

  • Snaps contains a function get_weather_temp for to return the temperature of a location in the United States
    • Data is scraped from the US National Weather Service website
    • Function takes the latitude and longitude as an argument

Example: Seattle Temperature

  • The following program can be used to get the current temperature reading from Seattle using snaps

      # Example 4.6: Seattle Temperature
      #
      # Get the current temperature in Seattle
    
      import snaps
    
      temp = snaps.get_weather_temp(latitude=47.61, longitude=-122.33)
    
      print("The temperature in Seattle is:", temp)
    pygame 2.6.1 (SDL 2.28.4, Python 3.12.3)
    Hello from the pygame community. https://www.pygame.org/contribute.html
    The temperature in Seattle is: 37

Example: Seattle Weather

  • We can also use snaps to get a short description of the current weather

      # Example 4.7: Seattle Weather
      #
      # Uses snaps to get a description of the weather in Seattle
    
      import snaps
    
      desc = snaps.get_weather_description(latitude=47.61, longitude=-122.33)
      print("The conditions are:", desc)
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    Cell In[43], line 7
          1 # Example 4.7: Seattle Weather
          2 #
          3 # Uses snaps to get a description of the weather in Seattle
          5 import snaps
    ----> 7 desc = snaps.get_weather_description(latitude=47.61, longitude=-122.33)
          8 print("The conditions are:", desc)
    
    AttributeError: module 'snaps' has no attribute 'get_weather_description'

Make Something Happen: Weather Display Program

Write a program that displays the current weather conditions. If you use the display_text function from snaps, your program can display the current weather and description

Our solution is written below,

# Exercise 4.3: Weather Display
#
# Displays the Weather in Seattle

import snaps

temperature_fahrenheit = snaps.get_weather_temp(latitude=47.61, longitude=-122.33)
temperature_string = "The temperature in Seattle is: " + str(temperature_fahrenheit)

weather_descr = snaps.get_weather_description(latitude=47.61, longitude=-122.33)
weather_descr_string = "The conditions are: " + str(weather_descr)

weather_string = temperature_string + "\n" + weather_descr_string

snaps.display_message(weather_string, size=100)

input("Press enter to continue...")

As we can see most the actual getting of the temperature and weather data is the same as for the Seattle Temperature and Weather programs. The rest of the work is done to construct the appropriate strings and merge them together so they can be passed to the display_message function for output

Summary

  • Variables can be created
    • Variables are effectively named regions of memory
    • Variables must start with a letter or underscore and can only consist of alphanumeric characters or underscores
  • Data can be viewed as two fundamental types, text or numeric
    • string is a type for holding text
      • input can be used to get strings from the user
      • int and float can convert a string to an integer or floating point number respectively
    • Numeric values have two forms
      • int for whole numbers with no fractional part
      • float for real numbers with a fractional part
        • float only approximate real numbers and can be subject to some errors

Questions and Answers

  1. What happens if I “overwrite” a variable of one type with a value of another type?
    • Python replaces the old variable with a new one of the same name but the new type
  2. Does using a long variable name slow the program down?
    • If it does it is insignificant, it’s much more important to use a name that clearly conveys meaning
  3. Can we write all our programs using floating point numbers?
    • You could, but you should use int where appropriate because float representation is inexact and subject to errors
      • These errors mean that it is often hard to compare for exact equality e.g. 1.0 might actually have the representation 1.0000...4
  4. Can I stop my program from crashing if someone types in an invalid input?
    • Yes, this is called error handling. This will be covered later in Chapter 6