Python & AI Tutorials Logo
Python Programming

3. Variables and Basic Data Types

In Chapter 2, you learned how to write simple programs that print text, accept input, and perform basic operations. However, those programs had a significant limitation: they couldn't store information for later use or work with different kinds of data in sophisticated ways. In this chapter, you'll learn how Python stores and manages information using variables and data types.

Think of variables as labeled containers that hold information. Just as you might use labeled boxes to organize items in your home, Python uses variables to organize and manage data in your programs. But not all data is the same—a number is fundamentally different from text, and Python needs to know what kind of data it's working with. That's where data types come in.

By the end of this chapter, you'll understand:

  • How to create and name variables properly
  • What data types are and why they matter
  • How to work with numbers, text, and logical values
  • How to convert between different types of data
  • How Python represents data internally

These concepts form the foundation for everything else you'll do in Python, so we'll explore them thoroughly and with plenty of practical examples.

3.1) Naming and Creating Variables

3.1.1) What Variables Are and Why They Matter

A variable is a name that refers to a value stored in your computer's memory. When you create a variable, you're essentially creating a label that points to some piece of data. This allows you to:

  1. Store information for later use in your program
  2. Reference the same value multiple times without retyping it
  3. Update values as your program runs
  4. Make your code readable by using meaningful names instead of raw values

Let's see a concrete example. Without variables, if you wanted to calculate the area of a rectangle, you might write:

python
# without_variables.py
print("Rectangle area:", 15 * 8)

This works, but what if you need to use these dimensions multiple times? And what do 15 and 8 represent? With variables, the code becomes clearer and more flexible:

python
# with_variables.py
length = 15
width = 8
area = length * width
print("Rectangle area:", area)  # Output: Rectangle area: 120

Now the code is self-documenting—anyone reading it can immediately understand that we're calculating the area of a rectangle with specific dimensions. If we need to change the dimensions, we only update the variable assignments at the top.

3.1.2) Creating Variables with Assignment

In Python, you create a variable using the assignment operator (=). The basic syntax is:

python
variable_name = value

The = sign doesn't mean "equals" in the mathematical sense. Instead, it means "assign the value on the right to the name on the left." This is a crucial distinction. When Python sees this statement, it:

  1. Evaluates the expression on the right side of the =
  2. Creates or updates the variable name on the left side
  3. Makes that name refer to the resulting value

Here are several examples of creating variables:

python
# creating_variables.py
age = 25
temperature = 72.5
name = "Alice"
is_student = True
 
print(age)          # Output: 25
print(temperature)  # Output: 72.5
print(name)         # Output: Alice
print(is_student)   # Output: True

Notice that we don't need to declare what type of data each variable will hold—Python figures this out automatically based on the value you assign. This is called dynamic typing, and it makes Python very flexible and easy to use.

You can also create multiple variables in a single line using multiple assignment:

python
# multiple_assignment.py
x, y, z = 10, 20, 30
print(x)  # Output: 10
print(y)  # Output: 20
print(z)  # Output: 30
 
# You can even assign the same value to multiple variables
a = b = c = 100
print(a, b, c)  # Output: 100 100 100

The first form (called tuple unpacking—we'll learn about tuples in Chapter 14) assigns each value to its corresponding variable in order. The second form assigns the same value to all three variables.

3.1.3) Variable Naming Rules and Conventions

Python has specific rules about what makes a valid variable name. Some of these are requirements (rules you must follow), while others are conventions (guidelines that make your code more readable and consistent with other Python code).

Requirements (Must Follow):

  1. Must start with a letter (a-z, A-Z) or underscore (_): Variable names cannot start with a digit.
  2. Can contain letters, digits, and underscores: After the first character, you can use letters, numbers, and underscores in any combination.
  3. Cannot contain spaces or special characters: Spaces, hyphens, and most punctuation marks are not allowed.
  4. Cannot be a Python keyword: Python reserves certain words for its own use (like if, for, while, def, etc.).
  5. Case-sensitive: age, Age, and AGE are three different variables.

Here are examples of valid and invalid variable names:

python
# valid_names.py
# Valid variable names
user_age = 30
firstName = "John"
total_2024 = 1000
_private = "hidden"
x = 5
MAX_SIZE = 100
 
# Invalid variable names (these will cause errors)
# 2nd_place = "silver"      # Error: starts with digit
# user-name = "alice"       # Error: contains hyphen
# total amount = 500        # Error: contains space
# class = "Python 101"      # Error: 'class' is a keyword

Conventions (Should Follow for Readability):

  1. Use lowercase with underscores for regular variables: This style is called snake_case and is the standard in Python.

    python
    user_age = 25
    total_price = 99.99
    is_valid = True
  2. Use UPPERCASE for constants: Values that shouldn't change during program execution.

    python
    MAX_ATTEMPTS = 3
    PI = 3.14159
    DEFAULT_COLOR = "blue"
  3. Use descriptive names: Variable names should clearly indicate what they represent.

    python
    # Good: clear and descriptive
    student_count = 30
    average_temperature = 72.5
     
    # Poor: unclear abbreviations
    sc = 30
    avg_tmp = 72.5
     
    # Poor: too generic
    x = 30
    data = 72.5
  4. Avoid single-letter names except for specific cases: Single letters like i, j, k are acceptable for loop counters (which we'll learn about in Chapter 11), and x, y, z are fine for coordinates. Otherwise, use descriptive names.

  5. Don't use names that shadow built-in functions: While Python allows it, avoid using names like list, str, int, print, etc., as these are built-in function names.

Let's see a practical example that demonstrates good naming practices:

python
# good_naming.py
# Constants at the top
SALES_TAX_RATE = 0.08
DISCOUNT_THRESHOLD = 100
 
# Descriptive variable names
item_price = 75.00
quantity = 3
subtotal = item_price * quantity
 
# Clear boolean variable
qualifies_for_discount = subtotal >= DISCOUNT_THRESHOLD
 
if qualifies_for_discount:
    discount = subtotal * 0.10
    subtotal = subtotal - discount
    print("Discount applied: $", discount)
 
tax = subtotal * SALES_TAX_RATE
total = subtotal + tax
 
print("Subtotal: $", subtotal)
print("Tax: $", tax)
print("Total: $", total)
# Output:
# Subtotal: $ 202.5
# Tax: $ 16.2
# Total: $ 218.7

Notice how the variable names make the code self-documenting. Even without comments, you can understand what the program is calculating.

3.1.4) Using Variables in Expressions

