Kotlin Tutorial #24: Build a REST API with Ktor

In the previous tutorial, you built a CLI tool. Now let’s build a REST API. Ktor is Kotlin’s official framework for building asynchronous servers and clients. It is lightweight, flexible, and uses Kotlin coroutines. In this tutorial, you will learn: Setting up Ktor Routing (GET, POST, PUT, DELETE) JSON serialization with Content Negotiation In-memory storage Path parameters and query parameters Error handling with StatusPages Testing with ktor-server-test-host What We’re Building A Notes API with CRUD operations: ...

March 22, 2026 · 9 min

Kotlin Tutorial #23: Build a CLI Tool with Kotlin

In the previous tutorial, you learned about Kotlin Serialization. Now let’s build something real. In this tutorial, you will build a complete CLI (Command-Line Interface) tool: a text analyzer that counts words, finds patterns, and shows statistics. In this tutorial, you will learn: Reading command-line arguments File I/O (reading files) Text analysis (word count, frequency) Colored terminal output with ANSI codes Argument parsing Error handling Structuring a real project What We’re Building A text analysis tool called TextAnalyzer with these commands: ...

March 22, 2026 · 8 min

Python Cheat Sheet 2026 — Syntax, Data Structures, and Common Patterns

Bookmark this page. Use Ctrl+F (or Cmd+F on Mac) to find what you need. This cheat sheet covers Python syntax from basics to advanced patterns. Try examples at pythontutor.com. Last updated: March 2026 Variables and Types name = "Alex" # str age = 25 # int height = 1.75 # float is_active = True # bool nothing = None # NoneType Type Example Notes int 42 No size limit float 3.14 64-bit decimal str "hello" Immutable bool True, False Capitalized None None Null equivalent list [1, 2, 3] Mutable, ordered tuple (1, 2, 3) Immutable, ordered dict {"a": 1} Key-value pairs set {1, 2, 3} Unique values, unordered Type Conversions int("42") # 42 float("3.14") # 3.14 str(42) # "42" list((1, 2, 3)) # [1, 2, 3] tuple([1, 2, 3]) # (1, 2, 3) set([1, 1, 2]) # {1, 2} bool(0) # False bool("") # False bool([]) # False bool("hello") # True Strings name = "Alex" f"Hello {name}" # f-string: Hello Alex f"Age: {age + 1}" # f-string with expression f"{price:.2f}" # format: 9.99 f"{name!r}" # repr: 'Alex' f"{num:,}" # thousands separator: 1,000,000 f"{value:>10}" # right-align, width 10 f"{value:<10}" # left-align, width 10 # Common methods "hello".upper() # "HELLO" "HELLO".lower() # "hello" "hello world".title() # "Hello World" " hello ".strip() # "hello" "hello world".split() # ["hello", "world"] "hello world".split("o") # ["hell", " w", "rld"] ", ".join(["a", "b", "c"]) # "a, b, c" "hello".replace("l", "r") # "herro" "hello".startswith("he") # True "hello".endswith("lo") # True "hello world".find("world") # 6 (-1 if not found) "hello world".count("l") # 3 "42".isdigit() # True # Multi-line strings text = """ First line Second line """ # Raw strings (no escape processing) path = r"C:\Users\name\folder" Lists nums = [1, 2, 3, 4, 5] # Access nums[0] # 1 (first) nums[-1] # 5 (last) nums[1:3] # [2, 3] (slice) nums[:3] # [1, 2, 3] (first 3) nums[2:] # [3, 4, 5] (from index 2) nums[::2] # [1, 3, 5] (every 2nd) nums[::-1] # [5, 4, 3, 2, 1] (reversed) # Modify nums.append(6) # add to end nums.insert(0, 0) # insert at index nums.extend([7, 8]) # add multiple nums.remove(3) # remove first occurrence nums.pop() # remove and return last nums.pop(0) # remove and return at index nums.sort() # sort in place nums.sort(reverse=True) # sort descending nums.reverse() # reverse in place nums.clear() # remove all # Functions len(nums) # length min(nums) # smallest max(nums) # largest sum(nums) # total sorted(nums) # new sorted list reversed(nums) # reversed iterator Dictionaries user = {"name": "Alex", "age": 25, "city": "Berlin"} # Access user["name"] # "Alex" (KeyError if missing) user.get("name") # "Alex" user.get("phone", "N/A") # "N/A" (default if missing) # Modify user["age"] = 26 # update user["email"] = "alex@mail.com" # add new key del user["city"] # delete key user.pop("age") # remove and return value # Iterate user.keys() # dict_keys(["name", "age", ...]) user.values() # dict_values(["Alex", 25, ...]) user.items() # dict_items([("name", "Alex"), ...]) for key, value in user.items(): print(f"{key}: {value}") # Merge (Python 3.9+) merged = dict1 | dict2 # dict2 values win on conflict Sets and Tuples # Sets — unique values, unordered colors = {"red", "green", "blue"} colors.add("yellow") colors.remove("red") # KeyError if missing colors.discard("red") # no error if missing a | b # union a & b # intersection a - b # difference a ^ b # symmetric difference # Tuples — immutable point = (10, 20) x, y = point # unpacking name, *rest = ("Alex", 25, "Berlin") # name="Alex", rest=[25, "Berlin"] Control Flow # if / elif / else if age < 18: print("minor") elif age < 65: print("adult") else: print("senior") # Ternary status = "adult" if age >= 18 else "minor" # for loop for item in items: print(item) for i in range(5): # 0, 1, 2, 3, 4 for i in range(2, 10): # 2, 3, ..., 9 for i in range(0, 10, 2): # 0, 2, 4, 6, 8 for i, item in enumerate(items): # index + value print(f"{i}: {item}") for a, b in zip(list1, list2): # parallel iteration print(a, b) # while loop while count > 0: count -= 1 # break / continue for item in items: if item == "skip": continue # skip this iteration if item == "stop": break # exit loop Pattern Matching (Python 3.10+) match status_code: case 200: print("OK") case 404: print("Not Found") case 500: print("Server Error") case _: print(f"Unknown: {status_code}") # Match with destructuring match point: case (0, 0): print("Origin") case (x, 0): print(f"On x-axis at {x}") case (0, y): print(f"On y-axis at {y}") case (x, y): print(f"Point at ({x}, {y})") # Match with guards match user: case {"role": "admin", "name": name}: print(f"Admin: {name}") case {"role": "user", "name": name} if name != "blocked": print(f"User: {name}") Comprehensions # List comprehension squares = [x**2 for x in range(10)] evens = [x for x in nums if x % 2 == 0] pairs = [(x, y) for x in range(3) for y in range(3)] # Dict comprehension word_lengths = {word: len(word) for word in words} filtered = {k: v for k, v in data.items() if v > 0} # Set comprehension unique_lengths = {len(word) for word in words} # Generator expression (lazy, memory-efficient) total = sum(x**2 for x in range(1000000)) Functions # Basic function def greet(name): return f"Hello {name}" # Default parameters def greet(name="World"): return f"Hello {name}" # *args and **kwargs def func(*args, **kwargs): print(args) # tuple of positional args print(kwargs) # dict of keyword args # Lambda double = lambda x: x * 2 sorted(users, key=lambda u: u["age"]) # Type hints (Python 3.10+) def greet(name: str) -> str: return f"Hello {name}" def process(items: list[int]) -> dict[str, int]: return {"sum": sum(items), "count": len(items)} Classes class User: def __init__(self, name: str, age: int): self.name = name self.age = age def greet(self) -> str: return f"Hi, I'm {self.name}" def __repr__(self) -> str: return f"User({self.name!r}, {self.age})" user = User("Alex", 25) # Dataclass (Python 3.7+) — auto-generates __init__, __repr__, __eq__ from dataclasses import dataclass @dataclass class User: name: str age: int city: str = "Unknown" # Inheritance class Admin(User): def __init__(self, name, age, level): super().__init__(name, age) self.level = level Error Handling try: result = 10 / 0 except ZeroDivisionError: print("Cannot divide by zero") except (ValueError, TypeError) as e: print(f"Error: {e}") else: print("Success") # runs if no exception finally: print("Always runs") # cleanup # Raise an exception raise ValueError("Invalid input") File I/O # Read entire file with open("file.txt", "r") as f: content = f.read() # Read lines with open("file.txt", "r") as f: lines = f.readlines() # list of lines # or for line in f: print(line.strip()) # Write with open("file.txt", "w") as f: # overwrite f.write("Hello\n") with open("file.txt", "a") as f: # append f.write("More text\n") # JSON import json data = json.loads('{"name": "Alex"}') # parse string text = json.dumps(data, indent=2) # to string with open("data.json", "r") as f: data = json.load(f) # parse file with open("data.json", "w") as f: json.dump(data, f, indent=2) # write file Async / Await import asyncio async def fetch_data(url: str) -> str: # simulate async work await asyncio.sleep(1) return f"Data from {url}" # Run async function async def main(): result = await fetch_data("https://api.example.com") print(result) asyncio.run(main()) # Run tasks in parallel async def main(): results = await asyncio.gather( fetch_data("https://api1.example.com"), fetch_data("https://api2.example.com"), ) print(results) # both complete in ~1 second, not 2 Common Built-in Functions Function Description Example len(x) Length len([1,2,3]) → 3 range(n) Sequence 0..n-1 range(5) → 0,1,2,3,4 enumerate(x) Index + value enumerate(["a","b"]) zip(a, b) Pair elements zip([1,2], ["a","b"]) map(fn, x) Apply function map(str, [1,2,3]) filter(fn, x) Filter elements filter(bool, [0,1,"",2]) any(x) True if any truthy any([False, True]) → True all(x) True if all truthy all([True, True]) → True sorted(x) New sorted list sorted([3,1,2]) → [1,2,3] reversed(x) Reversed iterator list(reversed([1,2,3])) isinstance(x, type) Type check isinstance(42, int) → True type(x) Get type type(42) → <class 'int'> dir(x) List attributes dir([]) help(x) Show docs help(str.split) Virtual Environments python -m venv .venv # create source .venv/bin/activate # activate (macOS/Linux) .venv\Scripts\activate # activate (Windows) pip install requests # install package pip freeze > requirements.txt # save dependencies pip install -r requirements.txt # install from file deactivate # deactivate Common Mistakes Mutable default arguments — def add(item, items=[]) shares the same list across all calls. Use def add(item, items=None) and items = items or [] inside the function. ...

