Home / Python / Introduction

Python Tutorial

Python is a high-level, interpreted, general-purpose programming language. This tutorial covers everything from basics to advanced internals — with every concept explained, examples to run, and quizzes to test yourself.

What is Python?

Python was created by Guido van Rossum and released in 1991. It emphasises readability and simplicity — Python code reads almost like English.

Why is Python Widely Used?

  • Readable syntax – reduces the learning curve dramatically.
  • Versatile – web dev (Django/FastAPI), data science (NumPy/Pandas), AI/ML (TensorFlow), scripting, automation.
  • Huge ecosystem – PyPI hosts over 450,000 packages.
  • Interpreted – no compilation step; great for rapid prototyping.
  • Cross-platform – runs on Windows, Linux, macOS.
  • Strong community – extensive documentation and support.

Key Features of Python

  • Interpreted – executed line by line at runtime.
  • Dynamically typed – no need to declare variable types.
  • Object-oriented – full OOP support with classes and inheritance.
  • Functional programming – lambda, map, filter, reduce.
  • Automatic memory management – garbage collection via reference counting.
  • "Batteries included" – vast standard library.
  • Indentation-based syntax – uses whitespace instead of braces.
💡
Tip: Python's philosophy is summarised in The Zen of Python. Run import this in your Python shell to read it.

Your First Python Program

Every Python journey starts with a simple print statement. Try it in the compiler below:

Python
print("Hello from Skillfort!")

name = "Alice"
age  = 25
print(f"My name is {name} and I'm {age} years old.")
Try It Yourself
Python
Code Editor
Output
Click ▶ Run to execute your code…
Quick Quiz

Which of the following best describes Python?


Python Data Types

Every value in Python has a type. Understanding Python's built-in data types is essential for writing correct programs.

Built-in Data Types

CategoryTypesExample
Numericint, float, complex42, 3.14, 2+3j
Sequencestr, list, tuple, range"hello", [1,2], (1,2)
Setset, frozenset{1,2,3}
Mappingdict{"a":1}
BooleanboolTrue, False
NoneNoneTypeNone
Python – Data Types
# Check the type of any value
x = 42
y = 3.14
z = "hello"
lst = [1, 2, 3]
tup = (1, 2)
d   = {"name": "Alice"}

for val in [x, y, z, lst, tup, d]:
    print(f{val!r:20} → {type(val).__name__}")
Try It Yourself
Python
Code Editor
Output
Click ▶ Run to execute…

Mutable vs Immutable

One of the most important concepts in Python — understanding which objects can change and which cannot.

What is Mutability?

Mutable objects can be changed after creation. Immutable objects cannot — operations on them create new objects.

  • Mutable: list, dict, set, bytearray
  • Immutable: int, float, str, tuple, frozenset, bytes
⚠️
Common Bug: Never use a mutable default argument like def fn(lst=[]). The list is shared across all calls! Use def fn(lst=None) and assign inside.
Python
# Mutable — changed in place
my_list = [1, 2, 3]
my_list[0] = 99
print("List after change:", my_list)

# Immutable — creates new object
my_str = "hello"
# my_str[0] = "H"  # TypeError!
my_str = "Hello"   # new object
print("String:", my_str)

# id() shows memory address
a = 10
print("Before:", id(a))
a += 1
print("After:", id(a), "(new object!)")
Try It Yourself
Python
Code Editor
Output
Click ▶ Run…
Quick Quiz

Which of the following is immutable in Python?


Python Functions

Functions are the building blocks of reusable, organised Python code. Learn about regular functions, lambdas, *args/**kwargs, and recursion.

Defining and Using Functions

A function is a named, reusable block of code defined with def. Functions support type hints (Python 3.5+), default arguments, and docstrings.

Python
def greet(name: str, greeting: str = "Hello") -> str:
    """Return a personalised greeting."""
    return f"{greeting}, {name}!"

print(greet("Alice"))
print(greet("Bob", greeting="Hi"))

*args and **kwargs

*args collects extra positional arguments into a tuple. **kwargs collects extra keyword arguments into a dict.

Python
def demo(*args, **kwargs):
    print("args:",   args)
    print("kwargs:", kwargs)

demo(1, 2, 3, name="Alice", role="admin")

# Lambda: anonymous single-expression function
square = lambda x: x ** 2
print("3² =", square(3))

people = [("Alice",30),("Bob",25),("Carol",28)]
people.sort(key=lambda p: p[1])
print("Sorted by age:", people)
Try It Yourself – Functions
Python
Code Editor
Output
Click ▶ Run…

Generators & Iterators

Generators are one of Python's most powerful features — they produce values lazily, making them ideal for large datasets and infinite sequences.

What is an Iterator?

An iterator implements __iter__() and __next__(). Calling next() on it returns the next value and raises StopIteration when exhausted.

What is a Generator?

A generator is a function that uses yield instead of return. It automatically becomes an iterator — pausing at each yield and resuming on the next next() call.

💡
Memory efficient: A generator doesn't store all values in memory at once. It computes each value on demand. Ideal for large files, infinite sequences, and data pipelines.
Python
# Generator function
def fibonacci(limit):
    a, b = 0, 1
    while a < limit:
        yield a          # pause here, return a
        a, b = b, a + b

print("Fibonacci under 100:")
print(list(fibonacci(100)))

# Generator expression (like list comprehension but lazy)
gen = (x**2 for x in range(10))
print("Squares:", list(gen))

# Memory comparison
import sys
lst = [x for x in range(10000)]
gen = (x for x in range(10000))
print(f"List size: {sys.getsizeof(lst)} bytes")
print(f"Gen size:  {sys.getsizeof(gen)} bytes")
Try It Yourself – Generators
Python
Code Editor
Output
Click ▶ Run…
Quick Quiz

What keyword turns a regular function into a generator?


OOP in Python

Object-Oriented Programming in Python — encapsulation, inheritance, polymorphism, and abstraction with real-world examples.

The Four Pillars of OOP

  • Encapsulation – bundle data + methods; hide internal state with private attributes (_name, __name).
  • Inheritance – child class inherits from parent. Dog(Animal) inherits breathe() and adds bark().
  • Polymorphism – same interface, different behaviour. dog.speak() → "Woof", cat.speak() → "Meow".
  • Abstraction – hide implementation via abstract base classes (ABC).
Python – OOP
from abc import ABC, abstractmethod

class Animal(ABC):
    def __init__(self, name):
        self.name = name      # encapsulation

    @abstractmethod
    def speak(self): ...   # abstraction

    def __str__(self):
        return f"{self.__class__.__name__}({self.name!r})"

class Dog(Animal):           # inheritance
    def speak(self): return "Woof!"

class Cat(Animal):
    def speak(self): return "Meow!"

animals = [Dog("Rex"), Cat("Luna"), Dog("Buddy")]
for a in animals:
    print(f"{a} says: {a.speak()}")  # polymorphism
Try It Yourself – OOP
Python
Code Editor
Output
Click ▶ Run…

Decorators & Closures

Decorators let you extend function behaviour without modifying the function. Closures are the mechanism that makes them work.

What is a Closure?

A closure is an inner function that remembers variables from its enclosing scope even after the outer function has finished.

What is a Decorator?

A decorator is a function that wraps another function to add behaviour. The @decorator syntax is syntactic sugar for fn = decorator(fn).

Python
import time
import functools

# Closure
def make_multiplier(factor):
    def multiply(x):
        return x * factor   # 'factor' is closed over
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)
print("double(5):", double(5))
print("triple(5):", triple(5))

# Decorator
def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start  = time.perf_counter()
        result = func(*args, **kwargs)
        end    = time.perf_counter()
        print(f"{func.__name__} took {(end-start)*1000:.3f}ms")
        return result
    return wrapper

@timer
def sum_to_n(n):
    return sum(range(n))

result = sum_to_n(1_000_000)
print("Sum:", result)
Try It Yourself – Decorators
Python
Code Editor
Output
Click ▶ Run…

Exception Handling

Python uses try/except blocks to handle errors gracefully. Learn how to catch, raise, log, and create custom exceptions.

try / except / else / finally

  • try – the block to attempt.
  • except – runs if an exception is raised.
  • else – runs only if NO exception occurred.
  • finally – always runs (cleanup code).
Python
# Custom exception
class InsufficientFundsError(Exception):
    def __init__(self, balance, amount):
        super().__init__(f"Need {amount}, have {balance}")
        self.balance = balance
        self.amount  = amount

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(balance, amount)
    return balance - amount

try:
    result = withdraw(100, 200)
except InsufficientFundsError as e:
    print("Error:", e)
except (TypeError, ValueError) as e:
    print("Type/Value error:", e)
else:
    print("Withdrawn! Balance:", result)
finally:
    print("Transaction complete.")
Try It Yourself – Exceptions
Python
Code Editor
Output
Click ▶ Run…

Python Coding Labs

14 hands-on challenges. Read the problem, edit the starter code, and run it to verify your solution.

Lab 1 – Palindrome Checker

Write a function that checks whether a given number or string is a palindrome.

Lab 1 – Palindrome
Python
Code Editor
Output
Click ▶ Run…

Lab 2 – Fibonacci Generator

Lab 2 – Fibonacci
Python
Code Editor
Output
Click ▶ Run…

Lab 3 – Anagram Checker

Lab 3 – Anagram
Python
Code Editor
Output
Click ▶ Run…

Lab 4 – Move Zeros to End

Lab 4 – Move Zeros
Python
Code Editor
Output
Click ▶ Run…

Lab 5 – OOP: Bank Account

Lab 5 – OOP Bank Account
Python
Code Editor
Output
Click ▶ Run…