Once you've created a variable, you can use it anywhere you would use the value it represents. Python will automatically substitute the variable's current value when evaluating expressions:

python
# using_variables.py
# Create some variables
hours_worked = 40
hourly_rate = 25.50
 
# Use variables in calculations
gross_pay = hours_worked * hourly_rate
print("Gross pay:", gross_pay)  # Output: Gross pay: 1020.0
 
# Use variables in other expressions
bonus = gross_pay * 0.10
total_pay = gross_pay + bonus
print("Total with bonus:", total_pay)  # Output: Total with bonus: 1122.0
 
# Use variables in strings (we'll learn more about this in Chapter 5)
message = "You worked " + str(hours_worked) + " hours"
print(message)  # Output: You worked 40 hours

Variables can also be used with the functions you learned in Chapter 2:

python
# variables_with_functions.py
name = input("What is your name? ")
age = input("What is your age? ")
 
greeting = "Hello, " + name + "!"
print(greeting)
 
age_next_year = int(age) + 1
print("Next year you will be", age_next_year, "years old.")
# If user enters "Alice" and "25":
# Output: Hello, Alice!
# Output: Next year you will be 26 years old.

This example also demonstrates an important concept: the input() function always returns text (a string), so if you want to do math with it, you need to convert it to a number first. We'll explore this conversion process in detail later in this chapter (Section 3.7).

3.2) Assignment and Reassignment of Variables

3.2.1) Understanding Assignment

When you assign a value to a variable, Python creates a connection between the variable name and the value in memory. It's important to understand that the variable doesn't "contain" the value in a physical sense—rather, it's a name that refers to or points to a value stored elsewhere in memory.

Think of it like a sticky note with a name written on it, attached to a box containing the actual value. The variable (sticky note) isn't the value itself—it just points to where the value is stored. When you reassign the variable, you're moving the sticky note to point to a different box, not changing what's in the original box.

This distinction becomes important when we talk about reassignment. Let's explore what happens when you assign and reassign variables:

python
# assignment_basics.py
x = 10
print(x)  # Output: 10
 
# Reassign x to a new value
x = 20
print(x)  # Output: 20
 
# The old value (10) is gone; x now refers to 20

Here's what happened step by step:

  1. x = 10: Python creates the integer value 10 in memory and makes the name x refer to it
  2. print(x): Python looks up what x refers to (10) and prints it
  3. x = 20: Python creates the integer value 20 in memory and makes x refer to this new value instead
  4. print(x): Python looks up what x refers to (now 20) and prints it

The original value 10 still exists in memory temporarily, but since no variable refers to it anymore, Python's automatic memory management (called garbage collection) will eventually clean it up.

3.2.2) Reassignment and Updating Variables

One of the most common patterns in programming is updating a variable based on its current value. For example, you might want to increase a counter, add to a running total, or modify a value based on some calculation:

python
# updating_variables.py
score = 0
print("Initial score:", score)  # Output: Initial score: 0
 
# Add 10 points
score = score + 10
print("After first update:", score)  # Output: After first update: 10
 
# Add 5 more points
score = score + 5
print("After second update:", score)  # Output: After second update: 15
 
# Double the score
score = score * 2
print("After doubling:", score)  # Output: After doubling: 30

Let's break down what happens in score = score + 10:

  1. Python evaluates the right side: score + 10
  2. It looks up the current value of score (0)
  3. It calculates 0 + 10, getting 10
  4. It assigns this new value (10) to score
  5. Now score refers to 10 instead of 0

This pattern is so common that Python provides shorthand operators for it, which we'll learn about in Chapter 4 (Section 4.3). But for now, it's important to understand the full form because it clearly shows the order of operations: evaluate the right side completely, then assign to the left side.

3.2.3) Variables Are Independent

When you assign one variable to another, Python copies the reference, not the value itself (for simple types like numbers and strings). However, for the basic types we're learning in this chapter, variables behave independently after assignment:

python
# independent_variables.py
a = 10
b = a  # b now refers to the same value as a (10)
print("a:", a)  # Output: a: 10
print("b:", b)  # Output: b: 10
 
# Change a
a = 20
print("After changing a:")
print("a:", a)  # Output: a: 20
print("b:", b)  # Output: b: 10 (unchanged!)

When we assigned b = a, we made b refer to the same value that a was referring to at that moment (10). When we later changed a to refer to 20, b was unaffected—it still refers to 10.

This behavior is straightforward for numbers and strings, but becomes more complex with collections like lists and dictionaries. We'll explore this thoroughly in Chapter 17 when we discuss Python's object model and reference semantics.

3.2.4) Using Variables Before Assignment

One common mistake beginners make is trying to use a variable before it has been assigned a value. Python will raise a NameError if you try to do this:

python
# undefined_variable.py
print(total)  # Error: NameError: name 'total' is not defined

This error occurs because Python doesn't know what total refers to—you haven't created it yet. The fix is simple: assign a value to the variable before using it:

python
# defined_variable.py
total = 0  # Initialize the variable first
print(total)  # Output: 0
 
# Now we can update it
total = total + 10
print(total)  # Output: 10

This pattern of initializing a variable (giving it an initial value) before using it is fundamental in programming. We'll see many examples of this throughout the book, especially when working with counters and accumulators in loops (Chapter 10).

3.2.5) Swapping Variable Values

A common operation is swapping the values of two variables. In many programming languages, this requires a temporary variable:

python
# swap_with_temp.py
x = 10
y = 20
print("Before swap: x =", x, ", y =", y)  # Output: Before swap: x = 10 , y = 20
 
# Swap using a temporary variable
temp = x    # Save x's value
x = y       # Put y's value in x
y = temp    # Put saved value in y
 
print("After swap: x =", x, ", y =", y)  # Output: After swap: x = 20 , y = 10

However, Python provides a more elegant way using simultaneous assignment:

python
# swap_pythonic.py
x = 10
y = 20
print("Before swap: x =", x, ", y =", y)  # Output: Before swap: x = 10 , y = 20
 
# Swap in one line
x, y = y, x
 
print("After swap: x =", x, ", y =", y)  # Output: After swap: x = 20 , y = 10

This works because Python evaluates the entire right side (y, x) before performing any assignments. So it creates a temporary structure containing the values of y and x, then unpacks those values into x and y respectively. This is a nice example of Python's philosophy of making common operations simple and readable.

3.3) The Concept of Data Types and type()

3.3.1) What Are Data Types?

A data type (or simply type) defines what kind of data a value represents and what operations can be performed on it. Think of data types as categories that tell Python how to interpret and work with different kinds of information.

For example:

  • The number 42 is an integer (whole number)
  • The number 3.14 is a floating-point number (number with decimal point)
  • The text "Hello" is a string (sequence of characters)
  • The value True is a boolean (logical true/false value)

Why do data types matter? Because different types support different operations:

python
# type_operations.py
# You can add numbers
result1 = 10 + 5
print(result1)  # Output: 15
 
# You can also "add" strings (concatenation)
result2 = "Hello" + " " + "World"
print(result2)  # Output: Hello World
 
# But you can't add a number and a string directly
# result3 = 10 + "5"  # Error: TypeError: unsupported operand type(s)

The error in the last example occurs because Python doesn't know how to add a number and a string—they're fundamentally different types of data. Should it convert the number to text and concatenate? Should it convert the text to a number and add mathematically? Python requires you to be explicit about what you want by converting one value to match the other's type (we'll learn how in Section 3.7).

3.3.2) Using type() to Check Data Types

Python provides a built-in function called type() that tells you what type a value or variable is. This is incredibly useful for understanding your data and debugging problems:

python
# checking_types.py
# Check types of literal values
print(type(42))        # Output: <class 'int'>
print(type(3.14))      # Output: <class 'float'>
print(type("Hello"))   # Output: <class 'str'>
print(type(True))      # Output: <class 'bool'>
 
# Check types of variables
age = 25
name = "Alice"
temperature = 98.6
is_valid = False
 
print(type(age))         # Output: <class 'int'>
print(type(name))        # Output: <class 'str'>
print(type(temperature)) # Output: <class 'float'>
print(type(is_valid))    # Output: <class 'bool'>

The output <class 'int'> means "this value belongs to the class (type) called int." In Python, types are actually classes (we'll learn about classes in detail in Part VIII), but for now, you can think of these terms as interchangeable.

3.3.3) Dynamic Typing in Python

Python is a dynamically typed language, which means:

  1. Variables don't have fixed types: A variable can refer to values of different types at different times
  2. Types are determined at runtime: Python figures out types when the program runs, not when you write the code
  3. You don't declare types explicitly: Unlike some other languages, you don't need to say "this variable will hold an integer"

Here's an example of dynamic typing in action:

python
# dynamic_typing.py
x = 42
print(x, "is of type", type(x))  # Output: 42 is of type <class 'int'>
 
x = "Hello"
print(x, "is of type", type(x))  # Output: Hello is of type <class 'str'>
 
x = 3.14
print(x, "is of type", type(x))  # Output: 3.14 is of type <class 'float'>

Notice how x can refer to different types of values at different points in the program. Python doesn't complain—it simply updates what type x currently refers to.

While this flexibility is convenient, it also means you need to be careful. If you expect a variable to hold a number but it actually holds a string, you might get unexpected errors:

python
# type_confusion.py
value = "100"  # This is a string, not a number!
 
# Trying to do math with it will fail
# result = value + 50  # Error: TypeError: can only concatenate str to str
 
# You need to convert it first
result = int(value) + 50
print(result)  # Output: 150

This is why understanding data types is crucial—it helps you predict what operations will work and catch mistakes before they cause errors.

3.3.4) Python's Built-in Types Overview

Python has several built-in data types. In this chapter, we'll focus on the most fundamental ones:

Python Built-in Types

Numeric Types

Text Type

Boolean Type

None Type

Collection Types

int: Integers

float: Decimal Numbers

complex: Complex Numbers

str: Text/Strings

bool: True/False

NoneType: None

list, tuple, dict, set...

In this chapter, we'll cover:

  • int: Integer numbers (whole numbers)
  • float: Floating-point numbers (numbers with decimal points)
  • str: Strings (text)
  • bool: Boolean values (True and False)
  • NoneType: The special value None

We'll briefly mention complex numbers (numbers with imaginary components), but they're rarely needed for everyday programming. The collection types (like lists, tuples, dictionaries, and sets) are so important that we've dedicated entire chapters to them in Part IV.

3.3.5) Type Checking in Practice

Understanding types becomes especially important when working with user input or data from external sources. Remember that input() always returns a string, even if the user types a number:

python
# input_types.py
user_input = input("Enter a number: ")
print("You entered:", user_input)
print("Type:", type(user_input))  # Output: Type: <class 'str'>
 
# Even if user types "42", it's still a string!
# To use it as a number, convert it:
number = int(user_input)
print("As a number:", number)
print("Type:", type(number))  # Output: Type: <class 'int'>
 
# Now we can do math with it
doubled = number * 2
print("Doubled:", doubled)

When you run this program and enter 42, you'll see:

Enter a number: 42
You entered: 42
Type: <class 'str'>
As a number: 42
Type: <class 'int'>
Doubled: 84

This demonstrates a crucial point: the string "42" and the integer 42 are different things in Python, even though they look similar when printed. The string is a sequence of two characters ('4' and '2'), while the integer is a numeric value that can be used in mathematical operations.

3.4) Integer and Floating-Point Numbers

3.4.1) Integers (int)

An integer (type int) is a whole number with no decimal point. Integers can be positive, negative, or zero, and in Python 3, they can be arbitrarily large—limited only by your computer's available memory.

python
# integers.py
# Positive integers
age = 25
year = 2024
population = 8000000000
 
# Negative integers
temperature = -15
debt = -5000
 
# Zero
balance = 0
 
# Very large integers (Python handles these easily)
huge_number = 123456789012345678901234567890
print(huge_number)  # Output: 123456789012345678901234567890
print(type(huge_number))  # Output: <class 'int'>

