Learn Python 3: Python basics in 7 coding challenges

Learn Python 3: Python basics in 7 coding challenges

13 mins read
Oct 31, 2025
Share
editor-page-cover
Content
Why challenge-based learning?
1. Hello World
2. Variables
Modern Python Features and Syntax Sugar
F-Strings
Unpacking and Swapping
enumerate() and zip()
Walrus Operator (:=)
Pattern Matching (match / case)
3. List
Keep building your Python programming experience.
4. Slicing
5. Branching
Type Hinting and Gradual Typing
Basic Function Annotations
Using Built-In Types
Optional and Union Types
Benefits of Type Hinting
6. For loop
Error Handling, Assertions, and Input Validation
Using try / except
Raising Exceptions Intentionally
Assertions
Validating Input
Comprehensions, Generators, and Lazy Iteration
List Comprehensions
Set and Dictionary Comprehensions
Generator Expressions
The yield Keyword
7. Functions
Mutability, References, and Common Pitfalls
Shallow vs Deep Copy
Mutable Default Arguments
Aliasing
Project Structure, Modules, and Recursion
Modules and Packages
The if __name__ == "__main__": Guard
Recursion Basics
Testing, Debugging, and Style Guidelines
Writing Tests
Doctests
Following PEP 8
Code Formatting and Linting
Debugging
What to learn next
Continue reading about Python Projects

Python 3 is the latest version of Python, a popular programming language used mainly for web development, data science, and machine learning. Python has quickly become one of the most popular programming languages for new developers because of the wide range of uses in software development and some of the great improvements added since Python 2.

Today, we’ll walk you through a hands-on, challenge-based tutorial to get you started with Python quickly!


Learn Python 3 in half the time with hands-on practice

Our new 50 challenge Python course lets you skip the book work and get right to hands-on learning, with challenges ranging from beginner to expert difficulties.

An Intro to Python 3


Why challenge-based learning?#

Not everyone learns new programming languages the same way. For many aspiring Python programmers, book learning is inefficient and doesn’t lead to lasting learning. The main flaws of book learning are that it doesn’t invite the reader to directly engage and it encourages memorization instead of the natural exploration of programming concepts.

To avoid the downsides of book learning, many would rather learn to code by exploring Python code through hands-on, challenge-based crash courses. This method is great for hands-on learners because it allows absolute beginners to learn both the theory and pragmatic use case for every basic concept or algorithm.

The challenges encourage you to explore the problems naturally and learn to use Python syntax through investigation rather than lecturing.


1. Hello World#

Let’s start with a basic puzzle that uses the print() function.

print('hello world')

The print() function allows you to connect your program to the outside world. This function, as the name indicates, prints a value to the standard output.

You can think of the standard output as the environment in which your Python program lives. Your standard output is the air around you. Let’s say you shout “Ouch!” Every person in your environment can read from your standard output that you just experienced pain. The data that is printed to the standard output is of the string data type.

A string is a sequence of characters.

You can define a string in Python in any of the following ways:

  • Single quotes ('hello world')
  • Double quotes ("hello world")
  • Triple quote ('''hello world''' and """hello world""")

In the puzzle, we use the single quote to define our string.

Challenge 1: Hello World

1.

What is the output of the above code?

A.

hello world

B.

'hello world'

C.

Hello World


1 / 1

2. Variables#

Now we’ll introduce the concepts of variables and float division.

x = 55 / 11
print(x)

The puzzle has two goals. First, it introduces the concept of variables. Python evaluates the result of the expression on the right side of the equation and stores it in the variable x. After defining the variable, you can access it at any point in the program code.

Second, it forces you to read code carefully due to a twist: Division operations always return a floating-point number. Thus, variable x stores the float value 5.0. The print function outputs the result as a float and not as an integer value 5.

This is the source of most errors in the code. People focus too much on what they mean (semantics) and too little on how they say it (syntax).

Tip: Programs always execute instructions according to syntax, which may be different from what you intended. The key is to get used to thinking like a computer.

Challenge 2: Variables

1.

What is the output of the above code?

A.

5

B.

05

C.

5.0


1 / 1

Modern Python Features and Syntax Sugar#

Modern Python introduces features that make code cleaner, more expressive, and easier to read.
Learning these early helps developers write idiomatic, professional-grade Python from the start.

F-Strings#

F-strings are the preferred way to format strings — they’re faster, more readable, and more concise than + concatenation or .format().

name = "Alice"
print(f"Hello, {name}!")

