On this page

Variables and Data Types

14 min read TextCh. 1 — Python Fundamentals

Variables and Data Types

One of the first things you notice when coming to Python from a language like Java or C++ is the absence of type declarations. In those languages, you must write something like int age = 25; or String name = "Alice";. In Python, you simply write age = 25 and name = "Alice". Python figures out the type on its own. This is called dynamic typing, and it is one of the features that makes Python so fast to write.

Understanding how Python handles types is fundamental — it influences how you write functions, how you debug errors, and how you design systems. This lesson covers the core built-in types you will use in virtually every Python program you ever write.

Variables and Assignment

A variable in Python is a name that refers to a value stored in memory. You create a variable by assigning a value to it with the = operator:

city = "La Paz"
population = 789_541    # underscores in numbers improve readability
altitude = 3_640.0      # meters above sea level
is_capital = True

print(city, population, altitude, is_capital)
# La Paz 789541 3640.0 True

Variable names follow these rules:

  • Must start with a letter or underscore (_)
  • Can contain letters, digits, and underscores
  • Case-sensitive (name and Name are different variables)
  • Cannot be a reserved keyword (if, for, while, True, False, None, etc.)

Python style (PEP 8) recommends snake_case for variable names: all lowercase with underscores separating words.

Dynamic Typing: Types Follow Values

In Python, types are attached to values, not to variable names. This means the same variable can hold different types at different points in time:

x = 42
print(type(x))    # <class 'int'>

x = "now I'm a string"
print(type(x))    # <class 'str'>

x = [1, 2, 3]
print(type(x))    # <class 'list'>

While this flexibility is convenient, it also means you need to be careful — especially when writing functions that receive values from external sources. Python 3 added type hints to help with this (covered in Lesson 15).

The `int` Type

Integers in Python have arbitrary precision. Unlike many languages where an int is limited to 32 or 64 bits, Python integers can be as large as your system's memory allows:

small = 42
big = 10 ** 100    # A googol — no overflow!
negative = -273

# Integer arithmetic
print(17 // 5)   # 3  — floor division (rounds toward negative infinity)
print(17 % 5)    # 2  — modulo (remainder)
print(2 ** 10)   # 1024 — exponentiation
print(abs(-17))  # 17  — absolute value

# Different bases
binary = 0b1010     # 10 in decimal
octal = 0o17        # 15 in decimal
hexadecimal = 0xFF  # 255 in decimal

print(binary, octal, hexadecimal)   # 10 15 255

You can convert between integers and other types:

print(int("42"))      # 42 — string to int
print(int(3.9))       # 3  — float to int (truncates, does NOT round)
print(int(True))      # 1
print(int(False))     # 0

The `float` Type

Floats represent decimal numbers. Python uses IEEE 754 double-precision (64-bit) floating-point:

pi = 3.14159265358979
speed_of_light = 3e8       # Scientific notation: 3 × 10^8
tiny = 1.5e-10             # 0.00000000015

print(round(pi, 2))        # 3.14
print(round(3.14159, 4))   # 3.1416

# Floating-point gotcha — inherent to how computers store decimals
print(0.1 + 0.2)           # 0.30000000000000004 (not exactly 0.3!)

# Use the decimal module when exactness matters (finance, etc.)
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))   # 0.3 (exact)

tip type: warning title: "Never compare floats with =="

Due to floating-point representation, 0.1 + 0.2 == 0.3 evaluates to False. Instead, use math.isclose(a, b) for approximate comparisons: import math; math.isclose(0.1 + 0.2, 0.3) returns True.

The `str` Type

Strings are sequences of Unicode characters. You can create them with single quotes, double quotes, or triple quotes for multi-line strings:

single = 'Hello'
double = "World"
multi = """This string
spans multiple
lines."""

# Escape sequences
tab_example = "Name\tAge"      # \t is a tab character
newline_example = "Line 1\nLine 2"
raw = r"C:\Users\name\docs"    # Raw string — backslashes are literal

print(tab_example)
print(newline_example)
print(raw)

Strings support a rich set of methods:

text = "  Python 3.14 is Amazing  "

