Python basics · Lesson 2 of 10 · about 25 minutes

Values and variables

The four kinds of value every script is built from, how to give them names, how to combine and compare them, and why text that looks like a number isn't one.

By the end you'll know

  • The four basic types of value: text, whole numbers, decimals and true or false
  • How to store a value under a name, and change it later
  • How to do arithmetic, compare values, and combine the answers
  • How to build a line of output with an f-string
  • How to convert between types, and why a setting that looks like a number arrives as text

Where your checker is

At the end of Lesson 1, checker.py printed two lines you typed by hand. It doesn't know anything about a site: not its name, not what it answered, not how long it took. It can't work anything out either, like whether the site is up.

This lesson teaches it to remember things and to work things out. You'll build it up a few lines at a time, so keep checker.py open in your editor, and keep a terminal running python3 beside it for quick experiments at the >>> prompt.

Four types of value

The checker needs to hold three facts about a site: its name, its status code, and how long it took to answer. Those are different kinds of data, and Python cares about the difference, the same way a form that asks for your age expects 34, not "thirty-four". Every value has a type, which decides what you can do with it: do maths with it, compare it, or only show it. Ask Python for a few types with type():

PYTHON
>>> type("example.com")
<class 'str'>
>>> type(200)
<class 'int'>
>>> type(0.25)
<class 'float'>
>>> type(True)
<class 'bool'>

Python prints each type as <class '...'>. For now, read "class" as "type": <class 'str'> just means "this is a str". Classes are how you make types of your own, which comes much later.

  • str (string): text, written inside quotes. "example.com" and 'example.com' are the same.
  • int (integer): a whole number, like 200 or -3. No quotes.
  • float (floating point number): a number with a decimal point, like 0.25.
  • bool (Boolean): one of exactly two values, True or False, with a capital letter.

The quotes matter more than anything else here. 200 is a number. "200" is text that happens to contain the characters 2, 0 and 0. They look alike on screen, but Python treats them as completely different things, and the last part of this lesson shows where that bites.

There's one more value you'll meet often: None, which means "no value". You'll see it properly in Lesson 4.

Variables: names for values

To keep a value, give it a name. Put a name, an equals sign, and a value, and Python stores the value under that name:

PYTHON
>>> status_code = 200
>>> status_code
200

The = here doesn't mean "equals" as in maths. It means "store the value on the right under the name on the left". You can store a new value under the same name at any time, and the old one is replaced:

PYTHON
>>> status_code = 404
>>> status_code
404

Once a value has a name, every line that uses the name gets the value, so changing it in one place changes it everywhere.

Think of a name tag on a parcel. The parcel is the value: the number 200, or the text "example.com". The variable is the name tag you stick on it, so you can say "pass me status_code" instead of describing the parcel. Reassigning moves the tag to a different parcel. The name tag doesn't change what's inside; it just tells you which parcel you mean.

Now give the checker its three facts. In checker.py, add these lines under the comment, above the two print lines:

PYTHON
site = "example.com"
status_code = 200
response_seconds = 0.25

For now you type these in. In Lesson 6, the checker gets them from a real website. Notice the names:

  • They use letters, numbers and underscores, and don't start with a number. status_code works; 2nd_site doesn't.
  • They follow Python's convention, snake_case: lowercase words joined by underscores.
  • They're case sensitive. status_code and Status_code are two different names. (The break-it section shows what happens when you mix them up.)
  • They say what the value is. response_seconds tells the next reader far more than r or x.

Working things out

The checker has its facts. Now it needs to work two things out: how long the site took in milliseconds, and whether the site is up.

Arithmetic works on ints and floats:

PYTHON
>>> 7 / 2
3.5
>>> 7 // 2
3
>>> 7 % 2
1
>>> 2 ** 10
1024

+, - and * do what you'd expect. / always gives a float, even when the answer is whole. // divides and drops the part after the decimal point, % gives the remainder, and ** raises to a power.