Unpacking and Swapping#

Python supports simultaneous assignment and value unpacking, reducing boilerplate and improving readability.

a, b = 10, 20
a, b = b, a # swap values
x, y, *rest = [1, 2, 3, 4, 5]

This syntax is powerful for tuple unpacking, returning multiple values, and working with iterable data.

enumerate() and zip()#

These built-in functions make iteration more Pythonic and expressive.

for i, val in enumerate(["a", "b", "c"]):
print(i, val)
for name, score in zip(names, scores):
print(name, score)
  • enumerate() gives you both the index and value while looping.

  • zip() lets you iterate over multiple sequences together.

Walrus Operator (:=)#

Introduced in Python 3.8, the walrus operator allows inline assignment inside expressions — reducing repetition and improving readability.

if (n := len(items)) > 5:
print(f"List is too long ({n} elements)")

It’s especially handy in loops and conditionals where you need to compute and reuse a value immediately.

Pattern Matching (match / case)#

Added in Python 3.10, pattern matching simplifies complex conditional logic and improves clarity.

match status_code:
case 200:
print("OK")
case 404:
print("Not Found")
case _:
print("Unknown status")

This feature provides a cleaner alternative to long if-elif chains, especially when handling structured data or enums.

By embracing these modern features, Python developers can write shorter, clearer, and more maintainable code that aligns with today’s best practices.


3. List#

This puzzle introduces lists, our first data structure in Python.

squares = [1, 4, 9, 16, 25]
print(squares[0])

The data type is “abstract” because you can use lists independently of the list elements’ concrete data types. Most complex algorithms that you’ll learn later will use lists as a building block. Many famous algorithms such as quicksort are based only on a single list as their core data structure.

The pythonic way of handling lists and list access is simple and clean. You can create a list by writing comma-separated values between the opening and closing square brackets.

lst = [1, 4, 9, 16, 25] 

You access the i-th element in a list lst with the intuitive bracket notation lst[i]. This notation is consistent for all compound data types, such as strings and arrays.

Challenge 3: Lists

1.

What is the output of the above code?

A.

1

B.

4

C.

9

D.

16

E.

25


1 / 1

Keep building your Python programming experience.#

Learn Python concepts from data structures to decorators, all with step-by-step challenges. Educative’s hands-on courses let you learn the languages and tools you need quickly, with real-world examples and in-browser code environments.

An Intro to Python 3


4. Slicing#

Now, we’ll explore how to use slicing and some more advanced variable manipulation.

word = "galaxy"
print(len(word[1:]))

Slicing

Slicing is a Python-specific concept for accessing a range of values in sequence types, such as lists or strings. It is one of the most popular Python features. Understanding slicing is one of the key requirements for understanding most existing Python codebases.

The idea behind slicing is simple. Use the bracket notation to access a sequence of elements instead of only a single element. You do this via the colon notation of [start: end]. This notation defines the start index (included) and the end index (excluded).

Tip: A very common source of bugs is forgetting that the end index is always excluded in sequence operators.

For the sake of completeness, quickly look into the advanced slicing notation [start:end:step]. The only difference to the previous notation is that it allows you to specify the step size. For example, the command 'python'[:5:2] returns every second character up to the fourth character, i.e., the string pto.

len()

The len() function is a handy tool to get the length of built-in Python data types, such as strings, lists, dictionaries, or tuples.

Challenge 4: Slicing

1.

What is the output of the above code?

A.

galaxy

B.

alaxy

C.

5

D.

6


1 / 1

5. Branching#

Now we’re moving into some harder challenges. In this challenge, we’ll introduce branching and conditional statements.

def if_confusion(
x, y):
if x > y:
if x - 5 > 0:
x=y
return "A" if y == y + y else "B"
elif x + y > 0:
while x > y: x -= 1
while y > x: y -= 1
if x == y:
return "E"
else:
if x - 2 > y - 4:
x_old = x
x=y*y
y = 2 * x_old
if (x - 4) ** 2 > (y - 7) ** 2:
return "C"
return "D"
return "F"
print(if_confusion(3, 7))

Just like any other programming language, Python also has Conditional Statements also known as Branching Statements. To create branches, you can use the keywords if, else, or elif. These statements return Booleans, true if the condition is met and false if it is not.

Tip: The computer does not execute the code strictly from top to bottom, and you shouldn’t either.

Instead, start where the program execution starts, which is at the bottom with the function call if_confusion(3, 7). Now you know that x=3 and y=7. Then, you proceed to do what the interpreter does.

As x>y is false, you can skip the whole upper part of the function. Similarly, you can skip the if branch for x-2>y-4.

Challenge 5: Branching

1.

What is the output of the above code?

A.

A

B.

B

C.

C

D.

D

E.

E

F.

F


1 / 1

Type Hinting and Gradual Typing#

Type hints make Python code more readable, maintainable, and self-documenting.
They work seamlessly with tools like MyPy, Pyright, and modern IDEs for autocomplete and static analysis — helping catch type errors before runtime.

Basic Function Annotations#

You can specify expected argument and return types directly in function definitions:

def greet(name: str) -> str:
return f"Hello, {name}"

This clearly communicates intent — name should be a string, and the function returns a string.

Using Built-In Types#

Add clarity by annotating collections such as lists, dictionaries, or tuples.

from typing import List, Dict
def process_scores(scores: List[int]) -> Dict[str, float]:
return {"average": sum(scores) / len(scores)}

This helps IDEs provide better autocomplete and makes function contracts explicit.

Optional and Union Types#

When a value might be None or one of several types, use Optional or Union.

from typing import Optional, Union
def find_item(data: dict, key: str) -> Optional[Union[str, int]]:
return data.get(key)

Here, the return value can be a str, an int, or None, depending on what data.get() returns.

Benefits of Type Hinting#

  • Improves readability and makes intent explicit.

  • Reduces runtime errors by catching mismatched types early.

  • Enhances IDE support with better autocomplete and refactoring.

  • Facilitates collaboration in larger codebases through clearer contracts.

Gradual typing means you can adopt type hints incrementally — start small and expand coverage over time for cleaner, safer Python code.


6. For loop#

Now that we’ve seen a while loop, we’ll learn about for loops.

words = ['cat', 'mouse']
for word in words:
print(len(word))

Tip: Repeated code is redundant and hard to read, debug, and maintain. As programmers, we should avoid redundant code at all costs.

The Python for loop statement is a way out of redundant code. With a for loop, you write code once and put it into different contexts.

Among the ingredients that make a programming language powerful are control flow statements. The Python for loop is one such control flow statement. It repeats the execution of the code body for all sequence elements, iterating over all elements in the order of the sequence. It’s similar to a forEach loop found in Java or JavaScript.

In the puzzle, the variable word takes first the value cat and second the value mouse. We then print out the length of each word.

Challenge 6: For Loops

1.

What is the output of the above code?

A.

cat mouse

B.

3 5

C.

cat

D.

3


1 / 1

Error Handling, Assertions, and Input Validation#

Good code anticipates and handles failures gracefully.
Teaching robust error handling prepares learners for writing reliable, production-quality Python applications.

Using try / except#

Handle predictable errors cleanly and provide fallback logic instead of crashing the program.

try:
result = int(input("Enter a number: "))
except ValueError:
print("Invalid input. Please enter an integer.")

This approach keeps programs resilient and user-friendly.

Raising Exceptions Intentionally#

Use raise to clearly signal unexpected or invalid conditions.

def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b

Intentional exceptions make debugging and error handling more explicit.

Assertions#

Assertions validate assumptions during development and testing.

assert len(data) > 0, "Data list cannot be empty"

Use them to catch logic errors early, but disable them in production for performance.

Validating Input#

Always sanitize and validate user input before processing — it prevents crashes and mitigates security risks.
Validation ensures that only expected and safe data enters your program.


Comprehensions, Generators, and Lazy Iteration#

Python’s comprehension syntax and generator tools let you write clean, expressive, and efficient code.

List Comprehensions#

Replace verbose loops with concise, readable expressions.

squares = [x**2 for x in range(10) if x % 2 == 0]

Set and Dictionary Comprehensions#

Quickly create collections from iterables.

squares_set = {x**2 for x in range(10)}
squares_dict = {x: x**2 for x in range(10)}

Generator Expressions#

Generate data lazily — without loading everything into memory.

gen = (x**2 for x in range(1_000_000))

Generators are ideal for large or streaming datasets.

The yield Keyword#

Turn functions into iterators that produce values one at a time.

def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b

Generators and yield make it easy to handle large or infinite sequences efficiently — a cornerstone of Python’s functional and memory-safe programming style.


7. Functions#

For our final challenge, we’ll take a look at a fundamental programming concept: functions.

def func(
x):
return x + 1
f = func
print(f(2) + func(2))

A function encapsulates a sequence of program instructions. The ideal function solves a single high-level goal.

