Python Variables and Data Types β€” The Complete Guide

Python BasicsVariables and Data TypesFree Lesson

Advertisement

Python Variables and Data Types

Learning Objectives

By the end of this tutorial, you will be able to:

  • Understand how Python variables differ from variables in statically-typed languages like C and Java
  • Identify and use all major Python data types including numeric, text, boolean, sequence, mapping, set, and binary types
  • Perform type checking with type() and isinstance(), and convert between types safely
  • Apply dynamic typing principles to write flexible, idiomatic Python code
  • Recognize and avoid common mistakes related to variable assignment and type behavior

What Are Variables?

In Python, a variable is a name that refers to a value stored in memory. Unlike C or Java where variables have fixed types declared at compile time, Python variables are labels (or references) bound to objects. When you write x = 42, Python creates an integer object with the value 42 and binds the name x to it.

Python uses reference counting for memory management. Every object tracks how many names refer to it. When the reference count drops to zero, the memory is freed automatically.

FeatureC / JavaPython
Type declarationRequired (int x = 42;)Not required (x = 42)
Type bindingCompile-time (static)Run-time (dynamic)
Memory managementManual or GCReference counting + garbage collector
Variable meaningMemory locationReference to an object

Creating Variables

Creating a variable in Python is as simple as assigning a value. No type declaration is required.

age = 25                 # int
temperature = 98.6       # float
name = "Alice"           # str
is_active = True         # bool

x, y, z = 10, 20, 30    # Multiple assignment
a = b = c = 0            # Same value to all

m, n = 5, 10
m, n = n, m              # Swap β€” Pythonic way
print(m, n)              # 10 5

Python supports optional type annotations (Python 3.6+) for clarity and static analysis, but they do not enforce types at runtime:

count: int = 42
name: str = "Bob"
count = "now I'm a string"  # Valid β€” type hints are not enforced

Python Data Types Deep Dive

Numeric Types

int β€” Integers

Python integers have unlimited precision. Unlike C's int, they can be arbitrarily large.

big_number = 10 ** 100   # No overflow

binary_num = 0b1010      # Binary: 10
octal_num = 0o17         # Octal: 15
hex_num = 0xFF           # Hex: 255
population = 7_900_000_000  # Underscores for readability (3.6+)

float β€” Floating-Point Numbers

Python floats are IEEE 754 double-precision (64-bit).

speed_of_light = 3e8     # Scientific notation: 300000000.0

# ⚠️ Precision gotcha
print(0.1 + 0.2)         # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False

import math
print(math.isclose(0.1 + 0.2, 0.3))  # True β€” use this instead

print(math.inf)          # inf (positive infinity)
print(float('nan') == float('nan'))  # False β€” NaN != NaN

complex β€” Complex Numbers

z = 3 + 4j
print(z.real, z.imag)    # 3.0 4.0
print(abs(z))            # 5.0 (magnitude)

decimal β€” Precise Decimal Arithmetic

For financial calculations where IEEE 754 floating-point errors are unacceptable:

from decimal import Decimal, getcontext, ROUND_HALF_UP
getcontext().prec = 28

price = Decimal('19.99')
tax = Decimal('0.08')
total = (price * tax).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
print(total)  # 1.60

Text Type

str β€” Strings

Strings in Python are immutable sequences of Unicode characters.

s = "Hello, World!"
raw = r"C:\new\folder\test"       # Raw string β€” no escape processing
multi = """Triple quotes
span multiple lines"""

# f-strings (3.6+)
name = "Alice"
age = 30
print(f"My name is {name} and I'll be {age + 1} next year.")

# Strings are immutable
# s[0] = "h"  # TypeError
s_new = "h" + s[1:]  # Create a new string

# Common methods
text = "  Hello, Python World!  "
print(text.strip())                          # "Hello, Python World!"
print(text.lower())                          # "  hello, python world!  "
print(text.replace("World", "Universe"))     # "  Hello, Python Universe!  "
print(text.split(","))                       # ["  Hello", " Python World!  "]
print("-".join(["a", "b", "c"]))             # a-b-c

Boolean Type

bool β€” Booleans

Booleans are a subclass of integers. True equals 1, False equals 0.

print(True + True)         # 2
print(isinstance(True, int))  # True

# Truthy and Falsy values
# Everything evaluates to False except:
# False, None, 0, 0.0, 0j, "", [], (), {}, set(), frozenset(), range(0)

print(bool(""))           # False
print(bool("False"))      # True β€” non-empty string!
print(bool(0))            # False
print(bool(-1))           # True β€” any non-zero number

Sequence Types

list β€” Lists (Mutable, Ordered)

fruits = ["apple", "banana", "cherry"]
mixed = [1, "hello", 3.14, True, None]  # Mixed types allowed

fruits.append("date")
fruits.insert(1, "blueberry")
fruits.remove("banana")
popped = fruits.pop()