March 22, 2026 · 7 min

Kotlin Tutorial #22: Kotlin Serialization and Working with JSON

In the previous tutorial, you learned about Kotlin DSLs. Now let’s learn about Kotlin Serialization. Serialization is the process of converting objects to a format like JSON, and deserialization is the reverse. Kotlin Serialization is the official library for this. In this tutorial, you will learn: Setting up Kotlin Serialization @Serializable and basic encoding/decoding Default values and optional fields @Transient for ignoring fields @SerialName for custom field names Nested objects and collections Enum serialization Json configuration options Polymorphic serialization JsonElement API for raw JSON Practical examples Setting Up Add the serialization plugin and dependency to your build.gradle.kts: ...

March 22, 2026 · 8 min

Kotlin Tutorial #21: Kotlin DSLs — Writing Expressive APIs

In the previous tutorial, you learned about inline functions and reified types. Now let’s learn about DSLs. A DSL (Domain-Specific Language) is a small language designed for a specific task. Kotlin makes it easy to create DSLs using lambdas with receivers. In this tutorial, you will learn: Lambdas with receiver Building a config DSL Nested DSL builders @DslMarker annotation HTML builder DSL Route DSL Query builder DSL Gradle-style DSL What is a DSL? A DSL is a language designed for a specific domain. You already use Kotlin DSLs every day: ...

