Skip to content
  • Home
  • How To
  • M0
  • Module 1 - Python
  • Agents
  • M2
  • M3
  • M4
  • M5
  • M6
  • Progress
  • Contact
  • Pricing
  • Sign in
  • Future
  1. Module 1M1
  2. ›
    S7Modules, Packages, and Environments
  3. ›
    Lesson 2 of 3The Standard Library, the Main Guard, and Entry Points
  4. ›
    S7›Lesson 2 of 3
  1. Module 1
  2. ›
  3. Section 7
  4. ›
  5. Lesson 2

The Standard Library, the Main Guard, and Entry Points

FREE·~1.5 h·2 labs·4 questions·1 assignment


Context

Python ships with a large collection of ready-made modules covering mathematics, summary statistics, random numbers, and the common file formats. A file can also detect whether it was run directly or merely imported, and a guard around that check separates the two cases.

Not writing code is the cheapest way to get it right. Mean and standard deviation, a reproducible random shuffle for splitting data, and the readers for structured formats are all already present, tested, and correct, which matters most for the numeric routines where a hand-rolled version is subtly wrong rather than obviously broken. The guard is what lets one file be both a runnable entry point and an importable library.

Leaving the guard out is the concrete failure. Importing a file to reuse one function also executes everything at its top level, so pulling in a cleaning helper silently launches a full training run, and from the caller's point of view the import simply hangs. Re-implementing a statistic by hand is the quieter cost, because the result looks reasonable and disagrees with every other tool. The previous lesson split code across files, this lesson decides which of those files is the one you run.


Goal

  • Reach for standard-library modules (math, statistics, random, json, csv) instead of re-implementing solved problems.
  • Distinguish a file being run as a script from a file being imported as a module.
  • Apply the main guard if __name__ == "__main__": to gate run-only code.
  • Explain why an importable file must not perform work the instant it is imported.
  • Construct an entry-point script that wires together imported library modules.

Prerequisites

Optional, neither is required. The lesson stands on its own, they are here if you want the guard explained once before you meet it.

  • YouTube - "if name == 'main' for Python beginners" by Bro Code. A short, precise account of what __name__ actually holds.
  • Official docs - The Python Standard Library contents page, the "Numeric and Mathematical Modules" and "File Formats" group headings. Skim only the group names, for the breadth of what ships built in.

Table of Contents

  • The Standard Library
  • stdlib Modules (math / statistics / random / json / csv)
  • Entry-Point Script
  • Main Guard (__name__ == "__main__")

Theory

The Standard Library

What. The standard library is the large set of modules that ships with Python itself, available to import with no installation step. Why. It means common problems, square roots, averages, randomness, reading JSON and CSV, are already solved, tested, and one import away, so you do not rewrite them. When. Before writing any non-trivial helper, you check whether the standard library already provides it, because re-implementing solved problems wastes time and adds bugs.

Everything covered in this lesson ships with Python 3, there is nothing to install, no pip step, no download. You simply import the module and use it. That is the meaning of "batteries included": a fresh Python gives you hundreds of ready modules out of the box. Attempting to install one, like pip install math, does not do what you expect. In (bootcamp), which has no pip of its own, the shell reports that pip is not found before anything is looked up, and an installer that does run looks for a published package of that name, while the standard library is not published, it is already inside your Python.

The standard library is organized by topic, numeric tools, text tools, file and data formats, and many more. You do not memorize it, you learn the handful you use constantly and look up the rest. The instinct to build before checking is the habit this lesson is trying to replace.

# everything here ships WITH Python 3, no 'pip install' needed
import math             # 'math' is a standard-library module of numeric helpers

# 'math.sqrt' computes a square root
print(math.sqrt(16.0))  # 4.0
# 'math.pi' is a constant the module provides
print(math.pi)          # 3.141592653589793

Neither line required any setup beyond the import. If you have ever started writing your own square-root routine, this is the module that had already done it, faster and correctly.

DON'T:

  • DON'T re-implement something the standard library already provides, like writing your own averaging loop when statistics.mean exists.
  • DON'T run pip install math or pip install json, those ship with Python and the command will fail or mislead you.

stdlib Modules (math / statistics / random / json / csv)