# Slicing
print(fruits[0:2])    # ['apple', 'blueberry']
print(fruits[::-1])   # Reversed copy

# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]

# Sorting
numbers = [3, 1, 4, 1, 5]
numbers.sort()                        # In-place
sorted_list = sorted(numbers, reverse=True)  # New list

tuple β€” Tuples (Immutable, Ordered)

coordinates = (10, 20)
single = (42,)  # Trailing comma required for single-item tuple

# Tuple unpacking
x, y = coordinates
print(x, y)  # 10 20

# When to use tuples: fixed collections, dict keys, return values
# Named tuples for readability
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(p.x, p.y)  # 3 4

range β€” Range Objects

print(list(range(5)))       # [0, 1, 2, 3, 4]
print(list(range(2, 8)))    # [2, 3, 4, 5, 6, 7]
print(list(range(0, 10, 2))) # [0, 2, 4, 6, 8]

r = range(1_000_000)
print(999_999 in r)  # True β€” O(1) membership test, no list in memory

Mapping Type

dict β€” Dictionaries (Mutable, Key-Value Pairs)

person = {"name": "Alice", "age": 30}

person["email"] = "a@b.com"     # Add
person["age"] = 31              # Update
del person["email"]             # Delete

print(person.get("phone", "N/A"))  # Default if key missing

# Dict comprehension
squares = {x: x**2 for x in range(6)}

# Merging (3.9+)
defaults = {"color": "blue", "size": "medium"}
custom = {"color": "red"}
merged = defaults | custom  # {'color': 'red', 'size': 'medium'}

# Iteration
for key, value in person.items():
    print(f"{key}: {value}")

Set Types

set and frozenset

colors = {"red", "green", "blue"}  # Duplicates removed automatically
empty_set = set()  # Not {} β€” that creates an empty dict!

colors.add("yellow")
colors.discard("red")  # No error if missing

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)   # Union:        {1, 2, 3, 4, 5, 6}
print(a & b)   # Intersection: {3, 4}
print(a - b)   # Difference:   {1, 2}
print(a ^ b)   # Sym diff:     {1, 2, 5, 6}

fs = frozenset([1, 2, 3])  # Immutable set β€” can be dict key or set element

Binary Types

bytes, bytearray, memoryview

# bytes β€” immutable
b = b"Hello"
print(b[0])  # 72 (ASCII for 'H')

# bytearray β€” mutable
ba = bytearray(b"Hello")
ba[0] = 74  # Change to 'J'
print(ba)    # b'Jello'

# Encoding/decoding
text = "Hello, δΈ–η•Œ"
encoded = text.encode("utf-8")
decoded = encoded.decode("utf-8")
print(decoded)  # Hello, δΈ–η•Œ

# memoryview β€” zero-copy access to binary data
data = bytearray(b"Hello, World!")
mv = memoryview(data)
mv[0] = 74
print(data)  # bytearray(b'Jello, World!')

Type Checking and Conversion

Checking Types

x = 42
print(type(x))            # <class 'int'>
print(isinstance(x, int)) # True β€” preferred for most cases

# isinstance() recognizes subclasses; type() does not
class MyInt(int): pass
num = MyInt(42)
print(type(num) == int)      # False
print(isinstance(num, int))   # True

Type Conversion Rules

FunctionExampleResultNotes
int("42")"42" β†’ 42intint(3.9) β†’ 3 (truncates, not rounds)
float("3.14")"3.14" β†’ 3.14floatfloat(5) β†’ 5.0
str(42)42 β†’ "42"strstr(True) β†’ "True"
bool("")"" β†’ FalseboolSee truthy/falsy section
list("abc")"abc" β†’ ["a","b","c"]list
set([1,1,2])[1,1,2] β†’ {1, 2}setDuplicates removed

Common Gotchas

print(bool("False"))     # True β€” non-empty string!
print(bool("0"))         # True β€” non-empty string!
print(int("3.14"))       # ValueError β€” use int(float("3.14")) β†’ 3

# Safe conversion pattern
def safe_int(value, default=0):
    try:
        return int(value)
    except (ValueError, TypeError):
        return default

print(safe_int("hello"))  # 0
print(safe_int("42"))     # 42

Dynamic Typing Explained

Variables don't have fixed types β€” the objects they reference do. Reassignment changes which object the name refers to.

x = 42
x = "Hello"   # Now refers to a string
x = [1, 2, 3] # Now refers to a list

# is vs ==
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)  # True β€” same value
print(a is b)  # False β€” different objects
print(a is c)  # True β€” same object

# Small integer caching: Python caches -5 to 256
x = 256; y = 256
print(x is y)  # True β€” same cached object
x = 257; y = 257
print(x is y)  # False (usually) β€” different objects

Variable Naming Rules and Best Practices

