[{"data":1,"prerenderedAt":1514},["ShallowReactive",2],{"page-\u002Fpython\u002F28-exercises-and-projects":3},{"id":4,"title":5,"body":6,"description":16,"extension":1507,"meta":1508,"navigation":1509,"path":1510,"seo":1511,"stem":1512,"__hash__":1513},"content\u002Fpython\u002F28-exercises-and-projects.md","28 — Exercises & Projects",{"type":7,"value":8,"toc":1469},"minimark",[9,13,17,22,25,48,52,57,65,72,197,212,216,235,239,261,265,284,288,292,303,307,322,326,333,337,386,407,414,438,442,446,471,475,489,493,518,522,540,544,551,559,565,569,573,694,698,859,863,1130,1134,1137,1198,1210,1214,1246,1250,1298,1302,1305,1425,1428,1432,1449,1459,1462,1465],[10,11,5],"h1",{"id":12},"_28-exercises-projects",[14,15,16],"p",{},"Reading is the first step; building is where you become a pro. This chapter gives you exercises and projects calibrated to internalize everything from chapters 01 through 27.",[18,19,21],"h2",{"id":20},"how-to-use-this-chapter","How to Use This Chapter",[14,23,24],{},"Each section has:",[26,27,28,36,42],"ul",{},[29,30,31,35],"li",{},[32,33,34],"strong",{},"Exercises",": small, focused tasks to test specific concepts.",[29,37,38,41],{},[32,39,40],{},"Projects",": end-to-end builds with concrete requirements and stretch goals.",[29,43,44,47],{},[32,45,46],{},"Solutions",": don't look anything up until you've tried for at least 30 minutes.",[18,49,51],{"id":50},"beginner-exercises","Beginner Exercises",[53,54,56],"h3",{"id":55},"_1-fizzbuzz-with-comprehensions","1. FizzBuzz with Comprehensions",[14,58,59,60,64],{},"Implement FizzBuzz for ",[61,62,63],"code",{},"1..=100"," as a single list comprehension producing strings, using a conditional expression (chapter 04, 09).",[53,66,68,69],{"id":67},"_2-stack-with-__slots__","2. Stack with ",[61,70,71],{},"__slots__",[73,74,76],"code-wrapper",{"language":75},"python",[77,78,82],"pre",{"className":79,"code":80,"language":75,"meta":81,"style":81},"language-python shiki shiki-themes github-light github-dark","class Stack:\n    __slots__ = (\"_items\",)\n    def __init__(self): ...\n    def push(self, item): ...\n    def pop(self): ...\n    def peek(self): ...\n    def __len__(self): ...\n    def __bool__(self): ...\n","",[61,83,84,101,121,136,149,161,173,185],{"__ignoreMap":81},[85,86,89,93,97],"span",{"class":87,"line":88},"line",1,[85,90,92],{"class":91},"svdQ7","class",[85,94,96],{"class":95},"sIsaT"," Stack",[85,98,100],{"class":99},"ssxIu",":\n",[85,102,104,108,111,114,118],{"class":87,"line":103},2,[85,105,107],{"class":106},"snvgF","    __slots__",[85,109,110],{"class":91}," =",[85,112,113],{"class":99}," (",[85,115,117],{"class":116},"sJ6F3","\"_items\"",[85,119,120],{"class":99},",)\n",[85,122,124,127,130,133],{"class":87,"line":123},3,[85,125,126],{"class":91},"    def",[85,128,129],{"class":106}," __init__",[85,131,132],{"class":99},"(self): ",[85,134,135],{"class":106},"...\n",[85,137,139,141,144,147],{"class":87,"line":138},4,[85,140,126],{"class":91},[85,142,143],{"class":95}," push",[85,145,146],{"class":99},"(self, item): ",[85,148,135],{"class":106},[85,150,152,154,157,159],{"class":87,"line":151},5,[85,153,126],{"class":91},[85,155,156],{"class":95}," pop",[85,158,132],{"class":99},[85,160,135],{"class":106},[85,162,164,166,169,171],{"class":87,"line":163},6,[85,165,126],{"class":91},[85,167,168],{"class":95}," peek",[85,170,132],{"class":99},[85,172,135],{"class":106},[85,174,176,178,181,183],{"class":87,"line":175},7,[85,177,126],{"class":91},[85,179,180],{"class":106}," __len__",[85,182,132],{"class":99},[85,184,135],{"class":106},[85,186,188,190,193,195],{"class":87,"line":187},8,[85,189,126],{"class":91},[85,191,192],{"class":106}," __bool__",[85,194,132],{"class":99},[85,196,135],{"class":106},[14,198,199,200,203,204,207,208,211],{},"Back it with a ",[61,201,202],{},"list",". Implement ",[61,205,206],{},"__iter__"," so ",[61,209,210],{},"for x in stack"," yields items top-to-bottom without mutating the stack (chapter 14, 15, 19).",[53,213,215],{"id":214},"_3-custom-exception-hierarchy","3. Custom Exception Hierarchy",[14,217,218,219,222,223,226,227,230,231,234],{},"Write ",[61,220,221],{},"parse_csv_row(row: str) -> list[int]"," that raises a custom ",[61,224,225],{},"CSVParseError"," (with the offending row and column attached) rather than letting a bare ",[61,228,229],{},"ValueError"," propagate. Use ",[61,232,233],{},"raise ... from"," to preserve the original exception (chapter 16).",[53,236,238],{"id":237},"_4-linked-list","4. Linked List",[14,240,241,242,245,246,249,250,252,253,256,257,260],{},"Implement a singly linked list using a ",[61,243,244],{},"Node"," dataclass and ",[61,247,248],{},"Optional[Node]"," links. Then implement ",[61,251,206],{},", ",[61,254,255],{},"__len__",", and ",[61,258,259],{},"__repr__"," (chapter 09, 12, 14, 20).",[53,262,264],{"id":263},"_5-cli-calculator","5. CLI Calculator",[14,266,267,268,271,272,275,276,279,280,283],{},"Take ",[61,269,270],{},"+ 2 3"," or ",[61,273,274],{},"* 4 5"," from ",[61,277,278],{},"sys.argv",", print the result. Handle malformed input with a helpful error message instead of a raw traceback. Add an ",[61,281,282],{},"argparse","-based version as a stretch goal (chapter 05, 16).",[18,285,287],{"id":286},"intermediate-exercises","Intermediate Exercises",[53,289,291],{"id":290},"_6-json-ish-parser","6. JSON-ish Parser",[14,293,294,295,298,299,302],{},"Hand-write a parser for a JSON subset (objects, arrays, strings, numbers, booleans, ",[61,296,297],{},"null",") using recursive descent — no ",[61,300,301],{},"json"," module allowed. Cover nested structures and escaped characters in strings (chapter 06, 16).",[53,304,306],{"id":305},"_7-async-file-watcher","7. Async File Watcher",[14,308,309,310,313,314,317,318,321],{},"Poll a directory for new\u002Fmodified files using ",[61,311,312],{},"asyncio"," and ",[61,315,316],{},"pathlib",", printing events as they occur. Support clean shutdown on ",[61,319,320],{},"Ctrl+C"," via a cancellation-aware loop (chapter 19, 22).",[53,323,325],{"id":324},"_8-concurrent-counter-with-threads","8. Concurrent Counter with Threads",[14,327,328,329,332],{},"Spawn 10 threads, each incrementing a shared counter 100,000 times. Demonstrate the race condition with no lock, then fix it with ",[61,330,331],{},"threading.Lock",". Measure and report the difference in final (wrong vs. correct) totals (chapter 21).",[53,334,336],{"id":335},"_9-custom-iterator-for-fibonacci","9. Custom Iterator for Fibonacci",[73,338,339],{"language":75},[77,340,342],{"className":79,"code":341,"language":75,"meta":81,"style":81},"class Fibonacci:\n    def __init__(self, limit): ...\n    def __iter__(self): ...\n    def __next__(self): ...\n",[61,343,344,353,364,375],{"__ignoreMap":81},[85,345,346,348,351],{"class":87,"line":88},[85,347,92],{"class":91},[85,349,350],{"class":95}," Fibonacci",[85,352,100],{"class":99},[85,354,355,357,359,362],{"class":87,"line":103},[85,356,126],{"class":91},[85,358,129],{"class":106},[85,360,361],{"class":99},"(self, limit): ",[85,363,135],{"class":106},[85,365,366,368,371,373],{"class":87,"line":123},[85,367,126],{"class":91},[85,369,370],{"class":106}," __iter__",[85,372,132],{"class":99},[85,374,135],{"class":106},[85,376,377,379,382,384],{"class":87,"line":138},[85,378,126],{"class":91},[85,380,381],{"class":106}," __next__",[85,383,132],{"class":99},[85,385,135],{"class":106},[14,387,388,389,392,393,396,397,252,400,256,403,406],{},"Make it a proper iterator (raises ",[61,390,391],{},"StopIteration"," at ",[61,394,395],{},"limit","), then chain it through ",[61,398,399],{},"itertools.islice",[61,401,402],{},"map",[61,404,405],{},"filter"," (chapter 09, 19).",[53,408,410,411],{"id":409},"_10-type-state-builder-with-protocol","10. Type-State Builder with ",[61,412,413],{},"Protocol",[14,415,416,417,420,421,252,424,256,427,430,431,433,434,437],{},"Design a ",[61,418,419],{},"QueryBuilder"," where ",[61,422,423],{},".select(...)",[61,425,426],{},".where(...)",[61,428,429],{},".build()"," are only valid in certain call orders, enforced via type hints and ",[61,432,413],{}," classes (not runtime checks). Verify the intended misuse fails ",[61,435,436],{},"mypy",", not just at runtime (chapter 20).",[18,439,441],{"id":440},"advanced-exercises","Advanced Exercises",[53,443,445],{"id":444},"_11-custom-descriptor-based-orm-field","11. Custom Descriptor-Based ORM Field",[14,447,448,449,452,453,252,456,252,459,462,463,466,467,470],{},"Implement a ",[61,450,451],{},"Field"," descriptor (",[61,454,455],{},"__set_name__",[61,457,458],{},"__get__",[61,460,461],{},"__set__",") that validates type and range on assignment, used inside a small ",[61,464,465],{},"Model"," base class with ",[61,468,469],{},"__init_subclass__"," collecting all fields into a registry (chapter 15, 23).",[53,472,474],{"id":473},"_12-metaclass-based-plugin-registry","12. Metaclass-Based Plugin Registry",[14,476,477,478,481,482,485,486,488],{},"Build a ",[61,479,480],{},"PluginMeta"," metaclass that auto-registers every subclass of ",[61,483,484],{},"PluginBase"," into a class-level registry, then compare the same behavior implemented with ",[61,487,469],{}," instead. Write a short note on which approach you'd ship to production and why (chapter 23).",[53,490,492],{"id":491},"_13-thread-safe-lru-cache-from-scratch","13. Thread-Safe LRU Cache From Scratch",[14,494,495,496,499,500,503,504,506,507,510,511,514,515,517],{},"Implement your own bounded LRU cache (no ",[61,497,498],{},"functools.lru_cache",") using ",[61,501,502],{},"collections.OrderedDict"," and a ",[61,505,331],{},", safe for concurrent ",[61,508,509],{},"get","\u002F",[61,512,513],{},"put"," from multiple threads. Benchmark against ",[61,516,498],{}," for the single-threaded case (chapter 21, 26).",[53,519,521],{"id":520},"_14-async-rate-limited-api-client","14. Async Rate-Limited API Client",[14,523,524,525,528,529,271,532,535,536,539],{},"Implement an ",[61,526,527],{},"async"," HTTP client wrapper (using ",[61,530,531],{},"aiohttp",[61,533,534],{},"httpx",") that limits concurrent requests via ",[61,537,538],{},"asyncio.Semaphore"," and retries transient failures with exponential backoff. Test it against a local mock server, not a real API (chapter 22, 24).",[53,541,543],{"id":542},"_15-memory-profiler-for-your-own-code","15. Memory Profiler for Your Own Code",[14,545,546,547,550],{},"Use ",[61,548,549],{},"tracemalloc"," to snapshot memory before\u002Fafter a function call, diff the snapshots, and print the top 5 lines responsible for allocation growth. Apply it to one of your earlier exercises and report a real finding (chapter 26).",[53,552,554,555,558],{"id":553},"_16-c-extension-via-ctypes-or-cython","16. C Extension via ",[61,556,557],{},"ctypes"," or Cython",[14,560,561,562,564],{},"Write a small numeric function (e.g., a tight loop computing a checksum) in C, call it from Python via ",[61,563,557],{},", then rewrite the same function in Cython and benchmark both against a pure-Python version (chapter 26).",[18,566,568],{"id":567},"project-ideas","Project Ideas",[53,570,572],{"id":571},"beginner","Beginner",[574,575,576,624,646,668],"ol",{},[29,577,578,581,582,252,585,252,587,252,590,593,594,510,596,598,599,271,601,604,605],{},[32,579,580],{},"CLI todo app",": ",[61,583,584],{},"add",[61,586,202],{},[61,588,589],{},"done",[61,591,592],{},"remove"," subcommands. JSON file storage via ",[61,595,316],{},[61,597,301],{},". Use ",[61,600,282],{},[61,602,603],{},"click",".\n",[26,606,607,614],{},[29,608,609,613],{},[610,611,612],"em",{},"Requirements",": persists between runs, handles a missing\u002Fcorrupt storage file gracefully, exit codes reflect success\u002Ffailure.",[29,615,616,619,620,623],{},[610,617,618],{},"Stretch",": due dates with ",[61,621,622],{},"datetime",", priority levels, colored terminal output.",[29,625,626,629,630],{},[32,627,628],{},"Word frequency counter",": read a text file, print the top 10 most common words.\n",[26,631,632,641],{},[29,633,634,636,637,640],{},[610,635,612],{},": case-insensitive, strips punctuation, uses ",[61,638,639],{},"collections.Counter",".",[29,642,643,645],{},[610,644,618],{},": exclude a configurable stopword list; handle files too large to fit in memory by streaming.",[29,647,648,651,652],{},[32,649,650],{},"Markdown to HTML converter",": minimal converter for headers, bold\u002Fitalic, links, and lists.\n",[26,653,654,663],{},[29,655,656,658,659,662],{},[610,657,612],{},": uses ",[61,660,661],{},"re"," or hand-written parsing, no external Markdown library.",[29,664,665,667],{},[610,666,618],{},": nested lists, code blocks with syntax-aware escaping.",[29,669,670,673,674],{},[32,671,672],{},"Unit converter CLI",": temperature, length, weight conversions.\n",[26,675,676,685],{},[29,677,678,680,681,684],{},[610,679,612],{},": rejects invalid units with a clear error message, uses ",[61,682,683],{},"Decimal"," where precision matters.",[29,686,687,689,690,693],{},[610,688,618],{},": a REPL mode using ",[61,691,692],{},"cmd"," from the standard library.",[53,695,697],{"id":696},"intermediate","Intermediate",[574,699,700,728,764,788,812,834],{"start":151},[29,701,702,705,706],{},[32,703,704],{},"REST API with FastAPI",": a small \"bookmarks\" service — create, list, delete, search bookmarks.\n",[26,707,708,723],{},[29,709,710,581,712,715,716,252,719,722],{},[610,711,612],{},[61,713,714],{},"pydantic"," models for request\u002Fresponse validation, SQLite via ",[61,717,718],{},"sqlalchemy",[61,720,721],{},"pytest"," test suite with a test database fixture.",[29,724,725,727],{},[610,726,618],{},": pagination, full-text search, JWT-based auth.",[29,729,730,733,734],{},[32,731,732],{},"Web scraper with rate limiting",": scrape a public site's listing pages politely.\n",[26,735,736,754],{},[29,737,738,581,740,510,743,745,746,749,750,753],{},[610,739,612],{},[61,741,742],{},"requests",[61,744,534],{}," + ",[61,747,748],{},"beautifulsoup4",", respects ",[61,751,752],{},"robots.txt",", configurable delay between requests, retries with backoff.",[29,755,756,758,759,745,761,763],{},[610,757,618],{},": convert to ",[61,760,312],{},[61,762,531],{}," for concurrent (still rate-limited) fetching; compare wall-clock time.",[29,765,766,769,770],{},[32,767,768],{},"Log analysis tool",": parse a large web server access log, report top IPs, status code distribution, and slow endpoints.\n",[26,771,772,780],{},[29,773,774,776,777,779],{},[610,775,612],{},": streams the file (never loads it fully into memory), uses ",[61,778,661],{}," or a proper log-format parser, outputs a summary table.",[29,781,782,784,785,640],{},[610,783,618],{},": process multiple gigabyte-scale log files in parallel using ",[61,786,787],{},"multiprocessing",[29,789,790,793,794],{},[32,791,792],{},"Task queue with workers",": a producer enqueues jobs; multiple worker processes consume and execute them.\n",[26,795,796,807],{},[29,797,798,581,800,271,803,806],{},[610,799,612],{},[61,801,802],{},"multiprocessing.Queue",[61,804,805],{},"redis","-backed queue, graceful shutdown, at-least-once job execution semantics documented.",[29,808,809,811],{},[610,810,618],{},": a simple retry\u002Fdead-letter mechanism for jobs that repeatedly fail.",[29,813,814,817,818,821,822],{},[32,815,816],{},"Database migration tool",": apply\u002Frollback schema migrations tracked by version number, similar in spirit to ",[61,819,820],{},"alembic"," but hand-built.\n",[26,823,824,829],{},[29,825,826,828],{},[610,827,612],{},": tracks applied migrations in a metadata table, supports up\u002Fdown migrations, is idempotent (running twice is safe).",[29,830,831,833],{},[610,832,618],{},": dry-run mode that prints the SQL without executing it.",[29,835,836,839,840,843,844],{},[32,837,838],{},"Config management library",": load configuration from environment variables, a ",[61,841,842],{},".env"," file, and a YAML\u002FTOML file, with a defined precedence order.\n",[26,845,846,854],{},[29,847,848,850,851,853],{},[610,849,612],{},": validated via ",[61,852,714],{}," (or dataclasses + manual validation), clear error messages for missing required values.",[29,855,856,858],{},[610,857,618],{},": hot-reload on file change using the async file watcher from Exercise 7.",[53,860,862],{"id":861},"advanced","Advanced",[574,864,866,890,908,934,959,984,1007,1043,1065,1089],{"start":865},11,[29,867,868,871,872],{},[32,869,870],{},"Async web framework mini-clone",": implement a minimal ASGI-compatible framework — routing, middleware, request\u002Fresponse objects.\n",[26,873,874,882],{},[29,875,876,878,879,640],{},[610,877,612],{},": handles path parameters, at least one middleware (logging or auth), runs under ",[61,880,881],{},"uvicorn",[29,883,884,886,887,640],{},[610,885,618],{},": dependency injection similar to FastAPI's ",[61,888,889],{},"Depends",[29,891,892,895,896],{},[32,893,894],{},"Distributed task scheduler",": cron-like scheduler that distributes jobs across multiple worker processes\u002Fmachines.\n",[26,897,898,903],{},[29,899,900,902],{},[610,901,612],{},": jobs survive a worker crash (persisted state, not in-memory only), no job runs twice concurrently.",[29,904,905,907],{},[610,906,618],{},": a small web dashboard (via FastAPI) showing job history and status.",[29,909,910,913,914,917,918],{},[32,911,912],{},"Custom ORM",": descriptor-based fields, a ",[61,915,916],{},"QuerySet","-like lazy query builder, migrations.\n",[26,919,920,925],{},[29,921,922,924],{},[610,923,612],{},": supports at least SQLite, lazy evaluation (a query only hits the database when iterated\u002Fmaterialized), parameterized queries only — no string-formatted SQL anywhere.",[29,926,927,929,930,933],{},[610,928,618],{},": a basic connection pool; support for ",[61,931,932],{},"JOIN","-like relationships between models.",[29,935,936,939,940],{},[32,937,938],{},"Static site generator",": Markdown → HTML with templates, front matter, and an RSS feed, similar in spirit to this very site.\n",[26,941,942,950],{},[29,943,944,658,946,949],{},[610,945,612],{},[61,947,948],{},"jinja2"," for templating, supports nested content directories, incremental rebuilds (only regenerate changed files).",[29,951,952,954,955,958],{},[610,953,618],{},": a ",[61,956,957],{},"watch"," mode with live-reload using the async file watcher pattern from Exercise 7.",[29,960,961,964,965,510,968,971,972],{},[32,962,963],{},"Package vulnerability scanner",": given a ",[61,966,967],{},"requirements.txt",[61,969,970],{},"pyproject.toml",", cross-reference installed versions against a vulnerability database and report findings.\n",[26,973,974,979],{},[29,975,976,978],{},[610,977,612],{},": parses both dependency file formats, calls a real advisory API (or a downloaded OSV dataset), produces a clear pass\u002Ffail report.",[29,980,981,983],{},[610,982,618],{},": a GitHub Action that runs it automatically on every pull request.",[29,985,986,989,990],{},[32,987,988],{},"Real-time chat server",": WebSocket-based, multiple rooms, message history.\n",[26,991,992,1002],{},[29,993,994,581,996,745,998,1001],{},[610,995,612],{},[61,997,312],{},[61,999,1000],{},"websockets"," or FastAPI's WebSocket support, handles disconnects gracefully, broadcasts to all clients in a room.",[29,1003,1004,1006],{},[610,1005,618],{},": horizontal scaling across multiple server processes using Redis pub\u002Fsub for cross-process broadcast.",[29,1008,1009,1015,1016,1019,1020,1022,1023],{},[32,1010,1011,1012,1014],{},"Type checker plugin or ",[61,1013,436],{}," stub package",": write ",[61,1017,1018],{},".pyi"," stub files for an untyped third-party library you use often, or a small ",[61,1021,436],{}," plugin that catches one specific project-specific mistake.\n",[26,1024,1025,1034],{},[29,1026,1027,1029,1030,1033],{},[610,1028,612],{},": stubs pass ",[61,1031,1032],{},"mypy --strict"," on a real consuming file; documents at least 3 real functions\u002Fclasses accurately.",[29,1035,1036,1038,1039,1042],{},[610,1037,618],{},": publish the stub package to PyPI as a ",[61,1040,1041],{},"types-\u003Cpackage>"," package.",[29,1044,1045,1048,1049,1052,1053],{},[32,1046,1047],{},"Profiler-guided optimization case study",": take one of your own earlier, slower projects (the web scraper or log analyzer are good candidates), profile it with ",[61,1050,1051],{},"cProfile",", and optimize it through at least three iterations.\n",[26,1054,1055,1060],{},[29,1056,1057,1059],{},[610,1058,612],{},": a before\u002Fafter benchmark with real numbers, a short written note per iteration explaining what changed and why it helped (or didn't).",[29,1061,1062,1064],{},[610,1063,618],{},": rewrite the single hottest function in Cython or Numba and measure the final delta.",[29,1066,1067,1070,1071],{},[32,1068,1069],{},"Secrets-safe configuration loader",": a library that loads secrets from environment variables or a secrets manager (AWS Secrets Manager, HashiCorp Vault, or a local encrypted file), never logging or accidentally serializing secret values.\n",[26,1072,1073,1084],{},[29,1074,1075,1077,1078,510,1080,1083],{},[610,1076,612],{},": secret values use a wrapper type whose ",[61,1079,259],{},[61,1081,1082],{},"__str__"," redact the value, unit tests confirm no secret ever appears in a log line or exception traceback.",[29,1085,1086,1088],{},[610,1087,618],{},": automatic secret rotation support with a callback on rotation.",[29,1090,1091,1094,1095],{},[32,1092,1093],{},"End-to-end capstone",": combine at least four earlier projects into one cohesive application — for example, the CLI todo app (1) backed by the custom ORM (13), exposed via the async web framework (11), with dependencies checked by the vulnerability scanner (15).\n",[26,1096,1097,1121],{},[29,1098,1099,1101,1102,1104,1105,1107,1108,1111,1112,252,1115,256,1117,1120],{},[610,1100,612],{},": a real ",[61,1103,970],{}," with proper dependency groups, a ",[61,1106,721],{}," suite with meaningful coverage (not just executed-line coverage — see chapter 24), a ",[61,1109,1110],{},"README"," explaining how to run it, and a CI pipeline (GitHub Actions) running tests, ",[61,1113,1114],{},"ruff",[61,1116,436],{},[61,1118,1119],{},"pip-audit"," on every push.",[29,1122,1123,1125,1126,1129],{},[610,1124,618],{},": containerize it with a ",[61,1127,1128],{},"Dockerfile",", and deploy it somewhere reachable over the internet.",[18,1131,1133],{"id":1132},"reading-code-to-mastery","Reading Code to Mastery",[14,1135,1136],{},"Read source of:",[26,1138,1139,1159,1164,1173,1178,1183,1188],{},[29,1140,1141,1144,1145,1148,1149,252,1152,252,1155,1158],{},[61,1142,1143],{},"cpython"," itself (",[61,1146,1147],{},"Lib\u002F"," for pure-Python standard library modules — ",[61,1150,1151],{},"collections",[61,1153,1154],{},"itertools",[61,1156,1157],{},"functools"," are approachable starting points).",[29,1160,1161,1163],{},[61,1162,742],{}," (a widely-imitated, readable HTTP client design).",[29,1165,1166,271,1169,1172],{},[61,1167,1168],{},"flask",[61,1170,1171],{},"fastapi"," (routing, request\u002Fresponse lifecycle, decorators in production use).",[29,1174,1175,1177],{},[61,1176,718],{}," (descriptor-heavy ORM internals — directly relevant after chapter 15's descriptor material).",[29,1179,1180,1182],{},[61,1181,721],{}," (plugin architecture, fixture resolution — dense but hugely rewarding).",[29,1184,1185,1187],{},[61,1186,603],{}," (decorator-based CLI construction, a masterclass in ergonomic API design).",[29,1189,1190,1193,1194,1197],{},[61,1191,1192],{},"attrs"," or the standard library's own ",[61,1195,1196],{},"dataclasses"," module (class-generation metaprogramming, chapter 23 territory).",[14,1199,1200,1201,252,1204,1206,1207,640],{},"The standard library is the best Python code you can read for free. Start with ",[61,1202,1203],{},"collections\u002F__init__.py",[61,1205,1154],{}," (implemented in C, but its documentation doubles as a spec you can reimplement in pure Python as an exercise), and ",[61,1208,1209],{},"contextlib",[18,1211,1213],{"id":1212},"practice-sites","Practice Sites",[26,1215,1216,1222,1228,1234,1240],{},[29,1217,1218,1221],{},[32,1219,1220],{},"Exercism Python track",": mentor-reviewed exercises, strong on idiomatic style feedback.",[29,1223,1224,1227],{},[32,1225,1226],{},"Advent of Code",": annual puzzles; a great forcing function for algorithms plus clean code under time pressure.",[29,1229,1230,1233],{},[32,1231,1232],{},"LeetCode \u002F HackerRank",": algorithmic practice, useful for interview preparation specifically.",[29,1235,1236,1239],{},[32,1237,1238],{},"Project Euler",": math-flavored problems that reward writing genuinely efficient code, not just correct code.",[29,1241,1242,1245],{},[32,1243,1244],{},"CodeWars (Python kata)",": short, focused katas across a huge range of difficulty.",[18,1247,1249],{"id":1248},"open-source-contribution","Open Source Contribution",[26,1251,1252,1268,1274,1283,1289],{},[29,1253,1254,252,1257,252,1260,1263,1264,1267],{},[61,1255,1256],{},"psf\u002Frequests",[61,1258,1259],{},"pallets\u002Fflask",[61,1261,1262],{},"tiangolo\u002Ffastapi",": widely used, well-documented codebases with active ",[61,1265,1266],{},"good first issue"," labels.",[29,1269,1270,1273],{},[61,1271,1272],{},"pytest-dev\u002Fpytest",": testing infrastructure used by nearly the entire ecosystem.",[29,1275,1276,1279,1280,1282],{},[61,1277,1278],{},"python\u002Fcpython",": the interpreter itself — a serious undertaking, but ",[61,1281,1266],{},"-labeled documentation and test fixes are approachable entry points.",[29,1284,1285,1288],{},[61,1286,1287],{},"django\u002Fdjango",": a large, mature, well-governed codebase; excellent for learning how a big project structures itself.",[29,1290,1291,271,1294,1297],{},[61,1292,1293],{},"astral-sh\u002Fruff",[61,1295,1296],{},"astral-sh\u002Fuv",": Rust-implemented but Python-ecosystem-facing tooling, if you're curious how the fastest Python tools are actually built.",[18,1299,1301],{"id":1300},"mastery-self-check","Mastery Self-Check",[14,1303,1304],{},"Can you confidently:",[26,1306,1307,1314,1325,1335,1355,1368,1382,1392,1402,1409,1415],{},[29,1308,1309,1310,1313],{},"Explain why ",[61,1311,1312],{},"def f(x, items=[])"," is dangerous, without looking it up? (Default arguments are evaluated once, at function definition time, and shared across all calls.)",[29,1315,1316,1317,1320,1321,1324],{},"Predict the output of ",[61,1318,1319],{},"0.1 + 0.2 == 0.3","? (",[61,1322,1323],{},"False"," — binary floating-point can't represent 0.1 or 0.2 exactly.)",[29,1326,1327,1328,313,1331,1334],{},"Explain the difference between ",[61,1329,1330],{},"__getattr__",[61,1332,1333],{},"__getattribute__","? (Fallback-on-miss vs. every access, unconditionally.)",[29,1336,1337,1338,252,1341,256,1343,1345,1346,1348,1349,1351,1352,1354],{},"Choose correctly between ",[61,1339,1340],{},"threading",[61,1342,787],{},[61,1344,312],{}," for a given workload? (I\u002FO-bound + many tasks: ",[61,1347,312],{},"; I\u002FO-bound + simpler blocking code: ",[61,1350,1340],{},"; CPU-bound: ",[61,1353,787],{},".)",[29,1356,1309,1357,313,1360,1363,1364,1367],{},[61,1358,1359],{},"is",[61,1361,1362],{},"=="," sometimes agree on small integers and sometimes don't? (CPython caches small ints ",[61,1365,1366],{},"-5..256","; identity comparison on larger ints is implementation-dependent, never guaranteed.)",[29,1369,1370,1371,510,1374,1377,1378,1381],{},"Write a context manager two different ways — a class with ",[61,1372,1373],{},"__enter__",[61,1375,1376],{},"__exit__",", and a generator with ",[61,1379,1380],{},"@contextmanager","? (Both, and know when each is preferable.)",[29,1383,1309,1384,1387,1388,1391],{},[61,1385,1386],{},"pickle.loads"," on untrusted data is a security vulnerability, precisely? (",[61,1389,1390],{},"__reduce__"," can specify arbitrary code to execute during deserialization.)",[29,1393,1394,1395,1398,1399,1401],{},"Read a ",[61,1396,1397],{},"Generic[T]"," class and a ",[61,1400,413],{}," and explain the difference in what they express? (Concrete generic type vs. structural\u002Fduck-typed interface.)",[29,1403,1404,1405,1408],{},"Explain what the GIL actually prevents, and what it doesn't? (Prevents true parallel ",[610,1406,1407],{},"bytecode"," execution across threads in one process; doesn't prevent I\u002FO-bound concurrency or multiprocessing parallelism.)",[29,1410,1411,1412,1414],{},"Write a metaclass and explain when ",[61,1413,469],{}," would have been enough instead? (Yes — most of the time.)",[29,1416,1417,1418,313,1421,1424],{},"Profile a slow function and explain the difference between ",[61,1419,1420],{},"tottime",[61,1422,1423],{},"cumtime","? (Time in the function alone vs. including everything it calls.)",[14,1426,1427],{},"If you can do all of the above without consulting docs, you're a pro Python developer.",[18,1429,1431],{"id":1430},"final-words","Final Words",[14,1433,1434,1435,1438,1439,1441,1442,1444,1445,1448],{},"Python's readability is deceptive — it makes advanced code ",[610,1436,1437],{},"look"," simple, which is exactly why so many of its real gotchas (mutable defaults, ",[61,1440,1359],{}," vs ",[61,1443,1362],{},", the GIL, decorator\u002Fdescriptor interactions, ",[61,1446,1447],{},"pickle","'s trust assumptions) surprise experienced developers coming from other languages, and even Python developers who never pushed past the basics.",[14,1450,1451,1452,1454,1455,1458],{},"Build things. Break things on purpose. Read the standard library. Profile before you optimize. Run ",[61,1453,1032],{}," on your own code and see how much it catches. Read a security advisory for a package you use and understand ",[610,1456,1457],{},"why"," the fix was necessary, not just that a new version exists.",[14,1460,1461],{},"Welcome to being a Python developer.",[14,1463,1464],{},"🐍",[1466,1467,1468],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":81,"searchDepth":103,"depth":103,"links":1470},[1471,1472,1480,1488,1497,1502,1503,1504,1505,1506],{"id":20,"depth":103,"text":21},{"id":50,"depth":103,"text":51,"children":1473},[1474,1475,1477,1478,1479],{"id":55,"depth":123,"text":56},{"id":67,"depth":123,"text":1476},"2. Stack with __slots__",{"id":214,"depth":123,"text":215},{"id":237,"depth":123,"text":238},{"id":263,"depth":123,"text":264},{"id":286,"depth":103,"text":287,"children":1481},[1482,1483,1484,1485,1486],{"id":290,"depth":123,"text":291},{"id":305,"depth":123,"text":306},{"id":324,"depth":123,"text":325},{"id":335,"depth":123,"text":336},{"id":409,"depth":123,"text":1487},"10. Type-State Builder with Protocol",{"id":440,"depth":103,"text":441,"children":1489},[1490,1491,1492,1493,1494,1495],{"id":444,"depth":123,"text":445},{"id":473,"depth":123,"text":474},{"id":491,"depth":123,"text":492},{"id":520,"depth":123,"text":521},{"id":542,"depth":123,"text":543},{"id":553,"depth":123,"text":1496},"16. C Extension via ctypes or Cython",{"id":567,"depth":103,"text":568,"children":1498},[1499,1500,1501],{"id":571,"depth":123,"text":572},{"id":696,"depth":123,"text":697},{"id":861,"depth":123,"text":862},{"id":1132,"depth":103,"text":1133},{"id":1212,"depth":103,"text":1213},{"id":1248,"depth":103,"text":1249},{"id":1300,"depth":103,"text":1301},{"id":1430,"depth":103,"text":1431},"md",{},true,"\u002Fpython\u002F28-exercises-and-projects",{"title":5,"description":16},"python\u002F28-exercises-and-projects","SbfrWmDWKOOBAoghI_AVt_xhJT2oZRjQ5zKzSEegmp0",1789924651854]