For example, you can encapsulate the task of searching the web for specific keywords into a named function. This allows you to call for a search later using just a single statement.

Functions allow you to write code once and reuse it later or in other programs. Reusing code is more efficient than writing the same code each time. Suppose you want to calculate the square root of 145. You could either calculate it for the specific value 145 or define a function that calculates the square root for any value x.

Defining a function

You can define a function with the keyword def, followed by a name and the function’s arguments. The Python interpreter maintains a symbol table that stores all function definitions, i.e., mappings from function names to function objects.

This allows the interpreter to relate each occurrence of the function name to the defined function object. A single function object can have zero, one, or even many names.

In the puzzle, we assign the function object to the name func and then reassign it to the new name f. We can then use both the names to refer to the same code.

Upon the function call, the Python interpreter will find the function in the symbol table and execute it. This can make your code more readable when calling the same function in different contexts.

Challenge 7: Functions

1.

What is the output of the above code?

A.

6

B.

3

C.

3+3


1 / 1

Mutability, References, and Common Pitfalls#

Understanding how Python handles mutable and immutable objects helps prevent subtle, hard-to-detect bugs.

Shallow vs Deep Copy#

Copying in Python can behave differently depending on whether the objects are nested.

import copy
a = [[1, 2], [3, 4]]
shallow = a.copy()
deep = copy.deepcopy(a)
  • Shallow copy creates a new outer object but keeps references to the same inner elements.

  • Deep copy recursively duplicates all nested objects.

Use copy.deepcopy() when you need complete independence between objects.

Mutable Default Arguments#

Avoid using mutable objects (like lists or dicts) as default parameters — they persist across function calls.

def append_to(lst=None):
if lst is None:
lst = []
lst.append(1)
return lst

Using None as the default ensures that each call creates a fresh list.

Aliasing#

Remember that a = b makes both variables refer to the same object.
To create an independent copy, use .copy() or copy.deepcopy() when appropriate.


Project Structure, Modules, and Recursion#

As Python projects grow, modularity and structure become essential for maintainability and scalability.

Modules and Packages#

Split code into logical units using separate .py files.
Group related modules into packages (directories containing an __init__.py file).

This encourages reusability and cleaner architecture.

The if __name__ == "__main__": Guard#

Prevent code from executing on import by using this guard:

if __name__ == "__main__":
main()

This allows files to serve both as reusable modules and as standalone scripts.

Recursion Basics#

Recursion is a fundamental technique for solving problems like factorials, Fibonacci sequences, or tree traversals.

def factorial(n: int) -> int:
if n == 1:
return 1
return n * factorial(n - 1)

Always include a base case to prevent infinite recursion.


Testing, Debugging, and Style Guidelines#

Professional Python development requires a focus on testing, consistency, and readability.

Writing Tests#

Use the built-in unittest framework or third-party tools like pytest to ensure correctness.

import unittest
class TestMath(unittest.TestCase):
def test_addition(self):
self.assertEqual(2 + 2, 4)

Doctests#

Embed simple tests directly in docstrings for small, self-contained functions.

def add(a, b):
"""Return the sum.
>>> add(2, 3)
5
"""
return a + b

Run them using python -m doctest -v filename.py.

Following PEP 8#

PEP 8 defines the official Python style guide — consistent naming, indentation, and spacing improve readability.

Code Formatting and Linting#

Automate style enforcement with tools like:

  • black – automatic code formatter

  • flake8 – linter for style and logical issues

  • isort – organizes imports

Debugging#

Use built-in debuggers like pdb or IDE debugging tools to inspect variables, trace execution, and step through code:

import pdb; pdb.set_trace()

Adopting testing, style checks, and debugging habits early helps students transition smoothly from classroom code to production-grade development.


What to learn next#

Now that you’ve completed these 7 challenges, you have programming experience with the major building blocks of most Python 3 programs. Transitioning to a new language can be tough, but know that you’re making a great investment in your education. Python is the best general-purpose language in use today and there has never been a better time to start learning Python!

As you continue your journey to become a python programmer, look for Python projects on concepts like:

To help you get to that point, Educative has launched our new Python course An Intro to Python 3. This course features 50 challenges that provide hands-on experience with all the skills you’ll need to jump into building Python projects. The course’s challenge-based learning approach is perfect for developers looking to transition to Python because it draws on your existing coding intuition to learn in half the time.

Happy learning!


Continue reading about Python Projects#


Written By:
Ryan Thelin