Skip to main content

Python Development Crash Guide 2026 — Part 2: Core Python: Syntax, Control Flow, Functions & Data Structures

Python Development Crash Guide 2026 — Part 2: Core Python: Syntax, Control Flow, Functions & Data Structures

If Part 1 was about understanding what Python is, Part 2 is where you start thinking in Python. This is the part that separates people who've watched Python tutorials from people who can actually write Python code on their own.

I use everything in this post daily — whether I'm writing automation scripts, building FastAPI endpoints, or teaching Python in my YouTube tutorials. Control flow, functions, and data structures aren't "beginner topics" you outgrow — they're the foundation you build everything else on.


Boolean Logic and Decision Making

Before you write a single if statement, you need to understand how Python evaluates truth. This trips up even experienced developers from other languages.

Python doesn't just check for True or False — it checks for truthiness. These values are all considered false: False, None, 0, "", [], {}, and set(). Everything else is true.

This means you can write:

# Instead of this:
if len(my_list) > 0:
    process(my_list)

# Write this — more Pythonic:
if my_list:
    process(my_list)

This pattern is everywhere in production Python. When I'm validating API request data, I write if not request.name: rather than checking length or comparing to empty string. It's shorter, more readable, and handles None for free.

if / elif / else

Conditions are checked top to bottom, and the first match wins — remaining branches are skipped entirely:

score = 85

if score >= 90:
    grade = "A"
elif score >= 75:
    grade = "B"
else:
    grade = "C"

A common beginner mistake: using multiple if statements when you mean elif. With separate if blocks, every condition is checked. With elif, Python stops at the first match.

Ternary expressions

status = "Adult" if age >= 18 else "Minor"

Clean for simple decisions. But never nest them — if the logic needs nesting, use a regular if/elif/else block. Readability always wins over cleverness.


Loops — They're More Powerful Than You Think

If you're coming from Java or C, Python loops will feel different. Python's for loop doesn't iterate over indices by default — it iterates over objects.

for loops are iterator-based

for item in [1, 2, 3]:
    print(item)

What's actually happening: Python calls __iter__() on the list to get an iterator, then repeatedly calls __next__() until StopIteration is raised. This is why you can loop over files, generators, database cursors, and any custom object — as long as it implements the iterator protocol. Coming from Java, where I'd write a for(int i=0; i<list.size(); i++), Python's approach felt strange at first. Now I find Java's index-based loops clunky.

while loops — for when you don't know how many iterations

count = 0
while count < 5:
    count += 1

Use while for retry logic, polling, or waiting for a condition. In my automation scripts, I use while loops for things like "keep checking this API until the status changes or we hit a timeout."

break, continue, and loop else

break exits the loop immediately. continue skips to the next iteration. Both straightforward. But Python has a feature most languages don't — else on a loop:

for user in users:
    if user.is_admin:
        print("Found admin")
        break
else:
    print("No admin found")

The else block runs only if the loop completed without hitting break. This comes up in interviews surprisingly often — most candidates don't know it exists.


Functions — Where Code Becomes Reusable

Functions aren't just about avoiding repetition. They're about making your code testable, readable, and maintainable. Every real application — every API endpoint I've built, every automation script I've written — is structured around functions.

The basics

def add(a, b):
    return a + b

Python functions can return multiple values (as tuples), return other functions, or return nothing (None implicitly). This flexibility is one of the reasons Python feels expressive.

Parameter types you need to know

Positional: add(2, 3) — order matters.

Keyword: add(a=2, b=3) — order doesn't matter, more readable for functions with many parameters.

Default values:

def greet(name="Guest"):
    return f"Hello {name}"

*args and **kwargs — for variable-length arguments:

def total(*numbers):      # accepts any number of positional args
    return sum(numbers)

def info(**data):         # accepts any number of keyword args
    return data

You'll see *args and **kwargs everywhere in frameworks — Django views, FastAPI dependencies, decorator implementations. Understanding them isn't optional for real Python work.

The call stack — why it matters for debugging

When functions call other functions, Python maintains a call stack. Each call creates a new stack frame with its own local variables. When a function returns, its frame is popped. Understanding this helps you read tracebacks and debug recursion issues.

Lambda functions

square = lambda x: x * x
sorted_users = sorted(users, key=lambda u: u.age)

Good for short callbacks. Bad for anything complex — if your lambda is hard to read, extract it into a named function.


Core Data Structures — Choose the Right One, or Pay the Price

Choosing the wrong data structure is one of the most expensive mistakes in Python. Here's how each one works and when to use it.

Lists — dynamic arrays

nums = [1, 2, 3]

Ordered, mutable, allows duplicates. Index access is O(1), append is O(1) amortised, but inserting or deleting from the middle is O(n). One thing that trips people up: list slicing creates a new list. nums[1:3] doesn't give you a view — it's a copy.