Strings have their own versions of + and *. Adding joins them, and multiplying repeats them:

PYTHON
>>> "web" + "site"
'website'
>>> "-" * 10
'----------'

Comparisons ask a question about two values, and the answer is always a bool. Your banking app does this every time you pay: is the balance at least the amount? Yes or no. In Python:

PYTHON
>>> 200 == 200
True
>>> 404 != 200
True
>>> 150 < 1000
True

Note the double equals: == asks "are these the same?", while a single = stores a value. != means "not equal", and <, >, <= and >= mean less than, greater than, and their "or equal to" versions.

Combining answers uses three words: and (both must be true), or (at least one must be true), and not (flips true to false and back):

PYTHON
>>> 200 >= 200 and 200 < 400
True
>>> not True
False

That first line is exactly the question the checker needs to answer: is the status code 200 or more, and less than 400? Web servers answer with codes in that range when things went well (200 means OK, and most of the 300s mean "go to this other address instead"), and with codes of 400 and above when something went wrong.

Add these two lines to checker.py, below the three facts:

PYTHON
is_up = status_code >= 200 and status_code < 400
response_ms = round(response_seconds * 1000)

is_up stores the answer to the comparison, a bool. response_ms turns seconds into milliseconds, and round() rounds it to a whole number. Both are worked out from the facts above them, so they're always right for whatever facts you give.

f-strings: reporting what it found

Last, the checker needs to report what it found. To show values inside text, put an f straight before the opening quote, and the variable names inside curly brackets:

PYTHON
>>> site = "example.com"
>>> status_code = 200
>>> f"{site} answered {status_code}"
'example.com answered 200'

This is an f-string (formatted string). Python fills in each {name} with that variable's value, the same way a text from your bank fills your name and balance into one template sent to millions of customers. You can also put a calculation inside the brackets, and add a format after a colon. :.2f means "as a decimal with 2 places":

PYTHON
>>> response_seconds = 0.256
>>> f"{response_seconds:.2f} seconds"
'0.26 seconds'

In checker.py, replace the line print("2 sites to check") with these two:

PYTHON
print(f"{site}: {status_code} in {response_ms} ms")
print(f"Up: {is_up}")

Checkpoint: your checker so far

Your checker.py should now look like this:

PYTHON
# A website checker, built one piece at a time.
site = "example.com"
status_code = 200
response_seconds = 0.25

is_up = status_code >= 200 and status_code < 400
response_ms = round(response_seconds * 1000)

print("Checking sites...")
print(f"{site}: {status_code} in {response_ms} ms")
print(f"Up: {is_up}")

The blank lines are optional; they just group the facts, the working out, and the output. Run it:

BASH
python3 checker.py
TEXT
Checking sites...
example.com: 200 in 250 ms
Up: True

Change status_code to 503 (a common "server is overloaded" answer) and run it again. The last line changes to Up: False, and you didn't touch the line that decides it. That's the point of variables: you describe the rule once, and it works for whatever value comes in. Change it back to 200.

Variables, values and typesOn the left, four names each point to a value: site to the text example.com, a str; status_code to 200, an int; response_seconds to 0.25, a float; is_up to True, a bool. On the right, the text "60" goes through int() and comes out as the number 60. Values that look alike can have different types, and converting changes the type.namevaluetypesite"example.com"strstatus_code200intresponse_seconds0.25floatis_upTrueboolConverting a type"60"strint()60inttext in, number outA variable is a name for a value. The value's type decides what you can do with it.
Each name points to one value, and every value has a type. "60" and 60 look alike, but only the int can be used in arithmetic; int() makes the switch.

Converting between types

Right now you type the checker's facts in yourself, so its numbers really are numbers. That won't always be true. Python won't guess what you meant when you mix types. You convert a value yourself, with the type's name:

PYTHON
>>> int("200")
200
>>> str(200)
'200'
>>> float("0.25")
0.25
>>> int(0.9)
0

int("200") reads the text and gives back the number. str(200) does the reverse. Watch the last one: int() on a float chops off the decimal part rather than rounding, so 0.9 becomes 0. Use round() when you want rounding.

If you've ever had a spreadsheet refuse to add up a column because the numbers were stored as text, you've already met this problem. Most data reaches your script as text. Anything read from a file, typed by a person, or set as an environment variable (a named setting a program reads when it starts, like the ones you passed to containers in the Docker track) arrives as a str, even when it looks like a number. Here's what happens if you forget:

PYTHON
>>> limit = "25"
>>> limit * 2
'2525'
>>> int(limit) * 2
50

Pretend "25" came from a file or a setting. It looks like a number, but the quotes show it's text, and multiplying text repeats it. So instead of 50 you get '2525', with no error at all. That's the most dangerous kind of mistake: one that runs and gives a wrong answer quietly.

Break it on purpose

Make three mistakes in checker.py, one at a time, and put each one back before the next.

Text where a number should be. Change the status code line to status_code = "200", with quotes, as if it had been read from a file. Run it:

TEXT
Traceback (most recent call last):
  File "checker.py", line 6, in <module>
    is_up = status_code >= 200 and status_code < 400
            ^^^^^^^^^^^^^^^^^^
TypeError: '>=' not supported between instances of 'str' and 'int'

Read it from the bottom, as in Lesson 1. A TypeError means an operation was used on a type it doesn't work with: Python can't say whether the text "200" is bigger than the number 200. Notice it blew up on line 6, not on line 3 where the real mistake is. The error appears where the value is used, which is why converting at the moment you read a value, like int(limit) above, is the better habit. Take the quotes away.

Text that isn't a number. Change the line to status_code = int("two hundred"):

TEXT
Traceback (most recent call last):
  File "checker.py", line 3, in <module>
    status_code = int("two hundred")
ValueError: invalid literal for int() with base 10: 'two hundred'

This is a different error. A ValueError means the type was right (int() does accept text) but this particular value made no sense. "Invalid literal for int() with base 10" is Python's way of saying "I can't read this as a whole number". Lesson 4 shows how to catch this error instead of crashing. Put 200 back.

A capital letter. On line 6, change the first status_code to Status_code:

TEXT
Traceback (most recent call last):
  File "checker.py", line 6, in <module>
    is_up = Status_code >= 200 and status_code < 400
            ^^^^^^^^^^^
NameError: name 'Status_code' is not defined. Did you mean: 'status_code'?

The same NameError you met with prnt in Lesson 1. To Python, Status_code is a brand new name that was never given a value, because names are case sensitive. Fix the capital, and run it once more to make sure you're back to Up: True.

Where you are now

Your checker holds one site's facts, works out whether the site is up, and reports it. But it only knows about one site: checking a second would mean copying every line and renaming every variable. Lesson 3 fixes that with lists, dictionaries and loops.

Check yourself

  1. What type is "200", written with the quotes?

  2. What does 7 // 2 give?

  3. A setting read from a file gives you limit = "25". What does limit * 2 give?

  4. What type of value does a comparison like status_code >= 200 give back?

  5. You set status = 200, and later write print(Status). Why do you get a NameError?

  6. What does the f do in f"{site}: {status_code}"?

Key terms

  • Value: a piece of data, like "example.com", 200 or True.
  • Type: the kind of value, which decides what you can do with it. Check it with type().
  • str, int, float, bool: text, whole numbers, decimals, and True or False.
  • None: Python's value for "no value".
  • Variable: a name for a value, set with =.
  • snake_case: Python's naming style: lowercase words joined by underscores.
  • f-string: text starting with f that fills in {names} with their values.
  • Environment variable: a named setting a program reads when it starts. Always text.
  • TypeError: an operation used on the wrong type, like comparing text with a number.
  • ValueError: the right type, but a value that makes no sense, like int("two hundred").

Go deeper