ElementConventionExample
Variablessnake_caseuser_name, total_count
Functionssnake_caseget_user(), calculate_total()
ClassesPascalCaseUserProfile, DataProcessor
ConstantsUPPER_SNAKE_CASEMAX_RETRIES, PI
PrivateLeading underscore_internal_var
# Valid names
_name = "valid"
user_name = "valid"
_count2 = "valid"

# Invalid names
# 2name = "invalid"    # Starts with number
# my-var = "invalid"   # Hyphen
# class = "invalid"    # Reserved keyword

import keyword
print(keyword.kwlist)  # All reserved keywords

Memory and Variables

id() β€” Object Identity

a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(id(a) == id(b))  # False β€” different objects
print(a is b)           # False
print(a is c)           # True β€” same object

# Mutability affects references
c.append(4)
print(a)  # [1, 2, 3, 4] β€” both a and c refer to the same list

Reference Counting

import sys
a = [1, 2, 3]
print(sys.getrefcount(a))  # 2 (variable + function call argument)

b = a
print(sys.getrefcount(a))  # 3

del b
print(sys.getrefcount(a))  # 2

Common Mistakes

1. Mutable Default Arguments

# ❌ WRONG β€” default list shared across calls
def append_to(item, target=[]):
    target.append(item)
    return target

print(append_to(1))  # [1]
print(append_to(2))  # [1, 2] β€” unexpected!

# βœ… CORRECT
def append_to_fixed(item, target=None):
    if target is None:
        target = []
    target.append(item)
    return target

2. Floating-Point Comparison

# ❌ WRONG
if 0.1 + 0.2 == 0.3: print("Equal")

# βœ… CORRECT
import math
if math.isclose(0.1 + 0.2, 0.3): print("Equal")

3. Modifying a List While Iterating

# ❌ WRONG β€” skips elements
numbers = [1, 2, 3, 4, 5]
for n in numbers:
    if n % 2 == 0:
        numbers.remove(n)

# βœ… CORRECT β€” list comprehension
numbers = [n for n in numbers if n % 2 != 0]

4. Confusing = with ==

x = 5       # Assignment
if x == 5:  # Comparison β€” two equals signs
    print("x is 5")

Practice Exercises

Exercise 1: Type Explorer

Write a function that accepts a string and converts it to the most appropriate type.

Exercise: Create identify_and_convert(value) that returns True/False for "True"/"False", an int for integer strings, a float for float strings, and the original string otherwise.

See Solution

def identify_and_convert(value):
    if value.lower() == "true":
        return True
    if value.lower() == "false":
        return False
    try:
        return int(value)
    except ValueError:
        pass
    try:
        return float(value)
    except ValueError:
        pass
    return value

print(identify_and_convert("True"))   # True
print(identify_and_convert("42"))     # 42
print(identify_and_convert("3.14"))   # 3.14
print(identify_and_convert("hello"))  # "hello"

Exercise 2: Variable Swap and Conversion

Exercise: Given a = 10, b = "20", c = 30.0: swap a and c, convert b to int, build a list of all three, and print each element's type.

See Solution

a = 10
b = "20"
c = 30.0

a, c = c, a          # Swap
b = int(b)            # Convert
my_list = [a, b, c]

for i, item in enumerate(my_list):
    print(f"Element {i}: value={item}, type={type(item).__name__}")
# Element 0: value=30.0, type=float
# Element 1: value=20, type=int
# Element 2: value=10, type=int

Exercise 3: Conversion Chain

Exercise: Write conversion_chain(value) that converts any value to string, list, bool, and numeric (if possible), printing each step.

See Solution

def conversion_chain(value):
    print(f"Original: {value!r} ({type(value).__name__})")
    print(f"  String: {str(value)!r} (len {len(str(value))})")
    print(f"  List: {list(str(value))}")
    print(f"  Bool: {bool(value)}")
    try:
        print(f"  Int: {int(value)}")
    except (ValueError, TypeError):
        try:
            print(f"  Float: {float(value)}")
        except (ValueError, TypeError):
            print("  Numeric: not possible")
    print()

conversion_chain(42)
conversion_chain("Hello")
conversion_chain(None)

Key Takeaways

  • Python variables are names, not containers. They reference objects in memory. The object has a type; the variable does not.
  • Dynamic typing makes Python flexible but requires discipline. Always ensure variables hold the expected type in larger programs.
  • Use isinstance() over type() for type checking when you want to support inheritance hierarchies.
  • Be mindful of mutability. Lists, dicts, and sets are shared by reference. Use .copy() or slicing for independent copies.
  • Prefer math.isclose() for floating-point comparison instead of ==. IEEE 754 arithmetic introduces precision errors.
  • Follow PEP 8 naming conventions. snake_case for variables/functions, PascalCase for classes, UPPER_SNAKE_CASE for constants.

Advertisement

Need Expert Python Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement