What this lesson is about
Every program moves data around. Before you can solve problems, you need to know the basic kinds of data Python gives you and what you can do with each. This lesson covers three of them:
- Numbers — for counting and calculating.
- Strings — for text.
- Booleans — for yes/no, true/false answers that drive decisions.
A value is a single piece of data, like the number 7 or the text "hello".
A type is the category a value belongs to. Python always knows the type of
every value, and the type decides what operations make sense. You cannot divide the
word "cat" by 2, but you can divide the number 10 by 2.
You can ask Python for the type of any value with the built-in type function. A
function is a named piece of behavior you can run; you “call” it by writing its
name followed by parentheses with an input inside.
print(type(7)) # -> <class 'int'>
print(type(3.5)) # -> <class 'float'>
print(type("hello")) # -> <class 'str'>
print(type(True)) # -> <class 'bool'>
print is a function that displays a value on the screen. Throughout this lesson,
a comment starting with # -> shows what a line produces.
Numbers: int vs float
Python has two everyday number types.
- int (short for integer) is a whole number with no fractional part:
-3,0,42. Python integers can be as large as you like; they never overflow. - float (short for floating-point number) is a number with a decimal point:
3.5,-0.001,2.0. Floats are how computers approximate real numbers.
count = 42 # int
price = 3.99 # float
print(type(count)) # -> <class 'int'>
print(type(price)) # -> <class 'float'>
The word count here is a variable: a name that refers to a value. The = sign
is assignment; it stores the value on the right into the name on the left. It is
not “equals” in the math sense (that comes later with ==).
Writing 2.0 instead of 2 matters to the type. 2 is an int; 2.0 is a float,
even though they represent the same quantity.
Arithmetic operators
An operator is a symbol that combines values. The basic arithmetic operators:
print(7 + 3) # -> 10 addition
print(7 - 3) # -> 4 subtraction
print(7 * 3) # -> 21 multiplication
print(7 / 3) # -> 2.3333333333333335 true division, always a float
Note that / always produces a float, even when the result is whole:
print(6 / 2) # -> 3.0 (a float, not 3)
Three more operators are worth knowing well, because they show up constantly in real problems.
Floor division // divides and throws away the fractional part, rounding down
to the nearest whole number.
print(7 // 3) # -> 2 (7/3 is 2.33..., rounded down)
print(8 // 2) # -> 4
print(-7 // 3) # -> -3 (rounds down, toward negative infinity)
Remainder % (called modulo) gives what is left over after floor division.
print(7 % 3) # -> 1 (7 = 2*3 + 1, remainder 1)
print(10 % 2) # -> 0 (10 is even, no remainder)
print(10 % 3) # -> 1
% is the standard way to test whether a number is even or divisible by something:
n % 2 == 0 is true exactly when n is even.
Power ** raises a number to an exponent.
print(2 ** 3) # -> 8 (2 * 2 * 2)
print(5 ** 2) # -> 25
print(9 ** 0.5) # -> 3.0 (a fractional power is a root; this is the square root)
If both operands are ints, + - * // % ** keep the result an int (except /, which
is always float). If either operand is a float, the result is a float.
print(3 + 4) # -> 7 (int)
print(3 + 4.0) # -> 7.0 (float, because one side is float)
Float rounding pitfalls
Floats are approximations. A computer stores them in binary (base 2), and many
ordinary decimal fractions cannot be written exactly in binary, the same way 1/3
cannot be written exactly in decimal. The result is small surprises:
print(0.1 + 0.2) # -> 0.30000000000000004
print(0.1 + 0.2 == 0.3) # -> False
This is not a bug in Python; every language using standard floats behaves this way. Two practical rules:
- Do not compare floats with
==for exact equality. Instead check that they are close enough:
a = 0.1 + 0.2
print(abs(a - 0.3) < 1e-9) # -> True
abs gives the absolute value (distance from zero), and 1e-9 is scientific
notation for 0.000000001, a tiny tolerance.
- To display a rounded number, use the
roundfunction, which takes the value and how many decimal places you want:
print(round(3.14159, 2)) # -> 3.14
print(round(2.5)) # -> 2 (rounds to nearest even; see pitfalls below)
Strings
A string is text: a sequence of characters such as letters, digits, spaces, and punctuation. You create one by wrapping characters in quotes. Single and double quotes both work; pick one and be consistent.
name = "Ada"
greeting = 'hello'
empty = "" # a string with zero characters
Length and indexing
len returns how many characters a string contains.
print(len("hello")) # -> 5
print(len("")) # -> 0
Indexing means reading one character by its position. Positions start at 0,
not 1. So in "hello", h is at index 0 and o is at index 4. You index
with square brackets.
word = "hello"
print(word[0]) # -> h
print(word[4]) # -> o
Negative indexes count from the end: -1 is the last character.
print(word[-1]) # -> o
print(word[-2]) # -> l
Asking for an index that does not exist raises an error (a crash with a message):
# word[10] -> IndexError: string index out of range
Every character in "hello" has two addresses at once — a positive index counting up from the front and a negative one counting back from the end:
graph LR A["'h'\nindex 0\nindex -5"] --- B["'e'\nindex 1\nindex -4"] --- C["'l'\nindex 2\nindex -3"] --- D["'l'\nindex 3\nindex -2"] --- E["'o'\nindex 4\nindex -1"]
Slicing
Slicing pulls out a range of characters. The syntax is s[start:stop], and it
includes start but excludes stop. This “up to but not including” rule is
everywhere in Python; get comfortable with it early.
s = "programming"
print(s[0:4]) # -> prog (indexes 0, 1, 2, 3)
print(s[4:7]) # -> ram
Leaving out a side means “from the beginning” or “to the end”:
print(s[:4]) # -> prog
print(s[4:]) # -> ramming
print(s[:]) # -> programming (a full copy)
f-strings: building text from values
Often you want to insert a value into text. An f-string (the f stands for
formatted) does this. Put f before the opening quote, then write {...} around
any value or variable you want to drop in.
name = "Ada"
age = 36
print(f"{name} is {age} years old") # -> Ada is 36 years old
print(f"next year: {age + 1}") # -> next year: 37
Without f-strings you would have to convert numbers to text and join pieces by hand, which is more error-prone. f-strings are the standard, readable choice.
Common string methods
A method is a function attached to a value, called with a dot: value.method().
Strings come with many useful ones. Here are the ones you will reach for constantly.
text = " Hello World "
print(text.upper()) # -> " HELLO WORLD "
print(text.lower()) # -> " hello world "
print(text.strip()) # -> "Hello World" (removes surrounding spaces)
print(text.replace("l", "L")) # -> " HeLLo WorLd "
.split() breaks a string into a list of pieces. A list is an ordered
collection of values, written with square brackets and commas. By default .split()
cuts on spaces; you can pass a different separator.
"a,b,c".split(",") # -> ['a', 'b', 'c']
"one two three".split() # -> ['one', 'two', 'three']
The in operator tests whether one string appears inside another. It gives back a
boolean (covered next).
print("ell" in "hello") # -> True
print("z" in "hello") # -> False
Strings are immutable
Immutable means “cannot be changed after it is created.” You cannot alter a single character of an existing string in place:
s = "cat"
# s[0] = "b" -> TypeError: 'str' object does not support item assignment
Instead, string operations always build a new string and leave the original alone:
s = "cat"
t = s.replace("c", "b")
print(s) # -> cat (unchanged)
print(t) # -> bat (a new string)
This matters for performance. Because a string cannot be edited in place, building a
long string by repeatedly adding pieces in a loop copies the whole thing each time,
which is O(n^2) total work for n characters. Big-O is a way to describe how the
cost of an operation grows with the size of the input; O(n^2) means the work grows
with the square of the length, which gets slow quickly. The efficient pattern is to
collect the pieces in a list and join them once with "".join(...), which is O(n).
parts = ["a", "b", "c"]
print("".join(parts)) # -> abc
Booleans
A boolean is a value that is either True or False. Booleans are the answers
to yes/no questions, and they drive every decision a program makes. There are exactly
two boolean values, and both are capitalized.
print(True) # -> True
print(type(False)) # -> <class 'bool'>
Comparison operators
Comparisons ask a question about two values and give back a boolean.
print(3 == 3) # -> True equal to
print(3 != 4) # -> True not equal to
print(3 < 4) # -> True less than
print(5 > 4) # -> True greater than
print(3 <= 3) # -> True less than or equal to
print(5 >= 6) # -> False greater than or equal to
Note the difference between = and ==. A single = assigns a value to a
variable. A double == compares two values and answers whether they are equal.
Mixing these up is one of the most common beginner mistakes.
Comparisons work on strings too, alphabetically (technically by character code):
print("apple" < "banana") # -> True
print("Ada" == "ada") # -> False (case matters)
Logical operators: and, or, not
These combine or flip booleans so you can express compound conditions.
andisTrueonly when both sides areTrue.orisTruewhen at least one side isTrue.notflips a boolean to its opposite.
print(True and False) # -> False
print(True or False) # -> True
print(not True) # -> False
age = 25
print(age >= 18 and age < 65) # -> True
Truthiness
Python lets you use non-boolean values where a true/false answer is expected. Each
value is treated as either truthy (acts like True) or falsy (acts like
False). The falsy values you will meet most are:
- the number
0(and0.0) - the empty string
"" - the empty list
[] - the special value
None(which means “no value”)
Almost everything else is truthy. You can check by passing a value to bool:
print(bool(0)) # -> False
print(bool(42)) # -> True
print(bool("")) # -> False
print(bool("hi")) # -> True
print(bool([])) # -> False
print(bool([1])) # -> True
This is why code often reads if items: instead of if len(items) > 0:. An empty
list is falsy, so the shorter form means the same thing and is idiomatic Python.
Common pitfalls
=vs==.x = 5stores 5 intox.x == 5asks whetherxequals 5. Using=where you meant==is a frequent bug.- Comparing floats with
==. Because floats are approximate,0.1 + 0.2 == 0.3isFalse. Compare with a tolerance instead. /is always a float.6 / 2is3.0, not3. Use//when you need an int result from division.- Indexes start at 0. The first character is at index
0, and the last valid index of a length-nstring isn - 1. Going past the end raisesIndexError. - Slices exclude the stop.
s[0:4]gives characters0,1,2,3— four of them, not five. - Strings are immutable.
s[0] = "x"is an error. Build a new string instead. rounduses banker’s rounding.round(2.5)is2andround(3.5)is4: ties round to the nearest even number, not always up. This surprises people who expect2.5to become3.
Practice
- Given
n = 47, print whether it is even or odd using%, and print its last digit (also using%). - Take the string
s = " Data Driven ". Produce a lowercase version with the surrounding spaces removed, then print how many characters that cleaned string has. - Write a single boolean expression that is
Truewhen a variablescoreis between 0 and 100 inclusive, andFalseotherwise. Test it with a few values.