Tuples — when the data shouldn't change

point = (10, 20)
rgb = (255, 128, 0)

Faster than lists, less memory, and can be used as dictionary keys. Use tuples for coordinates, database rows, configuration values — anything that represents a fixed record.

Sets — fast membership testing

unique_ids = {1, 2, 3}

# Set membership check: O(1)
if user_id in unique_ids:
    process(user_id)

# List membership check: O(n) — avoid for large data
if user_id in user_list:
    process(user_id)

This is a performance trap I've seen in real codebases: checking if x in large_list inside a loop. Converting the list to a set first turns O(n²) into O(n). I've fixed this exact issue in production scripts that went from taking 45 minutes to 3 seconds.

Dictionaries — the most important data structure in Python

user = {
    "name": "Prashant",
    "role": "backend",
    "years": 7
}

Key-value mapping with O(1) average lookup. Dictionaries power JSON, API responses, configuration files, and half of Python's internal machinery. Always use .get() for safe access:

# This crashes if "email" doesn't exist:
email = user["email"]  # KeyError!

# This returns a safe default:
email = user.get("email", "not provided")

I use .get() almost exclusively in production code. KeyError crashes in production are embarrassing and entirely preventable.


Mutability — The Concept That Causes the Most Bugs

If you take one thing from this post, let it be this: understand which types are mutable and which are immutable.

Mutable (can be changed in place): list, dict, set
Immutable (cannot be changed): int, float, bool, str, tuple

Why this matters:

def add_item(lst):
    lst.append(1)

my_list = []
add_item(my_list)
print(my_list)  # [1] — the original list changed!

Because lists are mutable, passing them to a function gives the function a reference to the same object — not a copy. This is Python's most common source of "I didn't change that, why did it change?" bugs. I covered this in detail in my Python interview questions post.


Comprehensions — Python's Most Elegant Feature

Comprehensions let you build lists, dicts, and sets in a single readable expression:

# List comprehension
squares = [x * x for x in range(5)]

# Dictionary comprehension
square_map = {x: x * x for x in range(5)}

# Set comprehension
remainders = {x % 3 for x in range(10)}

# With filtering
even_squares = [x * x for x in range(10) if x % 2 == 0]

Comprehensions are faster than equivalent for-loops because Python optimises them internally. But don't overdo it — if it doesn't fit on one line comfortably, use a regular loop.


Mistakes I See Beginners Make Repeatedly

These aren't hypothetical — I see them in code reviews, in mock interview sessions, and in my own early Python code:

Mutable default arguments: Writing def f(items=[]) instead of def f(items=None). The default list is shared across all calls.

Using is when you mean ==: is checks identity, == checks value. They're not the same thing.

Modifying a list while iterating: This either skips elements or crashes. Use a comprehension to build a new list instead.

Overusing global variables: Pass data through function parameters. Globals are hard to test and will cause bugs in concurrent code.

Deep nesting: If your code is five levels of indentation deep, extract inner logic into functions.


Where All of This Shows Up in Real Work

Everything in this post is the foundation of everything I build. FastAPI endpoints use functions, type-checked parameters, dictionary operations on JSON payloads, and list comprehensions for transforming data. Automation scripts use loops with break conditions, set operations for deduplication, and proper function structure for testability.

If these concepts feel solid, frameworks like FastAPI, Django, and pandas will make sense immediately. If they feel shaky, those frameworks will feel like magic — and magic is hard to debug.

Part 3 covers advanced Python: OOP, decorators, generators, and the memory model — the concepts that separate script writers from engineers.


Series Navigation


About the author: I'm Prashant Sharma — a backend developer and Tech Lead working with Java and Spring Boot in production. I teach Python and Java on YouTube and run 1:1 sessions on Topmate.

Comments

  1. This part of the Python crash guide does a good job of focusing on the transition from understanding basic concepts to actually writing Python with confidence. Syntax, control flow, functions, and data structures form the foundation for many practical areas of Python development, so developing a strong understanding of these topics is essential.

    A structured approach can make these fundamentals much easier to practice and apply. A Python Programming Course can help learners work through core syntax, decision-making, functions, and data structures while gradually becoming more comfortable writing their own programs.

    ReplyDelete
  2. Consistent practice is especially important when learning how to think in Python rather than simply memorizing syntax. Python Programming Training can reinforce these concepts through repeated exercises and practical coding tasks, helping learners develop better problem-solving habits.

    ReplyDelete
  3. Once the fundamentals are understood, applying them through real projects is the next useful step. Python Projects For Final Year can provide practical ideas for using functions, control flow, and data structures together while building applications. This progression from fundamentals to hands-on implementation is a strong way to prepare for backend development, automation, or data-focused Python work.

    ReplyDelete

Post a Comment