Python allows you to write large numbers with underscores for readability. The underscores are ignored by Python but make the numbers easier for humans to read:

python
# readable_numbers.py
# These are all the same number
million = 1000000
million = 1_000_000  # Much easier to read!
 
# Works with any size
population = 8_000_000_000
national_debt = 31_000_000_000_000
 
print(million)      # Output: 1000000 (underscores not shown in output)
print(population)   # Output: 8000000000

You can also write integers in different number bases using special prefixes:

python
# number_bases.py
# Binary (base 2) - prefix 0b
binary = 0b1010  # This is 10 in decimal
print(binary)  # Output: 10
 
# Octal (base 8) - prefix 0o
octal = 0o12  # This is 10 in decimal
print(octal)  # Output: 10
 
# Hexadecimal (base 16) - prefix 0x
hexadecimal = 0xFF  # This is 255 in decimal
print(hexadecimal)  # Output: 255

These alternative bases are useful in certain contexts (like working with colors in web design or low-level programming), but for most everyday programming, you'll use regular decimal integers.

3.4.2) Floating-Point Numbers (float)

A floating-point number (type float) is a number with a decimal point. Floats are used to represent real numbers—values that aren't necessarily whole numbers:

python
# floats.py
# Numbers with decimal points
price = 19.99
temperature = 98.6
pi = 3.14159
 
# Very small numbers
electron_mass = 0.00000000000000000000000000000091093837
 
# Very large numbers
speed_of_light = 299792458.0
 
print(price)           # Output: 19.99
print(temperature)     # Output: 98.6
print(electron_mass)   # Output: 9.1093837e-31 (scientific notation)
print(speed_of_light)  # Output: 299792458.0

Notice that very small or very large floats are displayed in scientific notation (also called exponential notation). The notation 9.1093837e-31 means "9.1093837 × 10⁻³¹" or "9.1093837 divided by 10 raised to the 31st power."

You can also write floats using scientific notation directly:

python
# scientific_notation.py
# These are equivalent
avogadro = 602214076000000000000000.0
avogadro = 6.02214076e23  # Much more readable!
 
# Small numbers
planck = 0.000000000000000000000000000000000662607015
planck = 6.62607015e-34  # Much more readable!
 
print(avogadro)  # Output: 6.02214076e+23
print(planck)    # Output: 6.62607015e-34

The e (or E) stands for "exponent." The number after e tells you how many places to move the decimal point (positive means right, negative means left).

3.4.3) Integer vs Float: Important Differences

While integers and floats both represent numbers, they have important differences:

1. Precision and Representation:

Integers are exact—the value 42 is exactly 42, with no approximation. Floats, however, are approximations due to how computers store decimal numbers in binary:

python
# float_precision.py
# This might surprise you!
result = 0.1 + 0.2
print(result)  # Output: 0.30000000000000004
 
# The result isn't exactly 0.3 due to floating-point representation
print(result == 0.3)  # Output: False

This isn't a bug—it's a fundamental limitation of how computers represent decimal numbers. We'll discuss this in more detail in Chapter 4 (Section 4.10), but for now, just be aware that float calculations may not be perfectly precise.

2. Operations and Results:

When you perform operations with integers and floats, Python follows specific rules:

python
# int_float_operations.py
# Integer operations
int_result = 10 + 5
print(int_result, type(int_result))  # Output: 15 <class 'int'>
 
# Float operations
float_result = 10.0 + 5.0
print(float_result, type(float_result))  # Output: 15.0 <class 'float'>
 
# Mixed operations: result is always float
mixed_result = 10 + 5.0
print(mixed_result, type(mixed_result))  # Output: 15.0 <class 'float'>
 
# Division always returns float, even with integers
division_result = 10 / 5
print(division_result, type(division_result))  # Output: 2.0 <class 'float'>

The key rule: any operation involving at least one float produces a float result. This makes sense because floats can represent a wider range of values (including non-whole numbers), so Python "promotes" the result to the more general type.

3. Memory and Performance:

Integers use less memory and are faster to compute with than floats. For most programs, this difference is negligible, but it can matter in performance-critical applications or when working with very large datasets.

3.4.4) When to Use int vs float

Here are practical guidelines for choosing between integers and floats:

Use integers when:

  • Counting discrete items (people, objects, iterations)
  • Representing exact quantities that can't be fractional (number of students, number of clicks)
  • Working with indices or positions in sequences
  • Exact arithmetic is required
python
# use_integers.py
student_count = 30  # Can't have 30.5 students
page_number = 42    # Can't be on page 42.7
loop_counter = 0    # Counting iterations

Use floats when:

  • Representing measurements (temperature, distance, weight)
  • Working with money (though be careful with precision—we'll discuss this in Chapter 4)
  • Calculating ratios, percentages, or averages
  • Approximate values are acceptable
python
# use_floats.py
temperature = 72.5     # Temperature can be fractional
price = 19.99          # Money amounts have cents
average_score = 87.3   # Averages are often fractional
percentage = 0.15      # 15% as a decimal

3.5) String and Boolean Literals (A Quick Preview)

In this section, we'll briefly introduce two more fundamental data types: strings and booleans. We'll cover strings in much more depth in Chapters 5 and 6, and booleans in detail in Chapter 7, but you need a basic understanding of them now to write even simple programs.

3.5.1) String Basics

A string (type str) is a sequence of characters—essentially, text. You create strings by enclosing text in quotes. Python accepts either single quotes (') or double quotes ("):

python
# string_basics.py
# Single quotes
name = 'Alice'
message = 'Hello, World!'
 
# Double quotes (exactly equivalent)
name = "Alice"
message = "Hello, World!"
 
# Print them
print(name)     # Output: Alice
print(message)  # Output: Hello, World!
print(type(name))  # Output: <class 'str'>

Both single and double quotes work identically—choose whichever you prefer, but be consistent within your code. Many Python programmers prefer double quotes because they're more common in other programming languages, but single quotes are equally valid.

Why have both? The main reason is convenience when your string contains quotes:

python
# quotes_in_strings.py
# Use double quotes when string contains single quotes
sentence = "It's a beautiful day!"
print(sentence)  # Output: It's a beautiful day!
 
# Use single quotes when string contains double quotes
speech = 'She said, "Hello!"'
print(speech)  # Output: She said, "Hello!"
 
# Or use escape sequences (we'll learn more in Chapter 5)
sentence = 'It\'s a beautiful day!'  # \' means a literal single quote
speech = "She said, \"Hello!\""     # \" means a literal double quote

Strings can be empty (contain no characters):

python
# empty_string.py
empty = ""
also_empty = ''
 
print(empty)            # Output: (nothing—it's empty!)
print(len(empty))       # Output: 0 (we'll learn about len() later)
print(type(empty))      # Output: <class 'str'>

You can combine strings using the + operator (called concatenation):

python
# string_concatenation.py
first_name = "John"
last_name = "Doe"
full_name = first_name + " " + last_name
print(full_name)  # Output: John Doe
 
# Be careful: you can't concatenate strings and numbers directly
age = 25
# message = "I am " + age + " years old"  # Error: TypeError
 
# Convert the number to a string first
message = "I am " + str(age) + " years old"
print(message)  # Output: I am 25 years old

We'll explore strings much more thoroughly in Chapters 5 and 6, including escape sequences, string methods, formatting, and text processing. For now, just remember that strings represent text and are created with quotes.

3.5.2) Boolean Basics

A boolean (type bool) is a logical value that can be either True or False. These are the only two boolean values in Python, and they're used to represent truth and falsehood in logical operations:

python
# boolean_basics.py
# Boolean literals
is_sunny = True
is_raining = False
 
print(is_sunny)         # Output: True
print(is_raining)       # Output: False
print(type(is_sunny))   # Output: <class 'bool'>

Important: The boolean values True and False must be capitalized exactly as shown. Python is case-sensitive, so true, TRUE, false, and FALSE won't work:

python
# boolean_case.py
correct = True    # Correct
# wrong = true    # Error: NameError: name 'true' is not defined
# wrong = TRUE    # Error: NameError: name 'TRUE' is not defined

Booleans typically come from comparisons or logical operations:

python
# boolean_from_comparisons.py
age = 25
 
# Comparison operators produce boolean results
is_adult = age >= 18
print(is_adult)  # Output: True
 
is_senior = age >= 65
print(is_senior)  # Output: False
 
# You can use booleans in conditions (we'll learn more in Chapter 8)
if is_adult:
    print("You can vote!")  # Output: You can vote!
 
if is_senior:
    print("You get a senior discount!")  # (no output—condition is False)

Common comparison operators that produce booleans:

  • == : equal to
  • != : not equal to
  • < : less than
  • > : greater than
  • <= : less than or equal to
  • >= : greater than or equal to
python
# comparison_operators.py
x = 10
y = 20
 
print(x == y)   # Output: False (10 is not equal to 20)
print(x != y)   # Output: True (10 is not equal to 20)
print(x < y)    # Output: True (10 is less than 20)
print(x > y)    # Output: False (10 is not greater than 20)
print(x <= 10)  # Output: True (10 is less than or equal to 10)
print(y >= 20)  # Output: True (20 is greater than or equal to 20)

Important note: Don't confuse = (assignment) with == (comparison):

  • x = 10 means "assign the value 10 to x"
  • x == 10 means "check if x is equal to 10" (produces True or False)
python
# assignment_vs_comparison.py
x = 10        # Assignment: x now refers to 10
result = (x == 10)  # Comparison: is x equal to 10?
print(result)  # Output: True
 
# This is a common mistake for beginners:
# if x = 10:  # Error: SyntaxError (can't use assignment in if condition)
if x == 10:   # Correct: comparison
    print("x is 10")  # Output: x is 10

We'll explore booleans in much more detail in Chapter 7, including logical operators (and, or, not), truthiness and falsiness, and how Python uses booleans in conditions. For now, just remember that booleans represent True/False values and typically come from comparisons.

3.5.3) Strings and Booleans in Context

Let's see how strings and booleans work together in a simple practical example:

python
# strings_booleans_example.py
# Get user input
name = input("What is your name? ")
age_input = input("What is your age? ")
 
# Convert age to integer
age = int(age_input)
 
# Create boolean conditions
is_adult = age >= 18
is_child = age < 13
 
# Build messages using strings
if is_adult:
    status = "adult"
else:
    status = "minor"
 
# Combine everything in output
print("Hello, " + name + "!")
print("You are an " + status + ".")
 
if is_child:
    print("You are a child.")
 
# When user enters "Alice" and "16":
# Output: Hello, Alice!
# Output: You are a minor.

This example demonstrates how different data types work together: strings for text, integers for numbers, and booleans for logical decisions. This interplay between types is fundamental to all programming.

3.6) The None Value and Its Uses

3.6.1) What Is None?

Python has a special value called None (type NoneType) that represents the absence of a value. It's Python's way of saying "there's nothing here" or "this has no value yet."

python
# none_basics.py
result = None
print(result)        # Output: None
print(type(result))  # Output: <class 'NoneType'>

None is not the same as zero, an empty string, or False—it's a distinct value that specifically means "no value":

python
# none_vs_others.py
x = None
y = 0
z = ""
w = False
 
print(x)  # Output: None
print(y)  # Output: 0
print(z)  # Output: (empty—nothing prints)
print(w)  # Output: False
 
# They're all different types
print(type(x))  # Output: <class 'NoneType'>
print(type(y))  # Output: <class 'int'>
print(type(z))  # Output: <class 'str'>
print(type(w))  # Output: <class 'bool'>

3.6.2) When None Is Used

None appears in several common situations:

1. As a placeholder for values you'll assign later:

python
# none_placeholder.py
# Initialize variables that will be assigned later
user_name = None
user_age = None
 
# Later in the program, after getting user input:
user_name = input("Enter your name: ")
user_age = int(input("Enter your age: "))
 
print("Name:", user_name)
print("Age:", user_age)

2. As the default return value of functions that don't explicitly return anything:

Functions we'll learn about in Chapter 19 can return values. If a function doesn't explicitly return anything, Python automatically returns None:

python
# none_from_function.py
# The print() function returns None
result = print("Hello!")  # Output: Hello!
print(result)             # Output: None
print(type(result))       # Output: <class 'NoneType'>

This might seem odd, but it's useful. It means every function returns something, even if that something is "no meaningful value."

3. To represent optional or missing data:

python
# none_optional.py
# Representing optional middle name
first_name = "John"
middle_name = None  # No middle name
last_name = "Doe"
 
if middle_name is None:
    full_name = first_name + " " + last_name
else:
    full_name = first_name + " " + middle_name + " " + last_name
 
print(full_name)  # Output: John Doe

3.6.3) Checking for None

To check if a value is None, use the is operator (not ==):

python
# checking_none.py
value = None
 
# Correct way: use 'is'
if value is None:
    print("Value is None")  # Output: Value is None
 
# Also correct: use 'is not'
if value is not None:
    print("Value has a value")
else:
    print("Value is None")  # Output: Value is None
 
# While == works, 'is' is preferred for None
if value == None:  # This works but is not idiomatic Python
    print("Value is None")  # Output: Value is None

Why use is instead of ==? The is operator checks if two names refer to the exact same object in memory (we'll learn more about this in Chapter 17). Since there's only one None object in Python, is is both more efficient and more precise for this check. Using is None is the standard Python idiom.

3.6.4) None in Practical Use

Here's a practical example showing how None is useful in real programs:

python
# none_practical.py
# Simulate looking up a user's age
# (In real programs, we'd use functions and dictionaries from later chapters)
 
user_name = input("Enter a name (Alice, Bob, or Charlie): ")
 
# Check each name and assign age or None
if user_name == "Alice":
    user_age = 25
elif user_name == "Bob":
    user_age = 30
elif user_name == "Charlie":
    user_age = 35
else:
    user_age = None  # User not found
 
# Check if we found the user
if user_age is not None:
    print(user_name, "is", user_age, "years old")
else:
    print(user_name, "not found")
 
# When user enters "Alice":
# Output: Alice is 25 years old
# When user enters "David":
# Output: David not found

In this example, None clearly indicates "user not found," which is different from finding a user with age 0 (which would be a valid age for a newborn).

3.6.5) None vs Empty Values

It's important to distinguish None from empty values:

python
# none_vs_empty.py
# These are all different
nothing = None
zero = 0
empty_string = ""
# Note: We'll learn about lists in Chapter 13
# empty_list = []
 
print(nothing is None)       # Output: True
print(zero is None)          # Output: False
print(empty_string is None)  # Output: False
 
# None means "no value"
# 0 means "the number zero"
# "" means "text with no characters"

Each of these represents a different concept:

  • None: absence of a value
  • 0: a specific numeric value (zero)
  • "": a string that exists but contains no characters

Understanding this distinction will help you write clearer, more correct programs.

3.7) Basic Type Conversion with int(), float(), and str()

3.7.1) Why Type Conversion Is Necessary

As we've seen, Python has strict rules about what operations work with what types. You can't add a number and a string, multiply a string by a float, or perform mathematical operations on text. When you need to use a value as a different type, you must convert it explicitly.

Type conversion (also called type casting) is the process of changing a value from one type to another. Python provides built-in functions for the most common conversions:

  • int(): Convert to integer
  • float(): Convert to floating-point number
  • str(): Convert to string

Let's explore each of these in detail.

3.7.2) Converting to Integer with int()

The int() function converts a value to an integer. Here are the main use cases:

Converting floats to integers:

python
# float_to_int.py
# int() truncates (cuts off) the decimal part
x = int(3.14)
y = int(3.99)
z = int(-2.7)
 
print(x)  # Output: 3 (not 4—it doesn't round!)
print(y)  # Output: 3 (not 4—it truncates!)
print(z)  # Output: -2 (truncates toward zero)

Important: int() doesn't round—it simply removes the decimal part (truncates toward zero). This is a common source of confusion for beginners who expect int(3.9) to return 4.

Converting strings to integers:

This is extremely common when working with user input:

python
# string_to_int.py
# Convert string containing a number
age_str = "25"
age_int = int(age_str)
 
print(age_str, type(age_str))  # Output: 25 <class 'str'>
print(age_int, type(age_int))  # Output: 25 <class 'int'>
 
# Now we can do math with it
next_year = age_int + 1
print("Next year:", next_year)  # Output: Next year: 26
 
# Practical example with input()
user_age = int(input("Enter your age: "))
print("In 10 years, you'll be", user_age + 10)

What happens with invalid conversions?

If you try to convert a string that doesn't represent a valid integer, Python raises a ValueError:

python
# invalid_int_conversion.py
# These work
print(int("123"))    # Output: 123
print(int("-456"))   # Output: -456
print(int("  789 ")) # Output: 789 (whitespace is ignored)
 
# These don't work
# print(int("12.5"))   # Error: ValueError: invalid literal for int()
# print(int("hello"))  # Error: ValueError: invalid literal for int()
# print(int("12 34"))  # Error: ValueError: invalid literal for int()

We'll learn how to handle these errors gracefully in Chapter 27 when we cover exception handling. For now, just be aware that you need to ensure your strings contain valid integer representations before converting.

Converting booleans to integers:

Booleans can be converted to integers, where True becomes 1 and False becomes 0:

python
# bool_to_int.py
print(int(True))   # Output: 1
print(int(False))  # Output: 0
 
# This is sometimes useful in calculations
is_premium = True
is_student = False
 
# Calculate discount (10% for premium, 5% for students)
discount = int(is_premium) * 0.10 + int(is_student) * 0.05
print("Discount:", discount)  # Output: Discount: 0.1

However, using booleans directly in arithmetic is generally discouraged because it makes code less readable. We'll discuss this more in Chapter 7.

3.7.3) Converting to Float with float()

The float() function converts a value to a floating-point number:

Converting integers to floats:

python
# int_to_float.py
x = float(42)
y = float(-17)
z = float(0)
 
print(x, type(x))  # Output: 42.0 <class 'float'>
print(y, type(y))  # Output: -17.0 <class 'float'>
print(z, type(z))  # Output: 0.0 <class 'float'>

Converting strings to floats:

python
# string_to_float.py
# Convert string containing decimal number
price_str = "19.99"
price_float = float(price_str)
 
print(price_str, type(price_str))    # Output: 19.99 <class 'str'>
print(price_float, type(price_float))  # Output: 19.99 <class 'float'>
 
# Strings without decimal points work too
x = float("42")
print(x, type(x))  # Output: 42.0 <class 'float'>
 
# Scientific notation strings work
big = float("1.5e10")
print(big)  # Output: 15000000000.0

Invalid conversions:

Like int(), float() raises a ValueError for invalid strings:

python
# invalid_float_conversion.py
# These work
print(float("3.14"))      # Output: 3.14
print(float("  2.5  "))   # Output: 2.5 (whitespace ignored)
print(float("-0.5"))      # Output: -0.5
print(float("inf"))       # Output: inf (infinity)
 
# These don't work
# print(float("hello"))   # Error: ValueError
# print(float("1.2.3"))   # Error: ValueError

Converting booleans to floats:

python
# bool_to_float.py
print(float(True))   # Output: 1.0
print(float(False))  # Output: 0.0

3.7.4) Converting to String with str()

The str() function converts any value to its string representation:

Converting numbers to strings:

python
# number_to_string.py
# Convert integer
age = 25
age_str = str(age)
print(age_str, type(age_str))  # Output: 25 <class 'str'>
 
# Convert float
price = 19.99
price_str = str(price)
print(price_str, type(price_str))  # Output: 19.99 <class 'str'>
 
# Now we can concatenate with other strings
message = "The price is $" + price_str
print(message)  # Output: The price is $19.99

This is particularly useful when building messages:

python
# building_messages.py
name = "Alice"
age = 25
height = 5.6
 
# Without str(), this would cause an error
# message = "Name: " + name + ", Age: " + age  # Error!
 
# With str(), it works
message = "Name: " + name + ", Age: " + str(age) + ", Height: " + str(height)
print(message)  # Output: Name: Alice, Age: 25, Height: 5.6

Converting booleans to strings:

python
# bool_to_string.py
is_valid = True
is_error = False
 
print(str(is_valid))  # Output: True
print(str(is_error))  # Output: False
 
# Useful in messages
status = "Status: " + str(is_valid)
print(status)  # Output: Status: True

Converting None to string:

python
# none_to_string.py
value = None
value_str = str(value)
print(value_str)        # Output: None
print(type(value_str))  # Output: <class 'str'>
 
# The string "None" is different from the value None
print(value is None)      # Output: True
print(value_str is None)  # Output: False
print(value_str == "None")  # Output: True

3.7.5) Conversion Patterns in Practice

Here's a comprehensive example showing common conversion patterns:

python
# conversion_patterns.py
# Get user input (always strings)
name = input("Enter your name: ")
age_str = input("Enter your age: ")
height_str = input("Enter your height in feet: ")
 
# Convert to appropriate types
age = int(age_str)
height = float(height_str)
 
# Perform calculations
age_in_months = age * 12
height_in_inches = height * 12
 
# Convert back to strings for output
print("Hello, " + name + "!")
print("You are " + str(age_in_months) + " months old.")
print("Your height is " + str(height_in_inches) + " inches.")
 
# Alternative: use multiple arguments to print() (no conversion needed)
print("Hello,", name + "!")
print("You are", age_in_months, "months old.")
print("Your height is", height_in_inches, "inches.")
 
# When user enters "Alice", "25", and "5.5":
# Output: Hello, Alice!
# Output: You are 300 months old.
# Output: Your height is 66.0 inches.
# Output: Hello, Alice!
# Output: You are 300 months old.
# Output: Your height is 66.0 inches.

Notice how print() with multiple arguments (separated by commas) automatically converts values to strings and adds spaces between them. This is often more convenient than manual string conversion and concatenation.

3.7.6) Conversion Flow Diagram

Here's a visual representation of common type conversions:

float

int truncates

str

str

int

float

str

int

float

int

float

str

bool

Key points about conversions:

  • int → float: Always safe, adds .0
  • float → int: Truncates decimal part (doesn't round)
  • Any → str: Always safe, converts to text representation
  • str → int/float: Only works if string represents a valid number
  • bool → int/float: True becomes 1/1.0, False becomes 0/0.0

3.7.7) Common Conversion Mistakes

Here are some common mistakes beginners make with type conversion:

Mistake 1: Forgetting to convert user input

python
# conversion_mistake1.py
# Wrong: trying to do math with string
age = input("Enter your age: ")
# next_year = age + 1  # Error: TypeError
 
# Right: convert first
age = int(input("Enter your age: "))
next_year = age + 1
print("Next year:", next_year)

Mistake 2: Converting when not necessary

python
# conversion_mistake2.py
# Unnecessary: print() handles conversion automatically
age = 25
print("Age:", age)  # This works fine
 
# No need for:
print("Age:", str(age))  # Unnecessary str() conversion

Mistake 3: Trying to convert invalid strings

python
# conversion_mistake3.py
# This will crash if user enters non-numeric input
# age = int(input("Enter your age: "))  # Crashes on "twenty"
 
# We'll learn to handle this safely in Chapter 27

Mistake 4: Expecting int() to round

python
# conversion_mistake4.py
# Wrong expectation: int() truncates, doesn't round
x = int(3.7)
print(x)  # Output: 3 (not 4!)
 
# If you want rounding, use round()
x = round(3.7)
print(x)  # Output: 4

3.8) Getting String Representations with str() and repr()

3.8.1) The Difference Between str() and repr()

Python provides two ways to get a string representation of an object: str() and repr(). While they might seem similar, they serve different purposes:

  • str(): Creates a "nice," human-readable string representation
  • repr(): Creates an "official," unambiguous string representation meant for developers

For simple types like numbers, these are often the same, but for other types, they can differ significantly:

python
# str_vs_repr.py
# For numbers, they're usually the same
x = 42
print(str(x))   # Output: 42
print(repr(x))  # Output: 42
 
# For strings, they differ
text = "Hello"
print(str(text))   # Output: Hello
print(repr(text))  # Output: 'Hello' (includes quotes!)
 