March 22, 2026 · 9 min

Kotlin Tutorial #20: Inline Functions, Reified Types, and Contracts

In the previous tutorial, you learned about Flow. Now let’s learn about inline functions, reified types, and contracts. These are advanced Kotlin features that help you write faster code, work with generic types at runtime, and give the compiler extra information about your functions. In this tutorial, you will learn: Inline functions and why they matter Non-local returns noinline and crossinline Reified type parameters Contracts (callsInPlace, returns) Practical examples What Are Inline Functions? When you pass a lambda to a function, Kotlin creates a lambda object behind the scenes. This has a small performance cost: memory allocation and method calls. ...

March 22, 2026 · 9 min

Kotlin Tutorial #19: Flow — Reactive Streams in Kotlin

In the previous tutorial, you learned about advanced coroutine topics. Now let’s learn about Flow. Flow is Kotlin’s way of working with reactive streams. It lets you emit multiple values over time, with full support for coroutines. In this tutorial, you will learn: What is Flow Flow builder and emit/collect Operators: map, filter, take, transform Terminal operators: toList, first, reduce, fold flowOn for changing dispatchers combine and zip Error handling with catch and retry onCompletion and onEach conflate and distinctUntilChanged flatMapConcat, flatMapMerge, flatMapLatest StateFlow and SharedFlow (hot flows) stateIn and shareIn Practical examples What is Flow? Flow is a cold, asynchronous stream that emits values one at a time. ...

March 22, 2026 · 12 min

Kotlin Tutorial #18: Coroutines Deep Dive — Error Handling, SupervisorJob, and Channels

In the previous tutorial, you learned the basics of coroutines: suspend functions, launch, async, and structured concurrency. Now let’s dive deeper into error handling, supervisor jobs, and channels. In this tutorial, you will learn: CoroutineExceptionHandler SupervisorJob and supervisorScope Channels (send, receive, close) Channel types (buffered, conflated, unlimited) produce and consumeEach Fan-out and fan-in patterns select expression Mutex for shared mutable state withTimeout and withTimeoutOrNull Practical patterns Error Handling in Coroutines Error handling in coroutines is different from regular code. When a coroutine throws an exception, the behavior depends on whether you use launch or async. ...

March 22, 2026 · 10 min

Jetpack Compose Tutorial #9: ViewModel — Managing Screen Logic

In Tutorial #5, we learned about remember and mutableStateOf. They work great for simple state. But they have a big problem — when your app needs to load data from a database or API, where does that logic go? Not in the Composable. Composables are for UI, not business logic. That is what ViewModel is for. What is ViewModel? ViewModel is a class that holds your screen’s data and logic. It survives things that destroy and recreate your Composable — like screen rotation or system theme changes. ...

March 22, 2026 · 8 min

Kotlin Tutorial #16: Sequences — Lazy Collections for Performance

In the previous tutorial, you learned about delegation. Now let’s learn about sequences. Sequences are lazy collections that process elements one at a time instead of all at once. This can make a big difference in performance when working with large data sets. In this tutorial, you will learn: Sequences vs Lists (lazy vs eager) Creating sequences generateSequence Sequence builders with yield and yieldAll Infinite sequences When to use sequences Performance comparison Practical examples Sequences vs Lists Lists are eager — they process all elements at each step and create intermediate collections. Sequences are lazy — they process one element through all steps before moving to the next. ...

March 22, 2026 · 9 min