print(text.strip())            # "Python 3.14 is Amazing" — removes leading/trailing whitespace
print(text.strip().lower())    # "python 3.14 is amazing"
print(text.strip().upper())    # "PYTHON 3.14 IS AMAZING"
print(text.strip().title())    # "Python 3.14 Is Amazing"
print("python" in text)        # False (case-sensitive)
print("Python" in text)        # True
print(text.strip().split())    # ['Python', '3.14', 'is', 'Amazing']
print(", ".join(["a", "b", "c"]))  # "a, b, c"
print(text.strip().startswith("Python"))  # True
print(text.strip().count("i"))            # 1

Strings are immutable — you cannot change a character in place. Every string method returns a new string.

F-Strings: The Modern Way to Format Strings

F-strings (formatted string literals), introduced in Python 3.6 and significantly enhanced since, are the preferred way to embed values in strings. You prefix the string with f and put expressions inside {}:

name = "Alice"
age = 30
height = 1.75

# Basic embedding
print(f"Name: {name}, Age: {age}")   # Name: Alice, Age: 30

# Expressions inside braces
print(f"In 10 years, {name} will be {age + 10}.")   # In 10 years, Alice will be 40.

# Format specifiers
print(f"Height: {height:.2f} m")     # Height: 1.75 m (2 decimal places)
print(f"Age in hex: {age:#x}")       # Age in hex: 0x1e
print(f"Big number: {1_000_000:,}")  # Big number: 1,000,000 (comma separator)
print(f"Percentage: {0.856:.1%}")    # Percentage: 85.6%

# Alignment and padding
print(f"{'left':<10}|")    # "left      |" (left-align, width 10)
print(f"{'right':>10}|")   # "     right|" (right-align, width 10)
print(f"{'center':^10}|")  # "  center  |" (center, width 10)

# Python 3.8+ self-documenting expressions with = sign
x = 42
print(f"{x = }")   # x = 42  (great for debugging!)

The `bool` Type

Booleans represent truth values. In Python, True and False (capitalized) are the only two boolean values:

is_active = True
is_deleted = False

print(type(is_active))   # <class 'bool'>

# Booleans are integers in disguise
print(True + True)   # 2
print(True * 5)      # 5
print(False + 1)     # 1

# Truthiness — every value has a boolean interpretation
print(bool(0))        # False
print(bool(1))        # True
print(bool(-42))      # True (any non-zero number is truthy)
print(bool(""))       # False (empty string is falsy)
print(bool("hello"))  # True
print(bool([]))       # False (empty list is falsy)
print(bool([0]))      # True (non-empty list is truthy, even if it contains 0)
print(bool(None))     # False

Understanding truthiness is crucial in Python — conditions in if statements and while loops evaluate any expression using these rules.

The `None` Type

None is Python's null value. It represents the absence of a value and is the only instance of the NoneType class:

result = None

print(result)         # None
print(type(result))   # <class 'NoneType'>

# Always compare with `is`, not `==`
if result is None:
    print("No result yet")

# Functions that do not return a value implicitly return None
def greet(name: str) -> None:
    print(f"Hello, {name}!")

output = greet("Bob")
print(output)    # None

tip type: tip title: "Use is None not == None"

Always check for None using if value is None: rather than if value == None:. The is operator checks identity (same object in memory), while == checks equality. For None, is is the semantically correct and Pythonic approach.

Type Conversion

Python provides built-in functions to convert between types:

# str() converts anything to a string
print(str(42))         # "42"
print(str(3.14))       # "3.14"
print(str(True))       # "True"
print(str(None))       # "None"

# int() converts strings and floats to integers
print(int("100"))      # 100
print(int(9.99))       # 9 (truncates — does not round)

# float() converts strings and ints to floats
print(float("3.14"))   # 3.14
print(float(7))        # 7.0

# bool() converts to boolean
print(bool(""))        # False
print(bool("any text"))  # True

Checking Types at Runtime

value = 42.5

# type() returns the exact type
print(type(value))            # <class 'float'>
print(type(value) is float)   # True

# isinstance() checks if a value is of a given type (or a subtype)
print(isinstance(value, float))           # True
print(isinstance(value, (int, float)))    # True — checks against a tuple of types
print(isinstance(True, int))             # True — bool is a subclass of int!

Prefer isinstance() over type() comparisons in most cases — it works correctly with inheritance and is more Pythonic.

nextSteps

  • operators-and-control-flow