# For strings with special characters, repr() shows escape sequences
message = "Hello\nWorld"
print(str(message))   # Output: Hello
                      #         World (newline is interpreted)
print(repr(message))  # Output: 'Hello\nWorld' (shows the \n literally)

3.8.2) When to Use str()

Use str() when you want a readable representation for end users:

python
# using_str.py
price = 19.99
quantity = 3
 
# Create user-friendly messages
message = "Total: $" + str(price * quantity)
print(message)  # Output: Total: $59.97
 
# str() is what print() uses automatically
print("Total: $", price * quantity)  # Output: Total: $ 59.97

The str() function is designed to produce output that makes sense to humans, even if it loses some technical precision. When you call print(), Python automatically calls str() on the values you pass to it.

3.8.3) When to Use repr()

Use repr() when you need an unambiguous representation, typically for debugging:

python
# using_repr.py
# Debugging: see exactly what's in a variable
text = "Hello\nWorld"
print("Debug info:", repr(text))  # Output: Debug info: 'Hello\nWorld'
 
# Compare two similar-looking strings
str1 = "42"
str2 = "42 "  # Has trailing space
 
print(str1)       # Output: 42
print(str2)       # Output: 42  (space not obvious)
print(repr(str1)) # Output: '42'
print(repr(str2)) # Output: '42 ' (space is visible!)

The repr() function is designed to show you exactly what Python sees, including details that might not be visible in normal output. This makes it invaluable for debugging.

3.8.4) The repr() Goal: Recreatable Representations

Ideally, repr() should produce a string that, if you passed it to Python's interpreter, would recreate the original object. For basic types, this works:

python
# repr_recreate.py
# For numbers
x = 42
x_repr = repr(x)
print(x_repr)  # Output: 42
 
# You could use this to recreate x
x_recreated = eval(x_repr)  # eval() evaluates a string as Python code
print(x_recreated)  # Output: 42
 
# For strings
text = "Hello"
text_repr = repr(text)
print(text_repr)  # Output: 'Hello'
 
# This could recreate the string
text_recreated = eval(text_repr)
print(text_recreated)  # Output: Hello

Important note: The eval() function evaluates a string as Python code. We mention it here to explain repr()'s purpose, but never use eval() on untrusted input—it's a security risk. We'll discuss safer alternatives in later chapters.

3.8.5) Practical Examples of str() and repr()

Example 1: Debugging string issues

python
# debugging_strings.py
# User input might have unexpected whitespace
user_input = "  Alice  "
 
print("User entered:", user_input)         # Output: User entered:   Alice  
print("Debug view:", repr(user_input))     # Output: Debug view: '  Alice  '
 
# Now the extra spaces are obvious!
cleaned = user_input.strip()  # Remove leading/trailing whitespace
print("Cleaned:", repr(cleaned))  # Output: Cleaned: 'Alice'

Example 2: Comparing different types

python
# comparing_types.py
# These look similar but are different
num = 42
text = "42"
 
print("Number:", num)       # Output: Number: 42
print("Text:", text)        # Output: Text: 42
 
print("Number repr:", repr(num))   # Output: Number repr: 42
print("Text repr:", repr(text))    # Output: Text repr: '42'
 
# repr() makes the difference clear
print("Are they equal?", num == text)  # Output: Are they equal? False

3.8.6) str() and repr() with Other Types

Let's see how str() and repr() work with the types we've learned:

python
# str_repr_types.py
# Integers
x = 42
print("int str:", str(x))    # Output: int str: 42
print("int repr:", repr(x))  # Output: int repr: 42
 
# Floats
y = 3.14159
print("float str:", str(y))   # Output: float str: 3.14159
print("float repr:", repr(y)) # Output: float repr: 3.14159
 
# Booleans
b = True
print("bool str:", str(b))   # Output: bool str: True
print("bool repr:", repr(b)) # Output: bool repr: True
 
# None
n = None
print("None str:", str(n))   # Output: None str: None
print("None repr:", repr(n)) # Output: None repr: None
 
# Strings (where they differ most)
s = "Hello\nWorld"
print("string str:", str(s))   # Output: string str: Hello
                                #                     World
print("string repr:", repr(s)) # Output: string repr: 'Hello\nWorld'

3.8.7) Key Takeaways

Use str() when:

  • Creating output for end users
  • Building user-facing messages
  • Formatting data for display
  • You want readable, friendly output

Use repr() when:

  • Debugging code
  • Logging technical information
  • You need to see exactly what's in a variable
  • You want unambiguous representations

Remember:

  • print() automatically uses str() on its arguments
  • For most basic types, str() and repr() give similar results
  • For strings, repr() includes quotes and shows escape sequences
  • repr() is designed to be unambiguous and (ideally) recreatable

Chapter Summary

In this chapter, you've learned the fundamental concepts of variables and data types in Python. Let's recap the key points:

Variables:

  • Variables are names that refer to values in memory
  • Created using assignment: variable_name = value
  • Can be reassigned to new values at any time
  • Must follow naming rules and should follow conventions
  • Use descriptive names to make code self-documenting

Data Types:

  • Every value in Python has a type
  • Use type() to check a value's type
  • Python is dynamically typed—variables can refer to different types at different times
  • Different types support different operations

The Basic Types:

  • int: Whole numbers (positive, negative, or zero), arbitrary size
  • float: Numbers with decimal points, may have precision limitations
  • str: Text enclosed in quotes (single or double)
  • bool: Logical values True or False
  • None: Special value representing absence of a value

Type Conversion:

  • int(): Convert to integer (truncates floats, parses strings)
  • float(): Convert to floating-point number
  • str(): Convert to string (always works)
  • Conversions can fail with invalid input (raises ValueError)

String Representations:

  • str(): Human-readable representation for end users
  • repr(): Unambiguous representation for developers/debugging
  • print() uses str() automatically

These concepts form the foundation for everything else in Python. Every program you write will use variables to store data, and understanding data types helps you predict what operations will work and what errors might occur.

In the next chapter, we'll build on this foundation by exploring how to work with numbers in detail, including arithmetic operations, operator precedence, and common numeric patterns. You'll learn how to perform calculations, work with mathematical functions, and handle the quirks of floating-point arithmetic.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai