From 9c756d476086a3fcb22f990e0e7e36e7dc462b07 Mon Sep 17 00:00:00 2001 From: Luka Sherman Date: Thu, 17 Sep 2026 20:21:47 -0700 Subject: [PATCH 1/2] new time library page --- docs/index.md | 5 ++ docs/libraries/datetime.md | 7 ++ docs/libraries/index.md | 14 ++++ docs/libraries/time.md | 147 +++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 5 files changed, 174 insertions(+) create mode 100644 docs/libraries/time.md diff --git a/docs/index.md b/docs/index.md index 1900dcc..d260392 100644 --- a/docs/index.md +++ b/docs/index.md @@ -605,6 +605,11 @@ hide: Regular expressions: searching, extracting, and replacing text by pattern. +- :material-clock-outline:{ .lg .middle } [__time__](libraries/time.md) +[:material-language-python:](libraries/time.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" } + + Reading the system clock, pausing execution, and measuring elapsed time. + diff --git a/docs/libraries/datetime.md b/docs/libraries/datetime.md index d54f81c..ad94b2d 100644 --- a/docs/libraries/datetime.md +++ b/docs/libraries/datetime.md @@ -10,6 +10,13 @@ description: >- The **`datetime`** module is Python's standard library for working with dates and times — logging when an observation happened, measuring how long ago it was, or formatting a date for display. +| | `datetime` | [`time`](time.md) | +|---|---|---| +| Focus | Calendar dates, date arithmetic, and human-readable date/time values. | The system clock, code timing, and pausing execution. | +| Time format | High-level objects — `date`, `time`, `datetime`, `timedelta`. | A Unix timestamp — a plain float counting seconds since the epoch. | +| Timezone support | Full — handles timezone-aware dates and conversions. | Limited — relies on the system's local time. | +| Common uses | | | +
## Setup { data-card-link="skip" } diff --git a/docs/libraries/index.md b/docs/libraries/index.md index 64f4d07..d6aad60 100644 --- a/docs/libraries/index.md +++ b/docs/libraries/index.md @@ -139,6 +139,20 @@ Libraries allow us to apply Python to real tasks. These are a few popular ones, [**`split`**](re.md#splitting-on-a-pattern) +- :material-clock-outline:{ .lg .middle } [__time__](time.md) +[:material-language-python:](time.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" } + + Reading the system clock, pausing execution, and measuring elapsed time. + + [**`time`**](time.md#reading-the-clock) + + [**`sleep`**](time.md#pausing-execution) + + [**`perf_counter`**](time.md#measuring-elapsed-time) + + [**`localtime`**](time.md#formatting-the-current-time): + [`strftime`](time.md#formatting-the-current-time) +
diff --git a/docs/libraries/time.md b/docs/libraries/time.md new file mode 100644 index 0000000..46b2ec8 --- /dev/null +++ b/docs/libraries/time.md @@ -0,0 +1,147 @@ +--- +description: >- + Reading the system clock, pausing execution, and measuring elapsed time in Python with the + time module. +--- + +# :material-clock-outline:{ .lg .middle } time library + +[Official documentation :material-open-in-new:](https://docs.python.org/3/library/time.html){ target="_blank" } + +The **`time`** module reads the system clock, pauses a program for a set number of seconds, and measures how long a piece of code takes to run. + +| | `time` | [`datetime`](datetime.md) | +|---|---|---| +| Focus | The system clock, code timing, and pausing execution. | Calendar dates, date arithmetic, and human-readable date/time values. | +| Time format | A Unix timestamp — a plain float counting seconds since the epoch. | High-level objects — `date`, `time`, `datetime`, `timedelta`. | +| Timezone support | Limited — relies on the system's local time. | Full — handles timezone-aware dates and conversions. | +| Common uses | | | + +
+ +## Setup { data-card-link="skip" } + +`time` ships with Python's standard library — nothing to install. The whole module is used through the `time.` prefix, so a plain import is all you need. + +```python-ref +import time +``` + +| Function | Returns | Example | +|----------|---------|---------| +| `time()` | Seconds since the epoch, as a float | `1785024000.0` | +| `sleep(seconds)` | Pauses the program, returns `None` | `sleep(2)` | +| `perf_counter()` | A high-resolution timer, for measuring durations | `perf_counter()` | +| `localtime()` | The current time as a `struct_time` | `localtime()` | +| `strftime(format, t)` | A `struct_time` formatted as a string | `strftime("%H:%M", localtime())` | + +
+ +
+ +## Reading the clock + +`time()` returns the number of seconds since the epoch[^epoch] — a single float that always increases, useful for a timestamp or for logging when an observation happened. + +```python-ref +import time + +print(time.time()) +``` + +[^epoch]: The epoch is a fixed reference point, midnight, January 1, 1970 (UTC). "Seconds since the epoch" is just a plain number, not tied to any calendar, which is why it's easy to compare or subtract. + +
+ +
+ +## Pausing execution + +`sleep()` pauses the program for the given number of seconds before continuing to the next line. Useful for spacing out repeated `print()` calls, or waiting between requests to an external service. + +```python-ref +import time + +print("checking on the burmese python...") +time.sleep(1) +print("still there.") +``` + +??? run "Run a pausing example" + All the examples above, combined into one script: + + ```python + import time + + print(time.time()) + + import time + + print("checking on the burmese python...") + time.sleep(1) + print("still there.") + ``` + +
+ +
+ +## Measuring elapsed time + +`perf_counter()` reads a high-resolution timer meant for measuring durations, not for reading the wall-clock date — call it before and after a block of code, then subtract the two readings to get the elapsed time in seconds. + +```python-ref +import time + +start = time.perf_counter() +total = sum(range(1_000_000)) +elapsed = time.perf_counter() - start + +print(elapsed) +``` + +??? note "Why not time() for this?" + `time()` tracks the system clock, which can jump backward or forward (a clock sync, daylight saving). `perf_counter()` is unaffected by that — it only ever counts forward, which makes it the right tool for timing how long code takes to run. + +??? run "Run a measuring elapsed time example" + All the examples above, combined into one script: + + ```python + import time + + start = time.perf_counter() + total = sum(range(1_000_000)) + elapsed = time.perf_counter() - start + + print(elapsed) + ``` + +
+ +
+ +## Formatting the current time + +`localtime()` returns a `struct_time` — the current date and time broken into named fields (`tm_year`, `tm_hour`, `tm_min`, and so on). `strftime()` turns one into a custom-formatted string, using the same format codes as [`datetime`'s `strftime`](datetime.md#formatting-with-strftime): `%H` the zero-padded hour, `%M` the zero-padded minute. + +```python-ref +import time + +now = time.localtime() +time.strftime("%H:%M", now) # "14:30" +``` + +??? tip "Reaching for datetime instead" + `time` works with a `struct_time`, a plain tuple of fields, which has no date arithmetic of its own — no adding a week, no subtracting two times. For anything beyond formatting the current moment, the [`datetime`](datetime.md) module's `date` and `datetime` objects are the better fit. + +??? run "Run a formatting example" + All the examples above, combined into one script: + + ```python + import time + + now = time.localtime() + print(time.strftime("%H:%M", now)) + ``` + +
diff --git a/mkdocs.yml b/mkdocs.yml index 7fe9411..7ac8570 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,6 +26,7 @@ nav: - math: libraries/math.md - random: libraries/random.md - re: libraries/re.md + - time: libraries/time.md - Data analysis: - csv: libraries/csv.md - matplotlib: libraries/matplotlib.md From 07cf8e27ae88e11970c06dfea96d946f00169ec8 Mon Sep 17 00:00:00 2001 From: Luka Sherman Date: Thu, 17 Sep 2026 20:22:24 -0700 Subject: [PATCH 2/2] rewrite style page and string formatting types --- docs/foundations.md | 2 +- docs/index.md | 25 ++- docs/style.md | 412 +++++++++++++++++++++++++++++++++++++++---- docs/types.md | 79 +++++---- includes/glossary.md | 1 + 5 files changed, 440 insertions(+), 79 deletions(-) diff --git a/docs/foundations.md b/docs/foundations.md index 8582f64..404ff27 100644 --- a/docs/foundations.md +++ b/docs/foundations.md @@ -310,7 +310,7 @@ You can also build one string yourself with `+` and print that instead of using print(species + " " + str(length_ft) + " ft") # ball python 4.5 ft — same output, more typing ``` -For building a full sentence out of text and variables, an [f-string](types.md#f-strings) is usually clearer than either approach. +For building a full sentence out of text and variables, an [f-string](types.md#building-strings) is usually clearer than either approach. ??? run "Run a printing variables example" All the examples above, combined into one script: diff --git a/docs/index.md b/docs/index.md index d260392..f158da5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -184,9 +184,10 @@ hide: [`combine`](types.md#combine) [`count`](types.md#search) [`endswith`](types.md#validate) - [`f-string`](types.md#f-strings) + [`f-string`](types.md#building-strings) [`find`](types.md#search) - [`format`](types.md#f-strings) + [`format`](types.md#building-strings) + [`format spec`](types.md#building-strings) [`in`](types.md#search) [`index`](types.md#access-characters) [`isalpha`](types.md#validate) @@ -520,6 +521,10 @@ hide: Conventions for standardized and readable Python. + [**`checklist`**](style.md#checklist) + + [**`linter`**](style.md#linter-tool) + [**`PEP 8`**](style.md#pep-8-style-guide): [`blank lines`](style.md#blank-lines) [`comments`](style.md#comments) @@ -531,14 +536,16 @@ hide: [`quote style`](style.md#quote-style) [`whitespace`](style.md#whitespace) - [**`Pythonic patterns`**](style.md#pythonic-patterns): - [`common patterns`](style.md#common-patterns) + [**`Pythonic patterns`**](style.md#pythonic-patterns) - [**`best practices`**](style.md#additional-best-practices) - - [**`linter`**](style.md#linter-tool) - - [**`checklist`**](style.md#checklist) + [**`Polish`**](style.md#polish): + [`banners`](style.md#banners) + [`input validation`](style.md#input-validation) + [`menus`](style.md#menus) + [`printing output`](style.md#printing-output) + [`progress bars`](style.md#progress-bars) + [`randomize`](style.md#randomize-messages) + [`unicode symbols`](style.md#unicode-symbols) - :material-bug-outline:{ .lg .middle } [__Errors__](errors.md) diff --git a/docs/style.md b/docs/style.md index 8b4c4f8..891d30f 100644 --- a/docs/style.md +++ b/docs/style.md @@ -271,9 +271,6 @@ length_ft = 4.5 # too short # PEP 8 — two spaces before, one after There's no single tool that reliably flags all "unpythonic" code the way PEP 8 has a document to check against. The real habit is asking *"does Python already have a built-in way to do this?"* before writing a manual loop, counter, or flag — an instinct built over time to recognize the built-in pattern. Other programming languages have different features and patterns, so if code is translated from another language into Python it might not be written very clearly. Pythonic code tends to be less buggy and faster. - -### Common patterns - A few of these a beginner tends to write out longhand before learning the built-in shortcut, roughly most to least common: - **Truthy checks instead of `len(x) > 0`** — test a collection directly; a non-empty list is already truthy @@ -311,36 +308,389 @@ A few of these a beginner tends to write out longhand before learning the built-
-## Additional best practices - -??? tip "Be creative with ASCII art" - Write in the terminal with bubble letters or draw images through creative character use. - - ```bash - ============================ - ,----, - ,-.----. ,/ .`| ,--, ,----.. ,--. - \ / \ ,` .' : ,--.'| / / \ ,--.'| - | : \ ,---, ; ; / ,--, | : / . : ,--,: : | - | | .\ : /_ ./|.'___,/ ,',---.'| : ' . / ;. \,`--.'`| ' : - . : |: | ,---, | ' :| : | | | : _' |. ; / ` ;| : : | | - | | \ :/___/ \. : |; |.'; ; : : |.' |; | ; \ ; |: | \ | : - | : . / . \ \ ,' '`----' | | | ' ' ; :| : | ; | '| : ' '; | - ; | |`-' \ ; ` ,' ' : ; ' | .'. |. | ' ' ' :' ' ;. ; - | | ; \ \ ' | | ' | | : | '' ; \; / || | | \ | - : ' | ' \ | ' : | ' : | : ; \ \ ', / ' : | ; .' - : : : \ ; ; ; |.' | | ' ,/ ; : / | | '`--' - | | : : \ \ '---' ; : ;--' \ \ .' ' : | - `---'.| \ ' ; | ,/ `---` ; |.' - `---` `--` '---' '---' - ============================ - Welcome to the program! - Press Enter: +## Polish + +The terminal is a **user interface**, and just like an app or website, it can be creatively designed within it's limitations to be more interactive, engaging, and readable. + +### Printing output + +#### Escape sequences + +An **escape sequence** is a backslash followed by a letter, standing in for a character that couldn't otherwise appear in the string. + +| Escape | Does | Shows up below in | +|---|---|---| +| `\n` | starts a new line | the [banner](#banners)'s greeting, printed on the line after the box | +| `\t` | inserts a tab | lining up columns of output (Foundations) | +| `\r` | returns the cursor to the start of the line, without moving down | redrawing a [progress bar](#progress-bars) in place | +| `\"`, `\'` | a literal quote character | a quote inside a string using the same quote mark | +| `\\` | a literal backslash | a Windows-style file path (Foundations) — the [original ASCII](#original-ascii) below sidesteps needing it with a raw string instead | + +#### Multi-line strings + +Here are three ways to print the same four-line string: + +- Multiple single quote `print("")` statements, they have an implicit `\n` at the end that puts each on a new line + + ```python + print("") + print("empty line above!") + print("and below...") + print("") + ``` + +- Escape character `\n` adds a new line + + ```python + print("\nempty line above!\nand below...\n") ``` - [ascii text resource](https://patorjk.com/software/taag/#p=display&f=Isometric1&t=Type+Something+&x=none&v=4&h=4&w=80&we=false) +- A triple-quoted string `print("""...""")` prints with every line break inside the quotes as typed - [ascii art resource](https://www.asciiart.eu/#google_vignette) + ```python + print(""" + empty line above! + and below... + """) + ``` -
+#### Formatting variables + +An [f-string](types.md#building-strings) — a variable's name dropped directly inside `{}` — is what turns the dashboard's bare `snake` dict into a filled-in box, and what plugs a typed-in name into the [banner](#banners)'s greeting. A [format spec](types.md#building-strings) inside that same `{}` controls how the value looks, built from these pieces in order: + +1. fill (padding character) +2. align (left, right, center, or pad between a sign and its digits) +3. sign (`-`, `+`, or space) +4. `0` (zero-pad shorthand) +5. width (minimum characters) +6. thousand separator (comma grouping) +7. precision (decimal digits) +8. type (`d`, `f`, `%`) + +An f-string can turn plain variables into a **dashboard**: + +```python +snake = {"species": "ball python", "length_ft": 4.5, "venomous": False} + +print(f""" +┌─────────────────────────────┐ +│ SNAKE RECORD │ +├─────────────────────────────┤ +│ Species {snake["species"]:<17}│ +│ Length ft {snake["length_ft"]:<17}│ +│ Venomous {str(snake["venomous"]):<17}│ +└─────────────────────────────┘ +""") +``` + +### Unicode symbols + +#### Original ASCII + +**ASCII** was the original 128 character encoding for computers, standardized in the 1960s — covering English letters, digits, and punctuation on a standard keyboard. Early console styling was built around using these characters to make **ascii text and art**. + +Building a [raw string](types.md#building-strings) with an `r` prefix (`r"""..."""`) makes this possible to print - so that Python doesn't mistake the backslashes `\` for meaningful escape characters. + +There are online tools to [convert text to ascii fonts](https://patorjk.com/software/taag/#p=display&f=Isometric1&t=Type+Something+&x=none&v=4&h=4&w=80&we=false) and [find ascii art](https://www.asciiart.eu/#google_vignette). + +```python +print(r""" + ____ _ _ ____ _ _ _____ _ _ +( _ \( \/ )(_ _)( )_( )( _ )( \( ) + )___/ \ / )( ) _ ( )(_)( ) ( +(__) (__) (__) (_) (_)(_____)(_)\_) +""") +``` + +#### Unicode expansion + +**Unicode** started in 1991 and replaced ASCII with a growing set: it started with the same 128 characters ASCII already had, and is now at 150,000 characters. Because it keeps growing, something built before a character existed may show a blank box or `?`. Python source files use UTF-8 which can represent every Unicode character — so any of these work in a Python file, although some editors and terminals may not *display* it correctly. + +Emoji are part of this too: the character itself (😀, 🐍) is a Unicode character, but the specific picture a device displays is drawn by each platform, which is why the same emoji looks different on iPhone vs. Android. + +Copy and paste these Unicode characters into your print statements, or [Browse the full set](https://unicode-table.com/en/): + +=== "Box-drawing" + + `┌` `─` `┐` `│` `├` `┤` `└` `┘` `┬` `┴` `┼` `╵` `╶` `╷` `╴` + + `╔` `═` `╗` `║` `╠` `╣` `╚` `╝` `╦` `╩` `╬` `╟` `╤` `╢` `╧` + + `╭` `╮` `╰` `╯` + + These were added to early character sets specifically so text terminals could draw frames and boxes: + +=== "Progress bars" + + `█` `▓` `▒` `░` + + `⠋` `⠙` `⠹` `⠸` `⠼` `⠴` `⠦` `⠧` `⠇` `⠏` + + `↺` `↻` `⟲` `⟳` + + Used for a timed spinner or loading bar for [progress bars](#progress-bars): + +=== "Arrows" + + `→` `➔` `➜` `←` `↑` `↓` + + `▶` `◀` `➤` `»` `›` `❯` `❮` `❱` `❰` + + `↳` `↲` `↰` `↱` `↵` `↴` `↪` `↩` + + `⮕` `⬅` `⬆` `⬇` + +=== "Checks and crosses" + + `✓` `✔` `☑` `✅` + + `✖` `✗` `✘` `☒` `𐄂` `❌` `❎` + +=== "Bullets" + + `•` `∙` `◉` `○` `◌` `◎` `●` `◦` `。` `☉` `⦾` `⦿` + + `◆` `◇` `◈` `♦` `⋄` `✦` `✧` + + `☸` `✱` `✲` `✳` + + `■` `□` `☐` `▪` + + `🔵` `🟢` `🟠` `🔴` `⚫` `🟤` `🟣` `⛔` + +=== "Special" + + `☺` `★` `☆` `©` `®` `™` `❤` `♡` `♥` + +### Input validation + +An `input()` is only as reliable as what it assumes the user will type. + +#### Wrong choice + +The below `while` loop keeps re-asking until the input is one of the allowed options: + +```python-ref +choice = input("> ") +while choice not in ("1", "2"): + print("Please enter 1 or 2.") + choice = input("> ") +``` + +#### Wrong type + +`input()` always returns a string, so when working with numbers it must be converted with `int()` or `float()`. However, this raises a `ValueError` if the user didn't type a number. Wrapping the conversion in [`try`/`except`](errors.md#catch-specific-exceptions) and re-asking on failure guards against input that's the wrong type. + +```python-ref +age = input("How old is this snake, in years? ") + +while True: + try: + age = int(age) + break + except ValueError: + age = input("Please enter a whole number: ") + +print(f"That's about {age * 7} in human years.") +``` + +Using a [string validate method](types.md#validate) is another other way to catch this — checking the string *before* converting it, instead of attempting the conversion and catching the failure after: +```python-ref +species = input("Enter a species name: ") + +while not species.isalpha(): + species = input("Letters only, try again: ") + +print(f"Logged: {species}") +``` + +### Menus + +Let the user pick from a short list of options with `input()` and a conditional. + +#### Single choice + +The options are printed first, so the input prompt doesn't need to repeat them — a bare `"> "` on it's own line is sometimes easier to see. + +```python-ref +print("You find a mysterious burmese python coiled in the grass.") +print("1. Approach it") +print("2. Back away slowly") + +choice = input("> ") +if choice == "1": + print("It doesn't move. Burmese pythons are famously calm.") +else: + print("You wisely continue on the trail.") +``` + +```bash +You find a mysterious burmese python coiled in the grass. +1. Approach it +2. Back away slowly +> 1 +It doesn't move. Burmese pythons are famously calm. +``` + +This choice isn't checked against anything — typing `3` still falls into `else`. Combine it with [input validation](#input-validation) above to re-ask until the user answers `1` or `2`. + +#### Repeating menu + +Wrap the same pattern in a `while` loop that reprints the menu and `break`s once the user's done, to keep offering choices instead of asking just once. + +```python-ref +while True: + print(""" +╔══════════════════════╗ +║ FIELD GUIDE MENU ║ +╠══════════════════════╣ +║ 1. Log a sighting ║ +║ 2. Look up a species ║ +║ 3. Quit ║ +╚══════════════════════╝ +""") + choice = input("> ") + if choice == "1": + print("Sighting logged.") + elif choice == "2": + print("Which species?") + elif choice == "3": + confirm = input("Are you sure? (y/n) ") + if confirm.strip().lower() == "y": + print("Goodbye!") + break +``` + +A quick confirmation before actually quitting keeps one wrong keypress from ending the whole program — comparing with [`.strip()`](types.md#modify) and `.lower()` means `"Y"`, `" y"`, and `"y"` all count as the same answer, instead of only an exact match. + +### Banners + +Draw a decorative box at the start of a program instead of a plain print statement — it can lead right into a prompt instead of standing alone, and combined with an f-string, a typed-in value gets inserted directly into the printed greeting. + +```python-ref +print(""" +╭────────────────────────╮ +│ THE SPECIES SCANNER™ │ +╰────────────────────────╯ +""") + +species = input("Enter a species: ") +print(f"\nScanning... {species} detected.") +print(f"Welcome to the field guide, {species}.") +``` + +#### Divider + +A row of repeated characters separates sections of output, without drawing a full box. + +```python +print("survey results") +print("=" * 40) +``` + +### Progress bars + +`time.sleep()` from the [time library](modules.md#import) pauses a program for a set number of seconds. Called in a loop between `print()` calls with [`end=""`](types.md#combine) to keep the cursor on the same line, it fakes a "loading" delay. + +```python-ref +import time + +print("Loading", end="") +for _ in range(3): + time.sleep(0.5) + print(".", end="") +print(" done!") +``` + +`end=""` never starts a new line, so this grows one dot at a time on the same line, half a second apart: + +```bash +Loading +Loading. +Loading.. +Loading... +Loading... done! +``` + +Print with [`end="\r"`](types.md#combine) instead, and each new line overwrites the last one instead of stacking below it — enough to build an animated progress bar out of characters. + +```python-ref +import time + +for i in range(10): + print("█" * i + "░" * (9 - i), end="\r") + time.sleep(0.1) +print("█" * 10) +``` + +```bash +░░░░░░░░░ +█░░░░░░░░ +██░░░░░░░ +███░░░░░░ +... +█████████ +██████████ +``` + +A fixed list of characters, indexed with `i % len(spinner)` so it wraps back to the start instead of running out, animates the same way — a spinner instead of a bar. + +```python-ref +import time + +spinner = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" +for i in range(20): + print(spinner[i % len(spinner)], end="\r") + time.sleep(0.1) +print("done!") +``` + +```bash +⠋ +⠙ +⠹ +⠸ +⠼ +⠴ +⠦ +⠧ +⠇ +⠏ +``` + +### Randomize messages + +[`random.choice()`](libraries/random.md) picks one item from a list at random, so it prints different messages every run. + +```python +import random + +responses = [ + "you got this!", + "excellent choice.", + "the python spirits approve.", + "interesting...", + "bold.", +] +print(random.choice(responses)) +``` + +Combined with `input()` and a conditional, the same idea lets a program react differently depending on what it's told, rather than just calculating and printing a result. + +```python-ref +import random + +name = input("What's your name? ") + +if name.strip().lower() == "python": + print("...you already know who I am.") +else: + responses = [ + "nice to meet you!", + "welcome to the field guide!", + "excellent name.", + ] + print(random.choice(responses)) +``` + diff --git a/docs/types.md b/docs/types.md index b063f6b..2b18c03 100644 --- a/docs/types.md +++ b/docs/types.md @@ -428,7 +428,15 @@ Strings use the same index and slice syntax as lists. `0` is the first character "-".join(["burmese", "python"]) # "burmese-python" ``` -#### f-strings +- **`print()`'s `sep` and `end` arguments** also take a string — `sep` replaces the space Python puts between multiple printed values (already covered on [Foundations](foundations.md#print-function)), and `end` replaces the newline `print()` adds after the last one, so the *next* `print()` call continues on the same line instead of starting a new one. + + ```python-ref + print("a", "b", sep="-") # "a-b" + print("a", end="") # no newline after — nothing printed on its own line yet + print("b") # "ab" — same line, since end="" skipped the newline + ``` + +#### Building strings - An **f-string** lets you embed variables directly inside `{}` and is a good choice once a string has multiple variables in it. Put a variable's name inside the `{}` and the variable's value will be inserted inside. @@ -439,58 +447,53 @@ Strings use the same index and slice syntax as lists. `0` is the first character print(f"{species} is {length} feet") # "ball is 5 feet" ``` -- **`.format()`** is the older way to build a string with embedded values — `{}` placeholders in the string are filled in with the arguments passed to `.format()`, in order, instead of reading variable names directly. - - ```python-ref - "{} {}".format(species, length) # "ball 5" - ``` - -- A **format spec** is an optional add-on *inside* one `{}` placeholder — it goes after the `value:` and controls how that value is displayed. It's built from **one or more optional** pieces, stacked together in this order: - - `{ value : [align] [sign] [width] [thousand separator ,] [.precision] [type] }` - - - **align** — `<` left, `>` right, or `^` center, aligns *within* the width, so it requires a specified width too. + - **`.format()`** is the older way to do the same thing — `{}` placeholders in the string are filled in with the arguments passed to `.format()`, in order, instead of reading variable names directly. ```python-ref - f"{length:<6}" # "5 " — left-aligned in 6 characters - f"{length:>6}" # " 5" — right-aligned in 6 characters - f"{length:^6}" # " 5 " — centered in 6 characters + "{} {}".format(species, length) # "ball 5" ``` - - **sign** — `-` is the default where only negative numbers get a sign, `+` forces a sign on every number, `=` forces the sign to the very front, before any zero-padding. + ??? tip "Filling in a template loaded from outside the code" + An f-string is evaluated the moment Python reads that line — the `f"..."` has to be written directly in the source file. A string loaded at runtime instead (from a file, an environment variable, a database) can't be turned into an f-string after the fact, since it was never written with the `f` prefix. `.format()` works on any string value, including one loaded this way, so it's still the right tool for filling in a template that isn't hardcoded into the script. - ```python-ref - f"{5:+}" # "+5" — always shows a sign - f"{-5:+}" # "-5" - f"{5:=+06}" # "+00005" — sign forced to the front, before the zero-padding - ``` + ```python-ref + template = "Species: {}, length: {} ft" # e.g. loaded from a config file + template.format(species, length) # "Species: ball, length: 5 ft" + ``` - - **width** — a plain number `width` pads the result to at least that many characters. + - A **format spec** is an optional add-on *inside* one `{}` placeholder in an f-string or `.format()`. It goes after the `value:` and controls how that value is displayed. Built from **one or more optional** pieces, stacked together in this order: - ```python-ref - f"{length:6}" # " 5" — padded to 6 characters wide - ``` + `{ value : [fill][align] [sign] [0] [width] [thousand separator ,] [.precision] [type] }` - - **thousands separator** - a `,` adds a comma to group every 3 digits. + Each piece accepts one of several valid options — the table below lists what each piece means, then every option it accepts as its own format spec, with a runnable example: - ```python-ref - f"{1234567.5:,}" # "1,234,567.5" - ``` + | Piece | Meaning | + |---|---| + | fill | Defaults to a space — only means anything paired with an align right after it.
Format specExampleOutput
`[any character, like *]``f"{length:*<6}"``"5*****"`
| + | align | Left, right, center, or — for a signed number — pad between the sign and the digits instead of outside them. Needs a width set too.
Format specExampleOutput
`<``f"{length:<6}"``"5 "`
`>``f"{length:>6}"``" 5"`
`^``f"{length:^6}"``" 5 "`
`=``f"{-5:=6}"``"- 5"`
| + | sign | `-` (default) signs only negatives, `+` signs every number, a space adds a leading space to positive numbers instead of nothing. Numbers only.
Format specExampleOutput
`-``f"{-5:-}"``"-5"`
`+``f"{5:+}"``"+5"`
` ` (space)`f"{5: }"``" 5"`
| + | 0 | A `0` right before the width zero-pads a number, automatically placing the zeros the same way `=` align would — a shorthand for `0=` fill+align together.
Format specExampleOutput
`0``f"{5:06}"``"000005"`
| + | width | Pads the result to at least that many characters.
Format specExampleOutput
a number`f"{length:6}"``" 5"`
| + | thousand separator | Groups every 3 digits with a comma. Numbers only.
Format specExampleOutput
`,``f"{1234567.5:,}"``"1,234,567.5"`
| + | precision | Digits after the decimal for an `f`/`%` type; defaults to 6. Not valid with `d`.
Format specExampleOutput
`.digits``f"{length_ft:.2f}"``"4.50"`
| + | type | `d` integer, `f` fixed-point, `%` percentage.
Format specExampleOutput
`d``f"{length:d}"``"5"`
`f``f"{length_ft:f}"``"4.500000"`
`%``f"{0.25:.1%}"``"25.0%"`
| - - **precision** — a `.` followed by a number `.digits` sets how many digits appear after the decimal point in a `f` and `%` type (detailed below), without a precision they default to 6 decimal places. + One gotcha specific to booleans: `bool` is secretly a subtype of `int`, so a bare `False` run through a format spec is treated as the number `0` and prints `"0"` instead of the word. Wrapping it in [`str()`](#convert_2) first converts it to text before the format spec ever sees it. ```python-ref - f"{length_ft:.2f}" # "4.50" — 2 digits after the decimal + venomous = False + + f"{venomous:<10}" # "0 " — treated as the int 0 + f"{str(venomous):<10}" # "False " — converted to text first ``` - - **type** — a letter at the very end — tells Python how to display the value: `d` for an integer, `f` for fixed-point notation (displayed with decimal places), `%` for a percentage. +- A **raw string** — `r"..."` — turns off escape-sequence processing, so a backslash stays a literal backslash instead of starting an escape sequence like `\n`. Combine the two prefixes as `rf"..."` for an f-string that's also raw. - ```python-ref - f"{length:d}" # "5" — treated as an integer - f"{length_ft:.2f}" # "4.50" — fixed-point notation, 2 decimal places - f"{length_ft:f}" # "4.500000" — no precision given, defaults to 6 digits - f"{0.25:.1%}" # "25.0%" — treated as a percentage - ``` + ```python-ref + print("C:\new_folder") # \n is read as an escape sequence — starts a new line + print(r"C:\new_folder") # r"..." keeps the backslash literal — no escape processing + print(rf"C:\{species}") # raw and an f-string together + ``` #### Modify diff --git a/includes/glossary.md b/includes/glossary.md index 9aa7595..d40ed23 100644 --- a/includes/glossary.md +++ b/includes/glossary.md @@ -68,3 +68,4 @@ *[lazily]: In a way that computes or produces a value only at the moment it's actually needed, instead of all at once ahead of time *[Lazily]: In a way that computes or produces a value only at the moment it's actually needed, instead of all at once ahead of time *[PascalCase]: Capitalizing each word with no separators (e.g. Snake, BallPython) — the naming convention for classes, unlike variables' snake_case +*[UTF-8]: The encoding used to save a Python file by default — the scheme that turns each Unicode character into the actual bytes a computer stores and reads, and it can represent every character Unicode defines