Variables and Data Types in Python
How Python variables name values, the core built-in types (int, float, str, bool and None), converting between them, and formatting values with f-strings.
A variable is a name for a value
Programs need to remember things: a score, a user's name, a running total. A variable is a name that refers to a value, so you can use the value later by writing the name.
You create a variable with an assignment statement: the name, an equals sign, and an expression. Python evaluates the right-hand side first and then attaches the name on the left to the result.
The = sign does not mean "is equal to" as it does in mathematics. Read score = score + 10 as "compute score + 10, then make score refer to that new value". Equality is tested with ==, which you will use in conditions.
A name bound to a value. Assignment (`name = value`) creates the binding or moves an existing name to a new value. Python has no separate declaration step.
1score = 02player = "Ada"3print(player, "has", score, "points")45score = score + 106print(player, "now has", score, "points")
Ada has 0 points Ada now has 10 points
Naming variables well
A name may contain letters, digits and underscores, must not start with a digit, and must not be one of Python's reserved keywords such as if, for, class or True. Names are case-sensitive: total, Total and TOTAL are three different variables.
Beyond the rules, Python has strong conventions (from the style guide PEP 8): ordinary variables use snake_case, and values meant to stay fixed are written in UPPER_CASE, such as MAX_ATTEMPTS = 3. Python does not enforce constants; the capital letters are a signal to other programmers.
A good name says what the value means, not what type it is. price_with_tax beats p2, and is_logged_in beats flag.
Valid and invalid names
| Name | Allowed? | Why |
|---|---|---|
| user_age | Yes | Letters and an underscore, snake_case |
| _count | Yes | May start with an underscore |
| total2 | Yes | Digits are fine after the first character |
| 2nd_place | No | Starts with a digit |
| user-age | No | A hyphen is the minus operator |
| class | No | Reserved keyword |
- ✓Use descriptive snake_case names: monthly_total, first_name.
- ✓Use UPPER_CASE for values that should never change: SECONDS_PER_DAY.
- ✓Prefix booleans with is_, has_ or can_: is_valid.
- ✕Use single letters except for short loop counters or maths (i, x, y).
- ✕Shadow built-in names such as list, str, input or sum.
- ✕Encode the type in the name: str_name, int_age.
The core built-in types
Every value in Python has a type, which decides what you can do with it. Five types cover most beginner programs:
int: whole numbers of any size, positive or negative:42,-7,10_000_000(underscores are allowed as digit separators).float: numbers with a fractional part, stored in binary floating point:3.14,-0.5,2.0,1e6.str: text, a sequence of characters in quotes:"hello",'Python',""(the empty string).bool: exactly two values,TrueandFalse, produced by comparisons and used in decisions.NoneType: the single valueNone, meaning "no value here".
The built-in function type() tells you the type of any value. Later lessons add the collection types: list, tuple, dict and set.
`42` and `"42"` look alike when printed but are different types: one is a number you can add to, the other is two characters of text.
1print(type(42))2print(type(3.14))3print(type("42"))4print(type(True))5print(type(None))6print(type(10_000_000), 10_000_000)
<class 'int'> <class 'float'> <class 'str'> <class 'bool'> <class 'NoneType'> <class 'int'> 10000000
Values have types, names do not
Python is dynamically typed: a variable has no fixed type. The type belongs to the value the name currently refers to, so the same name can refer to an int now and a str later.
That flexibility is convenient, but reusing one name for different kinds of data makes code hard to follow. A useful habit is to keep each variable to one meaning and one type for its whole life.
Python is also strongly typed: it will not silently mix incompatible types. Adding a number to a string raises a TypeError instead of guessing what you meant.
1value = 102print(value, type(value))3value = "ten"4print(value, type(value))5value = 10.06print(value, type(value))
10 <class 'int'> ten <class 'str'> 10.0 <class 'float'>
Two names, one value
Assignment never copies a value; it makes a name refer to an existing object. After b = a, both names refer to the same object.
For the types in this lesson that is never a problem, because numbers, strings, booleans and None are immutable: they cannot be changed in place. "Changing" a really means pointing a at a different object, and b keeps the old one. (Lists and dictionaries are mutable, and there two names for one object behave differently. The lists lesson returns to this.)
Step through the trace below and watch each name. The last line swaps the two values with tuple assignment: the right side is evaluated completely before anything is assigned, so no temporary variable is needed.
Binding names and swapping values
a = 5b = aa = a + 1a, b = b, a| Step | a | b | State Evolution |
|---|---|---|---|
| 1 | 5 | — | a is bound to 5. |
Converting between types
Data often arrives in the wrong type: input() and files give you strings, and a calculation may give you a float where you need an integer. The type names double as conversion functions:
int(x)converts a string of digits (spaces and a sign are allowed) or a number. For floats it truncates toward zero:int(3.9)is 3 andint(-3.9)is -3.float(x)converts a numeric string or an integer.str(x)gives the text form of any value.bool(x)givesFalsefor zero, empty text,Noneand empty collections, andTruefor everything else.
A conversion that cannot make sense raises a ValueError: int("3.5") and int("forty") both fail. To get the nearest integer instead of truncating, use round(). Note that round rounds exact halves to the nearest even integer, so round(2.5) is 2 and round(3.5) is 4.
`bool("False")` is True: any non-empty string is truthy. To read a yes/no answer, compare the text instead: `answer.strip().lower() == "yes"`.
1print(int("42") + 1)2print(int(" -7 "))3print(float("2.5") * 2)4print(int(3.9), int(-3.9))5print(round(3.9), round(2.5), round(3.5))6print(str(99) + " bottles")7print(bool(0), bool(""), bool("0"), bool(None), bool(3))
43 -7 5.0 3 -3 4 2 4 99 bottles False False True False True
Formatting values with f-strings
An f-string is a string literal with an f before the opening quote. Expressions inside curly braces are evaluated and inserted into the text. It is the clearest way to build output from variables.
After the expression you can add a colon and a format specifier:
:.2f: fixed-point with two decimal places:,: thousands separators:>8/:<8/:^8: right-, left- or centre-align in 8 characters:05d: pad an integer with zeros to width 5:%: show a fraction as a percentage
Writing {name=} (with an equals sign) prints both the expression and its value, which is handy for debugging.
1item = "Notebook"2price = 3.53quantity = 44total = price * quantity5print(f"{quantity} x {item} = {total:.2f}")6print(f"Downloaded: {1048576:,} bytes")7print(f"[{item:>10}] [{item:<10}] [{item:^10}]")8print(f"Order {7:05d}, discount {0.125:.1%}")9print(f"{total=}")
4 x Notebook = 14.00 Downloaded: 1,048,576 bytes [ Notebook] [Notebook ] [ Notebook ] Order 00007, discount 12.5% total=14.0
Multiple assignment and constants
Python lets you assign several names in one statement. x, y = 3, 4 pairs names and values by position; the counts must match or Python raises a ValueError. a = b = 0 binds both names to the same value.
For values that the program treats as fixed, write them once, near the top, as an UPPER_CASE constant. When a rule changes, for example a tax rate, you edit one line instead of hunting for every 0.16 in the code.
1TAX_RATE = 0.162width, height = 1920, 10803hits = misses = 04print(width * height, "pixels")5print(hits, misses)6subtotal = 2507print(f"Tax: {subtotal * TAX_RATE:.2f}")
2073600 pixels 0 0 Tax: 40.00
None: the value that means "nothing yet"
None represents the absence of a value: a search that found nothing, an optional setting that was not given, a result that is not known yet. Functions that do not return anything explicitly return None.
Test for it with is None (or is not None), not with ==. Do not confuse it with 0, False or "": those are real values that happen to be falsy, while None means there is no value at all.
1best_score = None2print(best_score is None)3best_score = 04print(best_score is None, best_score == 0)
True False True
Check your understanding
What does `print(type("7"))` print?
Practice
Two short problems: decide which type a piece of typed text describes, and convert temperatures with formatted output.
Data Types Explorer
Reveal Solution & AnalysisHide Solution
Temperature Converter
Reveal Solution & AnalysisHide Solution
Quick Syntax & Reference Sheet
| Concept / Rule | Syntax / Usage |
|---|---|
| Assign / reassign | count = 0; count = count + 1 |
| Check a type | type(value) # or isinstance(value, int) |
| Convert text to numbers | int("42"), float("2.5") |
| Convert to text | str(99) |
| Format a float | f"{price:.2f}" |
| Thousands separator | f"{n:,}" |
| Swap two variables | a, b = b, a |
| Constant (by convention) | MAX_USERS = 100 |
Common Pitfalls & Developer Mistakes
Adding a number to a string: "Age: " + 30
Why it happens: Python does not convert types implicitly and raises TypeError.
Expecting int() to round
Why it happens: int(2.99) truncates to 2.
Naming a variable list, str or input
Why it happens: The name now hides the built-in function, and a later call such as list(...) fails with a confusing TypeError.
Using bool("False") to parse text
Why it happens: Every non-empty string is truthy.