What. These five modules are the specific standard-library helpers this lesson leans on, numeric math, summary statistics, randomness, and the JSON and CSV data formats. Why. They cover the everyday needs of data work, computing a mean, fixing a random seed for reproducibility, and moving structured data in and out of files, without pulling in anything external. When. You use statistics and math for quick numeric work, random whenever sampling or shuffling, and json/csv whenever you persist or load structured data.

The statistics module gives you summary functions over a list of numbers, so an average becomes a single call rather than a hand-written loop.

import statistics                  # summary statistics over a sequence of numbers

losses = [0.30, 0.25, 0.20, 0.22]  # per-epoch training loss
# 'mean' is the average, 'median' is the middle value
print(statistics.mean(losses))     # 0.2425
print(statistics.median(losses))   # 0.235
# 'pstdev' is the population standard deviation, a spread measure
print(statistics.pstdev(losses))   # 0.0376662979332984

Three one-line calls replace several loops and their edge cases. For modest data this is exactly the right tool, and for very large numeric arrays you will later reach for numpy (a third-party numerical library the next lesson installs), which is faster.

import random                        # pseudo-random numbers and sampling

random.seed(42)                      # fix the seed so runs are REPRODUCIBLE (vital in ML)
# 'random' returns a float in [0.0, 1.0)
print(random.random())               # 0.6394267984578837
# 'randint' returns an integer from 1 to 6, both ends included
print(random.randint(1, 6))          # 1
# 'sample' draws unique items, here a mini-batch of 5 indices
print(random.sample(range(100), 5))  # [94, 35, 31, 28, 17]

The seed is the line that matters. It pins where the sequence starts, so every run of the program produces the same draws in the same order, and without it a difference between two runs of the same experiment can come from the draws alone.

import json                # convert between Python objects and JSON text

# dict of hyperparameters
config = {"learning_rate": 0.01, "epochs": 10}
# 'json.dumps' SERIALIZES a Python object into a JSON string
text = json.dumps(config)
print(text)                # {"learning_rate": 0.01, "epochs": 10}
# 'json.loads' parses JSON text back into a Python object
restored = json.loads(text)
print(restored["epochs"])  # 10

You met json in 1.5.3 as a way to save and load data, and here it sits in its proper home, one standard-library module among several. That round trip, object to text to object, is how configuration and results travel between a program and a file.

import csv                                  # read and write comma-separated rows

# 'with open(...)' opens the file and auto-closes it at block end
with open("scores.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)                  # a CSV writer bound to file 'f'
    writer.writerow(["model", "accuracy"])  # the header row
    writer.writerow(["svm", 0.91])          # one data row
    writer.writerow(["tree", 0.87])         # another data row

The csv module handles the quoting and the separators for you. Writing CSV by hand with string joins works right up until a value contains a comma, and then it corrupts the row without raising, which is the bug this module exists to remove.

DON'T:

  • DON'T forget random.seed(...) when you need reproducible results, an unseeded generator starts each run from a different point, so two runs cannot be expected to match.
  • DON'T build CSV lines by gluing strings with commas yourself, a value containing a comma will silently corrupt the file, use the csv module.

Entry-Point Script

What. The entry-point script is the single file you launch a program from, the one you name in python3 something.py. Why. A file behaves differently depending on whether it is the entry point or merely imported, and confusing the two leads to code that runs at the wrong time. When. You think about the entry point whenever a file can be both run directly and imported by another file, which is most library modules.

A .py file can enter execution in two different ways. It can be the file you launched, python3 train.py, in which case it is the entry-point script. Or it can be pulled in by another file, import train, in which case it runs as an imported module. This sharpens what Lesson 1.7.1 showed, that importing a file runs it: the same file, two roles.

Python tells the file which role it is playing through a built-in variable named __name__. When the file is the entry point, __name__ holds the string "__main__". When the file is imported, __name__ holds the module's own name instead, such as "train".

# === file: train.py ===
# '__name__' is a variable Python sets automatically for every module
print(__name__)

# run directly:  python3 train.py   -> prints __main__
# imported:      import train        -> prints train

One line, two answers, decided entirely by how the file was started. That single variable is the whole basis of the main guard you are about to write.

DON'T:

  • DON'T place slow or side-effecting work at the top level of an importable file, it will execute in every program that imports it, at the first import.
  • DON'T assume a file is always the entry point, the same file often runs as an imported module elsewhere.

Main Guard (__name__ == "__main__")

What. The main guard is the idiom if __name__ == "__main__":, a block that runs only when the file is the entry point and is skipped when the file is imported. Why. It lets one file be both a usable library (safe to import) and a runnable script (does something when launched), without the import accidentally triggering the script's work. When. You add it to any module that defines reusable functions but also wants a demo, a self-test, or a launch routine that should fire only on direct execution.

Without the guard, every top-level statement runs on import. If metrics.py ends with a print that demonstrates its function, then any program doing import metrics would unexpectedly execute that print. The guard fences off that run-only code so importing the module stays silent and side-effect free.

# === file: metrics.py ===
# 'accuracy' returns the fraction of correct predictions
def accuracy(correct, total):
    return correct / total

# this block runs ONLY when the file is the entry point
# it is SKIPPED when another file does 'import metrics'
if __name__ == "__main__":
    # a quick self-test, useful when developing 'metrics.py' alone
    print(accuracy(91, 100))  # 0.91

An importer of metrics gets the function and nothing else. The file still runs top to bottom on import, the guard is simply false, so the self-test is the one thing that does not happen.

A common, cleaner pattern puts the program's actual work inside a function named main, and the guard merely calls it. This keeps the top level free of logic and makes the launch point obvious.

# === file: app.py ===
import statistics

# 'main' holds the program's real work, defined but not yet run
def main():
    dataset = [0.91, 0.88, 0.94]  # accuracy per run
    # use a stdlib helper instead of a hand-written average
    print("mean accuracy:", statistics.mean(dataset))

# call 'main' ONLY when launched directly
if __name__ == "__main__":
    main()

This main()-plus-guard shape is the conventional skeleton of a runnable Python file, and what it buys is the separation of defining the work from deciding when it runs.

DON'T:

  • DON'T write __name__ == "main", the entry-point value is "__main__" with double underscores on both sides. The single-word version is silently false whenever you run the file, and true only when another file imports a file named main.py, the one case the guard exists to skip.
  • DON'T bury reusable functions inside the guard, names defined under if __name__ == "__main__": are not available when the file is imported.

Lab Exercises

Lab Exercise 1: Four Modules, No Loops of Your Own

Given samples = [9.0, 7.5, 8.0, 10.0, 6.5], use statistics to print the mean and the median, and math to print the square root of the mean. Write no manual averaging loop.

Then take config = {"learning_rate": 0.01, "epochs": 10}, turn it into JSON text with the string form of the json call, print that text, parse it back, and print one value out of the restored dict.

Finally write scores.csv with the csv module, a header row model,accuracy and two data rows, then read the whole file back and print the rows as a list. Add a comment naming the type every value comes back as, and say which earlier lesson made that same point about a field read from a file.

Lab Exercise 2: One File, Two Roles

Write converter.py defining to_fraction(correct, total) that returns correct / total. Put a main() function beneath it that prints __name__ and then the result for (3, 4), and call main() from a main guard.

Run converter.py directly and record both printed lines. Then write use_converter.py that imports the function, prints __name__ from its own perspective, and calls to_fraction(1, 2).

Run the second file and state how many lines it printed, what __name__ held in each of the two files, and why the self-test did not fire.


Solution 1: Four Modules, No Loops of Your Own
import statistics                     # summary numbers over a sequence
import math                           # scalar numeric helpers

samples = [9.0, 7.5, 8.0, 10.0, 6.5]
avg = statistics.mean(samples)        # computed once, reused below

print("mean:", avg)                   # mean: 8.2
# median: 8.0
print("median:", statistics.median(samples))
print("sqrt(mean):", math.sqrt(avg))  # sqrt(mean): 2.8635642126552705

No loop, no manual sum, no empty-list special case. That is the whole argument for looking before you build.

import json

config = {"learning_rate": 0.01, "epochs": 10}

text = json.dumps(config)    # 'dumps' returns a STRING, it touches no file
print(text)                  # {"learning_rate": 0.01, "epochs": 10}

restored = json.loads(text)  # 'loads' parses a string back into Python
print(restored["epochs"])    # 10
import csv

with open("scores.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["model", "accuracy"])  # the header row
    writer.writerow(["svm", 0.91])
    writer.writerow(["tree", 0.87])

with open("scores.csv", newline="", encoding="utf-8") as f:
    print(list(csv.reader(f)))
# [['model', 'accuracy'], ['svm', '0.91'], ['tree', '0.87']]
# Everything comes back as a STRING, including the 0.91 that went in as a
# float. That is the same point 1.5.3 made about a field read from a file,
# and the 'csv' module does not change it, it handles quoting and separators.

The newline="" argument is the one piece here that is not obvious. It tells Python to hand the csv module the line endings untouched, because the module does its own line-ending handling, and omitting it produces a blank line between every row on some platforms.

DON'T:

  • DON'T hand statistics.mean an empty list, it raises StatisticsError rather than returning zero.
  • DON'T build CSV lines by joining strings with commas yourself, one value containing a comma corrupts the file silently.
Solution 2: One File, Two Roles
# === file: converter.py ===
# 'to_fraction' returns a ratio, and is the reusable part of this file
def to_fraction(correct, total):
    return correct / total

def main():
    print("converter says __name__ is", __name__)
    print(to_fraction(3, 4))

# the call happens ONLY when this file is the one being launched
if __name__ == "__main__":
    main()
$ python3 converter.py
converter says __name__ is __main__
0.75
# === file: use_converter.py ===
from converter import to_fraction  # runs converter.py, guard evaluates to False

print("use_converter says __name__ is", __name__)
print(to_fraction(1, 2))
$ python3 use_converter.py
use_converter says __name__ is __main__
0.5

Two lines, not four. converter.py did run, in full, the moment it was imported, which is why to_fraction exists at all. What did not run is main(), because inside an imported converter.py the name __name__ holds "converter" and the guard is false. Both files print __main__ for their own __name__ when they are the one you launched, which is the point: the value describes the file's role right now, not the file.

DON'T:

  • DON'T move to_fraction under the guard, use_converter.py imports that name and the import would fail because the name would never be created.
  • DON'T read the silent self-test as proof the module did not execute, it executed completely and only the guarded block was skipped.

Theory Assessment

Q1: What Ships With Python, and Reaching For It

Explain what the standard library is and what it implies about installation when you want math or json. Then say which module and which specific functions you would reach for to report the average and the spread of a list of training losses, and why that beats writing the loops yourself.

Hint: One of the two halves is about what you do not have to fetch, the other about what you do not have to write.

Q2: The Line Ending, and the Comma Inside the Field

Explain why a file handed to the csv module is opened with newline="", and what goes wrong without it. Then say why writing a CSV with ",".join(...) is a bug waiting to happen, and what the csv module does about it.

Hint: For the first half, ask which of the two should be in charge of line endings, Python's file object or the csv module. For the second, think about a value that itself contains the separator.

Q3: Two Values, One Variable

State the two possible values of __name__ for a file and what each one tells you about how the file is running at that moment. Then explain what goes wrong when a teammate leaves print(train_summary()) unguarded at the top level of train.py, from the point of view of a file that merely wanted to import one function from it.

Hint: Every top-level statement runs on import, not only the ones you thought of as setup.

Q4: The Same Two Files, Launched Two Ways

Read the following without running it, then state the exact output of python3 run.py, and separately the exact output of python3 tools.py.

# === file: tools.py ===
import random

random.seed(0)
DRAWN = random.randint(1, 100)
print("tools:", __name__, DRAWN)

def draw():
    return random.randint(1, 100)

if __name__ == "__main__":
    print("self-test:", draw())

# === file: run.py ===
import tools

print("run:", __name__, tools.DRAWN)
print(tools.draw())
print(tools.draw())

Say why run.py prints four lines rather than five, why DRAWN is the same number in both invocations, and why the two tools.draw() calls do not both return that same number even though the seed was fixed.

Hint: A seed fixes where the sequence starts, not what every draw in it is. Ask what random.randint does to the generator's position each time it is called.


Answer 1:

The standard library is the collection of modules bundled with Python itself, so math, statistics, random, json and csv are available the instant Python is installed. Using them takes an import and nothing else, never a pip install and never a download, because they are already inside your Python. That is what "batteries included" names.

For the average and the spread of a list of losses you want statistics, specifically statistics.mean for the center and statistics.pstdev (or stdev, the sample standard deviation) for the spread. They beat hand-written versions because they are already correct and already tested, they turn several lines of looping into two clear calls, and every edge case you would have had to think about has been thought about. Re-implementing them can only add bugs the library had already removed.

DON'T try to pip install a standard-library module, it ships with Python and the command will either fail or install something else entirely.

Answer 2:

newline="" tells Python's file object to pass line endings through untouched, because the csv module does its own line-ending handling. csv.writer ends every row with \r\n, and a file opened without newline="" on a platform that translates line endings, Windows among them, writes each one as \r\r\n, which shows up as a blank line after every row.

Writing a CSV with ",".join(...) breaks the first time a value contains a comma, because the separator you are inserting is indistinguishable from the one already inside the field, and the row silently gains a column. The csv module handles that by quoting any value that needs it, and its reader understands those quotes on the way back in. Nothing raises in the hand-rolled version, which is what makes it worse than a crash.

DON'T drop newline="" from a csv file because your own rows look fine, the same script on a platform that translates line endings writes a blank line after every row.

Answer 3:

__name__ holds "__main__" when the file is the entry point you launched directly, and it holds the module's own name, such as "train", when the file was imported by something else. Reading it is how a file discovers which of the two roles it is playing at that moment, and testing it is the whole mechanism of the guard.

An unguarded print(train_summary()) at the top level runs the instant a program first imports train, because the first import executes every top-level statement in the file. A file that only wanted train's cleaning helper gets a full summary computed and printed as a side effect it never asked for, and if train_summary() is slow, every program that imports train pays that cost. From the importer's side the symptom is baffling, the import statement appears to hang or to print things nobody wrote. Wrapping the call in if __name__ == "__main__": fences it off so the import stays silent and the launch still works.

DON'T put work at the top level of a file you expect anyone to import, define things there and gate the doing.

Answer 4:

python3 run.py prints four lines:

tools: tools 50
run: __main__ 50
98
54

python3 tools.py prints two:

tools: __main__ 50
self-test: 98

run.py prints four rather than five because tools.py's guarded self-test never fires. Importing runs tools.py in full, so the seed is set, DRAWN is computed and the tools: line prints, but inside an imported tools.py the name __name__ holds "tools", the guard is false, and the self-test line is skipped. Run tools.py directly and the same file reports __main__ and the self-test does run, which is the same file producing different output for no reason except how it was launched.

DRAWN is 50 in both invocations because random.seed(0) resets the generator to a fixed starting position and DRAWN is always the first draw taken from it.

The two tools.draw() calls return 98 and 54 because each call advances the generator. A seed fixes where the sequence begins, it does not make every value in the sequence identical, so the second and third draws are simply the next numbers along. That is exactly what you want from reproducibility: the same run twice gives the same four lines, while the four lines within one run are still different from each other.

DON'T read a fixed seed as "the same number every time", it means the same SEQUENCE every time, and each draw takes the next value along it.


Assignment

Assignment: A CSV-Driven Tool You Launch From the Terminal

Build the following in one file, meanline.py, and then run it three ways.

When launched directly, it takes a CSV filename from the command line, reads the first column with the csv module, prints the mean of that column with statistics, and writes the result to result.json. If no filename is given it prints a usage line and stops rather than crashing. All of that lives inside a main() called from a main guard.

Before running it, write a small losses.csv from a separate short script so the tool has something real to read, and keep that generation out of meanline.py itself.

Then run it three ways and record each result: with the filename, with no argument at all, and with the filename a second time to show the output is identical. Finally, add a comment stating what sys.argv holds in the first of those runs and what type its entries are.

Hints:

  • sys.argv[0] is the script's own name, so the user's filename sits at sys.argv[1], and the length has to be checked before that index is read.
  • csv.reader yields each row as a list of strings, so row[0] needs converting before statistics.mean will accept it.

Solution:
# === file: make_data.py ===
# Kept OUT of the tool, so the tool reads a file it did not create itself.
import csv

with open("losses.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    for value in [0.30, 0.25, 0.20, 0.22]:  # per-epoch training loss
        writer.writerow([value])
# === file: meanline.py ===
import sys                                # 'sys.argv' holds the command-line pieces
import csv                                # read the rows
import json                               # write the result
import statistics                         # compute the mean

def main():
    # 'sys.argv[0]' is the script name, so a user argument sits at index 1
    if len(sys.argv) < 2:
        # no crash, a message
        print("usage: python3 meanline.py <file.csv>")
        return
    filename = sys.argv[1]                # always a STRING, whatever the user typed

    values = []
    with open(filename, newline="", encoding="utf-8") as f:
        for row in csv.reader(f):         # each row is a list of strings
            values.append(float(row[0]))  # text to number, mandatory

    avg = statistics.mean(values)
    print("mean:", avg)
    with open("result.json", "w", encoding="utf-8") as f:
        json.dump({"mean": avg}, f)

# the tool only runs itself when it is the file being launched
if __name__ == "__main__":
    main()
$ python3 make_data.py
$ python3 meanline.py losses.csv
mean: 0.2425
$ python3 meanline.py
usage: python3 meanline.py <file.csv>
$ python3 meanline.py losses.csv
mean: 0.2425
# In the first run, 'sys.argv' is ['meanline.py', 'losses.csv'], a list of
# two STRINGS whatever the user typed, which is why 'sys.argv[1]' is usable
# as a filename as it stands.

Three runs, three outcomes, and the interesting one is the middle. A missing argument is not an error in the program's own logic, it is a user doing something reasonable that the program has to survive, so it gets a message and a return rather than an IndexError from sys.argv[1]. That is the same fail-fast instinct from the previous section applied at the outermost boundary a program has.

The first and third runs being identical is worth noticing too. The tool reads a file it did not write, so its output depends on the data rather than on how many times it has run, which is the property that makes it a tool rather than a demonstration. Had the CSV generation lived inside meanline.py, every run would have silently rebuilt its own input and the tool would only ever be able to report on itself.

DON'T read sys.argv[1] before checking the length, and don't hand statistics.mean the raw row[0], every value out of a CSV is text and the conversion is yours to make.


Additional Resources

Optional, and nothing later depends on either. Worth a look if you want the sampling helpers or the authoritative word on entry points.

  • Official docs - random, "Functions for sequences". choice, sample and shuffle, which is most of what sampling work needs.
  • Official docs - __main__, "Idiomatic Usage". The authoritative account of the guard and of what belongs on either side of it.

Summary

There is a quiet discipline at the heart of this lesson, and it is mostly about restraint. The standard library exists so that you write less, not more, the mean is already implemented, the JSON parser is already correct, the CSV writer already handles the comma you would have forgotten. Reaching for those first is not laziness, it is the recognition that solved problems should stay solved. As your programs grow into the real work of loading data and reporting results, the instinct to check what already exists before building it yourself is what keeps a codebase small and trustworthy.

The main guard looks like a single odd line, but it encodes a real idea about identity. A file does not know in advance whether it will be launched or imported, and the same code can mean two different things depending on which. By testing __name__, a file asks "am I the program right now, or am I a part someone else is using?" and answers correctly every time. That question is what lets one file serve honestly as both a library and a script, and it is why importing a well-built module is silent while running it does something.

Put together, these ideas describe the shape of a grown-up Python file: it defines reusable things at the top level, it leans on the standard library for the common parts, and it gates its run-only behavior behind a guard so importing it is always safe. None of this is flashy. It is the difference between code that happens to work when you run it and code that behaves predictably wherever it is used, which is the difference you will feel most as your projects start importing each other in earnest.


Cheat Sheet

Module / Idiom Key calls When to Use When to Avoid
math math.sqrt, math.pi scalar numeric math large arrays (use numpy later)
statistics mean, median, pstdev summaries of small lists huge numeric data (slow)
random seed, random, randint, sample sampling, shuffling when you forget to seed for reproducibility
json dump/load, dumps/loads structured config and results binary or non-serializable objects
csv reader, writer tabular text data hand-joining strings with commas
Entry point python3 file.py launching a program expecting it on import
Main guard if __name__ == "__main__": gate run-only code placing importable names inside it
# the conventional runnable-file skeleton
import statistics                 # stdlib: no install needed

def main():                       # all real work lives here
    data = [3, 1, 4, 1, 5]
    print(statistics.mean(data))  # lean on the stdlib

if __name__ == "__main__":        # '__main__' = launched directly
    main()                        # skipped entirely on import
←Previous: Lesson 1
Next: Lesson 3→

Questions about this lesson: #module-1 on Discord

Found an error in this lesson? Report it

Your progress
Current streak
🔥 0
Lessons marked today
0 / 3
Longest streak
0

Sign in

The bootcamp

  • Curriculum
  • How To
  • Pricing
  • Changelog
  • Future

The community

  • Discord
  • Reviews
  • Contact

Follow

  • Instagram
  • X
  • TikTok
  • Reddit

Legal

  • Terms
  • Privacy
  • Refunds

Coming soon on November 16, 2026

Theme