[{"data":1,"prerenderedAt":3021},["ShallowReactive",2],{"page-\u002Fpython\u002F18-file-io-and-serialization":3},{"id":4,"title":5,"body":6,"description":27,"extension":3015,"meta":3016,"navigation":116,"path":3017,"seo":3018,"stem":3019,"__hash__":3020},"content\u002Fpython\u002F18-file-io-and-serialization.md","18 — File I\u002FO & Serialization",{"type":7,"value":8,"toc":2993},"minimark",[9,13,18,230,332,359,456,460,685,692,933,972,979,1264,1269,1436,1447,1595,1602,1762,1781,1788,2020,2038,2049,2160,2172,2300,2316,2323,2455,2464,2468,2559,2563,2645,2649,2652,2782,2934,2938,2989],[10,11,5],"h1",{"id":12},"_18-file-io-serialization",[14,15,17],"h2",{"id":16},"opening-files-modes-and-the-context-manager","Opening Files: Modes and the Context Manager",[19,20,22],"code-wrapper",{"language":21},"python",[23,24,28],"pre",{"className":25,"code":26,"language":21,"meta":27,"style":27},"language-python shiki shiki-themes github-light github-dark","with open(\"notes.txt\", \"w\", encoding=\"utf-8\") as f:\n    f.write(\"first line\\n\")\n    f.write(\"second line\\n\")\n\nwith open(\"notes.txt\", \"r\", encoding=\"utf-8\") as f:\n    content = f.read()\nprint(content)\n# first line\n# second line\n\nwith open(\"notes.txt\", \"a\", encoding=\"utf-8\") as f:\n    f.write(\"appended line\\n\")\n","",[29,30,31,79,97,111,118,148,159,168,175,181,186,216],"code",{"__ignoreMap":27},[32,33,36,40,44,48,52,55,58,60,64,67,70,73,76],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"svdQ7","with",[32,41,43],{"class":42},"snvgF"," open",[32,45,47],{"class":46},"ssxIu","(",[32,49,51],{"class":50},"sJ6F3","\"notes.txt\"",[32,53,54],{"class":46},", ",[32,56,57],{"class":50},"\"w\"",[32,59,54],{"class":46},[32,61,63],{"class":62},"sCrzJ","encoding",[32,65,66],{"class":38},"=",[32,68,69],{"class":50},"\"utf-8\"",[32,71,72],{"class":46},") ",[32,74,75],{"class":38},"as",[32,77,78],{"class":46}," f:\n",[32,80,82,85,88,91,94],{"class":34,"line":81},2,[32,83,84],{"class":46},"    f.write(",[32,86,87],{"class":50},"\"first line",[32,89,90],{"class":42},"\\n",[32,92,93],{"class":50},"\"",[32,95,96],{"class":46},")\n",[32,98,100,102,105,107,109],{"class":34,"line":99},3,[32,101,84],{"class":46},[32,103,104],{"class":50},"\"second line",[32,106,90],{"class":42},[32,108,93],{"class":50},[32,110,96],{"class":46},[32,112,114],{"class":34,"line":113},4,[32,115,117],{"emptyLinePlaceholder":116},true,"\n",[32,119,121,123,125,127,129,131,134,136,138,140,142,144,146],{"class":34,"line":120},5,[32,122,39],{"class":38},[32,124,43],{"class":42},[32,126,47],{"class":46},[32,128,51],{"class":50},[32,130,54],{"class":46},[32,132,133],{"class":50},"\"r\"",[32,135,54],{"class":46},[32,137,63],{"class":62},[32,139,66],{"class":38},[32,141,69],{"class":50},[32,143,72],{"class":46},[32,145,75],{"class":38},[32,147,78],{"class":46},[32,149,151,154,156],{"class":34,"line":150},6,[32,152,153],{"class":46},"    content ",[32,155,66],{"class":38},[32,157,158],{"class":46}," f.read()\n",[32,160,162,165],{"class":34,"line":161},7,[32,163,164],{"class":42},"print",[32,166,167],{"class":46},"(content)\n",[32,169,171],{"class":34,"line":170},8,[32,172,174],{"class":173},"sdCPZ","# first line\n",[32,176,178],{"class":34,"line":177},9,[32,179,180],{"class":173},"# second line\n",[32,182,184],{"class":34,"line":183},10,[32,185,117],{"emptyLinePlaceholder":116},[32,187,189,191,193,195,197,199,202,204,206,208,210,212,214],{"class":34,"line":188},11,[32,190,39],{"class":38},[32,192,43],{"class":42},[32,194,47],{"class":46},[32,196,51],{"class":50},[32,198,54],{"class":46},[32,200,201],{"class":50},"\"a\"",[32,203,54],{"class":46},[32,205,63],{"class":62},[32,207,66],{"class":38},[32,209,69],{"class":50},[32,211,72],{"class":46},[32,213,75],{"class":38},[32,215,78],{"class":46},[32,217,219,221,224,226,228],{"class":34,"line":218},12,[32,220,84],{"class":46},[32,222,223],{"class":50},"\"appended line",[32,225,90],{"class":42},[32,227,93],{"class":50},[32,229,96],{"class":46},[231,232,233,246],"table",{},[234,235,236],"thead",{},[237,238,239,243],"tr",{},[240,241,242],"th",{},"Mode",[240,244,245],{},"Meaning",[247,248,249,259,273,282,297,315],"tbody",{},[237,250,251,256],{},[252,253,254],"td",{},[29,255,133],{},[252,257,258],{},"read (default); errors if file doesn't exist",[237,260,261,265],{},[252,262,263],{},[29,264,57],{},[252,266,267,268,272],{},"write; ",[269,270,271],"strong",{},"truncates"," the file if it exists, creates it if not",[237,274,275,279],{},[252,276,277],{},[29,278,201],{},[252,280,281],{},"append; creates the file if it doesn't exist, never truncates",[237,283,284,289],{},[252,285,286],{},[29,287,288],{},"\"x\"",[252,290,291,292,296],{},"exclusive create; errors if the file ",[293,294,295],"em",{},"already"," exists",[237,298,299,305],{},[252,300,301,304],{},[29,302,303],{},"\"b\""," suffix",[252,306,307,308,54,311,314],{},"binary mode (",[29,309,310],{},"\"rb\"",[29,312,313],{},"\"wb\"",") — bytes, not str",[237,316,317,322],{},[252,318,319,304],{},[29,320,321],{},"\"+\"",[252,323,324,325,54,328,331],{},"read and write (",[29,326,327],{},"\"r+\"",[29,329,330],{},"\"w+\"",")",[333,334,335,338,339,342,343,346,347,350,351,354,355,358],"p",{},[269,336,337],{},"Best practice",": always use ",[29,340,341],{},"with open(...) as f:"," — never a bare ",[29,344,345],{},"f = open(...)"," followed by manual ",[29,348,349],{},"f.close()",". The context manager guarantees the file is closed (flushing any buffered writes) even if an exception is raised mid-block, exactly like the ",[29,352,353],{},"__enter__","\u002F",[29,356,357],{},"__exit__"," protocol from chapter 14.",[19,360,361],{"language":21},[23,362,364],{"className":25,"code":363,"language":21,"meta":27,"style":27},"# RISKY — if write() or any code between open\u002Fclose raises, close() never runs\nf = open(\"data.txt\", \"w\")\nf.write(\"data\")\nprocess_data()   # if this raises, the file is never closed — buffered writes may be lost\nf.close()\n\n# CORRECT — guaranteed cleanup regardless of exceptions\nwith open(\"data.txt\", \"w\") as f:\n    f.write(\"data\")\n    process_data()\n",[29,365,366,371,391,401,409,414,418,423,443,451],{"__ignoreMap":27},[32,367,368],{"class":34,"line":35},[32,369,370],{"class":173},"# RISKY — if write() or any code between open\u002Fclose raises, close() never runs\n",[32,372,373,376,378,380,382,385,387,389],{"class":34,"line":81},[32,374,375],{"class":46},"f ",[32,377,66],{"class":38},[32,379,43],{"class":42},[32,381,47],{"class":46},[32,383,384],{"class":50},"\"data.txt\"",[32,386,54],{"class":46},[32,388,57],{"class":50},[32,390,96],{"class":46},[32,392,393,396,399],{"class":34,"line":99},[32,394,395],{"class":46},"f.write(",[32,397,398],{"class":50},"\"data\"",[32,400,96],{"class":46},[32,402,403,406],{"class":34,"line":113},[32,404,405],{"class":46},"process_data()   ",[32,407,408],{"class":173},"# if this raises, the file is never closed — buffered writes may be lost\n",[32,410,411],{"class":34,"line":120},[32,412,413],{"class":46},"f.close()\n",[32,415,416],{"class":34,"line":150},[32,417,117],{"emptyLinePlaceholder":116},[32,419,420],{"class":34,"line":161},[32,421,422],{"class":173},"# CORRECT — guaranteed cleanup regardless of exceptions\n",[32,424,425,427,429,431,433,435,437,439,441],{"class":34,"line":170},[32,426,39],{"class":38},[32,428,43],{"class":42},[32,430,47],{"class":46},[32,432,384],{"class":50},[32,434,54],{"class":46},[32,436,57],{"class":50},[32,438,72],{"class":46},[32,440,75],{"class":38},[32,442,78],{"class":46},[32,444,445,447,449],{"class":34,"line":177},[32,446,84],{"class":46},[32,448,398],{"class":50},[32,450,96],{"class":46},[32,452,453],{"class":34,"line":183},[32,454,455],{"class":46},"    process_data()\n",[14,457,459],{"id":458},"reading-patterns-whole-file-vs-line-by-line-vs-chunks","Reading Patterns: Whole File vs Line-by-Line vs Chunks",[19,461,462],{"language":21},[23,463,465],{"className":25,"code":464,"language":21,"meta":27,"style":27},"# Whole file at once — fine for small\u002Fmedium files, dangerous for huge ones (loads it ALL into memory)\nwith open(\"small.txt\", encoding=\"utf-8\") as f:\n    content = f.read()\n\n# Line by line — memory-efficient, the idiomatic way to process large text files (see chapter 09)\nwith open(\"huge.log\", encoding=\"utf-8\") as f:\n    for line in f:                 # the file object IS an iterator over lines\n        if \"ERROR\" in line:\n            print(line.strip())\n\n# All lines as a list — loads everything, but gives you len()\u002Findexing\nwith open(\"small.txt\", encoding=\"utf-8\") as f:\n    lines = f.readlines()\n\n# Fixed-size chunks — for binary data or streaming processing\nwith open(\"large.bin\", \"rb\") as f:\n    while chunk := f.read(8192):     # walrus operator (chapter 03) — read until EOF returns b\"\"\n        process_chunk(chunk)\n",[29,466,467,472,497,505,509,514,539,556,570,578,582,587,611,622,627,633,655,679],{"__ignoreMap":27},[32,468,469],{"class":34,"line":35},[32,470,471],{"class":173},"# Whole file at once — fine for small\u002Fmedium files, dangerous for huge ones (loads it ALL into memory)\n",[32,473,474,476,478,480,483,485,487,489,491,493,495],{"class":34,"line":81},[32,475,39],{"class":38},[32,477,43],{"class":42},[32,479,47],{"class":46},[32,481,482],{"class":50},"\"small.txt\"",[32,484,54],{"class":46},[32,486,63],{"class":62},[32,488,66],{"class":38},[32,490,69],{"class":50},[32,492,72],{"class":46},[32,494,75],{"class":38},[32,496,78],{"class":46},[32,498,499,501,503],{"class":34,"line":99},[32,500,153],{"class":46},[32,502,66],{"class":38},[32,504,158],{"class":46},[32,506,507],{"class":34,"line":113},[32,508,117],{"emptyLinePlaceholder":116},[32,510,511],{"class":34,"line":120},[32,512,513],{"class":173},"# Line by line — memory-efficient, the idiomatic way to process large text files (see chapter 09)\n",[32,515,516,518,520,522,525,527,529,531,533,535,537],{"class":34,"line":150},[32,517,39],{"class":38},[32,519,43],{"class":42},[32,521,47],{"class":46},[32,523,524],{"class":50},"\"huge.log\"",[32,526,54],{"class":46},[32,528,63],{"class":62},[32,530,66],{"class":38},[32,532,69],{"class":50},[32,534,72],{"class":46},[32,536,75],{"class":38},[32,538,78],{"class":46},[32,540,541,544,547,550,553],{"class":34,"line":161},[32,542,543],{"class":38},"    for",[32,545,546],{"class":46}," line ",[32,548,549],{"class":38},"in",[32,551,552],{"class":46}," f:                 ",[32,554,555],{"class":173},"# the file object IS an iterator over lines\n",[32,557,558,561,564,567],{"class":34,"line":170},[32,559,560],{"class":38},"        if",[32,562,563],{"class":50}," \"ERROR\"",[32,565,566],{"class":38}," in",[32,568,569],{"class":46}," line:\n",[32,571,572,575],{"class":34,"line":177},[32,573,574],{"class":42},"            print",[32,576,577],{"class":46},"(line.strip())\n",[32,579,580],{"class":34,"line":183},[32,581,117],{"emptyLinePlaceholder":116},[32,583,584],{"class":34,"line":188},[32,585,586],{"class":173},"# All lines as a list — loads everything, but gives you len()\u002Findexing\n",[32,588,589,591,593,595,597,599,601,603,605,607,609],{"class":34,"line":218},[32,590,39],{"class":38},[32,592,43],{"class":42},[32,594,47],{"class":46},[32,596,482],{"class":50},[32,598,54],{"class":46},[32,600,63],{"class":62},[32,602,66],{"class":38},[32,604,69],{"class":50},[32,606,72],{"class":46},[32,608,75],{"class":38},[32,610,78],{"class":46},[32,612,614,617,619],{"class":34,"line":613},13,[32,615,616],{"class":46},"    lines ",[32,618,66],{"class":38},[32,620,621],{"class":46}," f.readlines()\n",[32,623,625],{"class":34,"line":624},14,[32,626,117],{"emptyLinePlaceholder":116},[32,628,630],{"class":34,"line":629},15,[32,631,632],{"class":173},"# Fixed-size chunks — for binary data or streaming processing\n",[32,634,636,638,640,642,645,647,649,651,653],{"class":34,"line":635},16,[32,637,39],{"class":38},[32,639,43],{"class":42},[32,641,47],{"class":46},[32,643,644],{"class":50},"\"large.bin\"",[32,646,54],{"class":46},[32,648,310],{"class":50},[32,650,72],{"class":46},[32,652,75],{"class":38},[32,654,78],{"class":46},[32,656,658,661,664,667,670,673,676],{"class":34,"line":657},17,[32,659,660],{"class":38},"    while",[32,662,663],{"class":46}," chunk ",[32,665,666],{"class":38},":=",[32,668,669],{"class":46}," f.read(",[32,671,672],{"class":42},"8192",[32,674,675],{"class":46},"):     ",[32,677,678],{"class":173},"# walrus operator (chapter 03) — read until EOF returns b\"\"\n",[32,680,682],{"class":34,"line":681},18,[32,683,684],{"class":46},"        process_chunk(chunk)\n",[14,686,688,691],{"id":687},"pathlib-the-modern-way-to-handle-paths",[29,689,690],{},"pathlib"," — The Modern Way to Handle Paths",[19,693,694],{"language":21},[23,695,697],{"className":25,"code":696,"language":21,"meta":27,"style":27},"from pathlib import Path\n\nconfig_dir = Path.home() \u002F \".config\" \u002F \"myapp\"    # `\u002F` is OVERLOADED to join paths — cross-platform!\nconfig_file = config_dir \u002F \"settings.json\"\n\nconfig_dir.mkdir(parents=True, exist_ok=True)      # like `mkdir -p`\nconfig_file.write_text('{\"debug\": true}', encoding=\"utf-8\")\n\nprint(config_file.exists())      # True\nprint(config_file.name)            # \"settings.json\"\nprint(config_file.stem)              # \"settings\"\nprint(config_file.suffix)              # \".json\"\nprint(config_file.parent)                # ...\u002F.config\u002Fmyapp\n\nfor py_file in Path(\".\").glob(\"**\u002F*.py\"):    # recursive glob\n    print(py_file)\n\ncontent = config_file.read_text(encoding=\"utf-8\")\nprint(content)   # {\"debug\": true}\n",[29,698,699,713,717,741,756,760,788,806,810,820,830,840,850,860,864,892,900,904,922],{"__ignoreMap":27},[32,700,701,704,707,710],{"class":34,"line":35},[32,702,703],{"class":38},"from",[32,705,706],{"class":46}," pathlib ",[32,708,709],{"class":38},"import",[32,711,712],{"class":46}," Path\n",[32,714,715],{"class":34,"line":81},[32,716,117],{"emptyLinePlaceholder":116},[32,718,719,722,724,727,729,732,735,738],{"class":34,"line":99},[32,720,721],{"class":46},"config_dir ",[32,723,66],{"class":38},[32,725,726],{"class":46}," Path.home() ",[32,728,354],{"class":38},[32,730,731],{"class":50}," \".config\"",[32,733,734],{"class":38}," \u002F",[32,736,737],{"class":50}," \"myapp\"",[32,739,740],{"class":173},"    # `\u002F` is OVERLOADED to join paths — cross-platform!\n",[32,742,743,746,748,751,753],{"class":34,"line":113},[32,744,745],{"class":46},"config_file ",[32,747,66],{"class":38},[32,749,750],{"class":46}," config_dir ",[32,752,354],{"class":38},[32,754,755],{"class":50}," \"settings.json\"\n",[32,757,758],{"class":34,"line":120},[32,759,117],{"emptyLinePlaceholder":116},[32,761,762,765,768,770,773,775,778,780,782,785],{"class":34,"line":150},[32,763,764],{"class":46},"config_dir.mkdir(",[32,766,767],{"class":62},"parents",[32,769,66],{"class":38},[32,771,772],{"class":42},"True",[32,774,54],{"class":46},[32,776,777],{"class":62},"exist_ok",[32,779,66],{"class":38},[32,781,772],{"class":42},[32,783,784],{"class":46},")      ",[32,786,787],{"class":173},"# like `mkdir -p`\n",[32,789,790,793,796,798,800,802,804],{"class":34,"line":161},[32,791,792],{"class":46},"config_file.write_text(",[32,794,795],{"class":50},"'{\"debug\": true}'",[32,797,54],{"class":46},[32,799,63],{"class":62},[32,801,66],{"class":38},[32,803,69],{"class":50},[32,805,96],{"class":46},[32,807,808],{"class":34,"line":170},[32,809,117],{"emptyLinePlaceholder":116},[32,811,812,814,817],{"class":34,"line":177},[32,813,164],{"class":42},[32,815,816],{"class":46},"(config_file.exists())      ",[32,818,819],{"class":173},"# True\n",[32,821,822,824,827],{"class":34,"line":183},[32,823,164],{"class":42},[32,825,826],{"class":46},"(config_file.name)            ",[32,828,829],{"class":173},"# \"settings.json\"\n",[32,831,832,834,837],{"class":34,"line":188},[32,833,164],{"class":42},[32,835,836],{"class":46},"(config_file.stem)              ",[32,838,839],{"class":173},"# \"settings\"\n",[32,841,842,844,847],{"class":34,"line":218},[32,843,164],{"class":42},[32,845,846],{"class":46},"(config_file.suffix)              ",[32,848,849],{"class":173},"# \".json\"\n",[32,851,852,854,857],{"class":34,"line":613},[32,853,164],{"class":42},[32,855,856],{"class":46},"(config_file.parent)                ",[32,858,859],{"class":173},"# ...\u002F.config\u002Fmyapp\n",[32,861,862],{"class":34,"line":624},[32,863,117],{"emptyLinePlaceholder":116},[32,865,866,869,872,874,877,880,883,886,889],{"class":34,"line":629},[32,867,868],{"class":38},"for",[32,870,871],{"class":46}," py_file ",[32,873,549],{"class":38},[32,875,876],{"class":46}," Path(",[32,878,879],{"class":50},"\".\"",[32,881,882],{"class":46},").glob(",[32,884,885],{"class":50},"\"**\u002F*.py\"",[32,887,888],{"class":46},"):    ",[32,890,891],{"class":173},"# recursive glob\n",[32,893,894,897],{"class":34,"line":635},[32,895,896],{"class":42},"    print",[32,898,899],{"class":46},"(py_file)\n",[32,901,902],{"class":34,"line":657},[32,903,117],{"emptyLinePlaceholder":116},[32,905,906,909,911,914,916,918,920],{"class":34,"line":681},[32,907,908],{"class":46},"content ",[32,910,66],{"class":38},[32,912,913],{"class":46}," config_file.read_text(",[32,915,63],{"class":62},[32,917,66],{"class":38},[32,919,69],{"class":50},[32,921,96],{"class":46},[32,923,925,927,930],{"class":34,"line":924},19,[32,926,164],{"class":42},[32,928,929],{"class":46},"(content)   ",[32,931,932],{"class":173},"# {\"debug\": true}\n",[333,934,935,938,939,942,943,946,947,950,951,954,955,957,958,961,962,964,965,967,968,971],{},[29,936,937],{},"pathlib.Path"," objects are cross-platform by construction — ",[29,940,941],{},"Path(\"a\") \u002F \"b\""," produces ",[29,944,945],{},"a\\b"," on Windows and ",[29,948,949],{},"a\u002Fb"," on POSIX automatically, unlike manually concatenating strings with ",[29,952,953],{},"os.path.join"," or (far worse) hardcoded ",[29,956,354],{}," or ",[29,959,960],{},"\\",". ",[269,963,337],{},": prefer ",[29,966,690],{}," over ",[29,969,970],{},"os.path"," string manipulation in all new code.",[14,973,975,978],{"id":974},"json-the-ubiquitous-interchange-format",[29,976,977],{},"json"," — The Ubiquitous Interchange Format",[19,980,981],{"language":21},[23,982,984],{"className":25,"code":983,"language":21,"meta":27,"style":27},"import json\n\ndata = {\"name\": \"Ada\", \"age\": 36, \"active\": True, \"tags\": [\"math\", \"computing\"]}\n\njson_string = json.dumps(data, indent=2)\nprint(json_string)\n# {\n#   \"name\": \"Ada\",\n#   \"age\": 36,\n#   \"active\": true,\n#   \"tags\": [\n#     \"math\",\n#     \"computing\"\n#   ]\n# }\n\nparsed = json.loads(json_string)\nprint(parsed[\"name\"])   # \"Ada\"\nprint(parsed == data)     # True\n\nwith open(\"data.json\", \"w\", encoding=\"utf-8\") as f:\n    json.dump(data, f, indent=2)\n\nwith open(\"data.json\", encoding=\"utf-8\") as f:\n    loaded = json.load(f)\n",[29,985,986,993,997,1054,1058,1078,1085,1090,1095,1100,1105,1110,1115,1120,1125,1130,1134,1144,1159,1174,1179,1209,1223,1228,1253],{"__ignoreMap":27},[32,987,988,990],{"class":34,"line":35},[32,989,709],{"class":38},[32,991,992],{"class":46}," json\n",[32,994,995],{"class":34,"line":81},[32,996,117],{"emptyLinePlaceholder":116},[32,998,999,1002,1004,1007,1010,1013,1016,1018,1021,1023,1026,1028,1031,1033,1035,1037,1040,1043,1046,1048,1051],{"class":34,"line":99},[32,1000,1001],{"class":46},"data ",[32,1003,66],{"class":38},[32,1005,1006],{"class":46}," {",[32,1008,1009],{"class":50},"\"name\"",[32,1011,1012],{"class":46},": ",[32,1014,1015],{"class":50},"\"Ada\"",[32,1017,54],{"class":46},[32,1019,1020],{"class":50},"\"age\"",[32,1022,1012],{"class":46},[32,1024,1025],{"class":42},"36",[32,1027,54],{"class":46},[32,1029,1030],{"class":50},"\"active\"",[32,1032,1012],{"class":46},[32,1034,772],{"class":42},[32,1036,54],{"class":46},[32,1038,1039],{"class":50},"\"tags\"",[32,1041,1042],{"class":46},": [",[32,1044,1045],{"class":50},"\"math\"",[32,1047,54],{"class":46},[32,1049,1050],{"class":50},"\"computing\"",[32,1052,1053],{"class":46},"]}\n",[32,1055,1056],{"class":34,"line":113},[32,1057,117],{"emptyLinePlaceholder":116},[32,1059,1060,1063,1065,1068,1071,1073,1076],{"class":34,"line":120},[32,1061,1062],{"class":46},"json_string ",[32,1064,66],{"class":38},[32,1066,1067],{"class":46}," json.dumps(data, ",[32,1069,1070],{"class":62},"indent",[32,1072,66],{"class":38},[32,1074,1075],{"class":42},"2",[32,1077,96],{"class":46},[32,1079,1080,1082],{"class":34,"line":150},[32,1081,164],{"class":42},[32,1083,1084],{"class":46},"(json_string)\n",[32,1086,1087],{"class":34,"line":161},[32,1088,1089],{"class":173},"# {\n",[32,1091,1092],{"class":34,"line":170},[32,1093,1094],{"class":173},"#   \"name\": \"Ada\",\n",[32,1096,1097],{"class":34,"line":177},[32,1098,1099],{"class":173},"#   \"age\": 36,\n",[32,1101,1102],{"class":34,"line":183},[32,1103,1104],{"class":173},"#   \"active\": true,\n",[32,1106,1107],{"class":34,"line":188},[32,1108,1109],{"class":173},"#   \"tags\": [\n",[32,1111,1112],{"class":34,"line":218},[32,1113,1114],{"class":173},"#     \"math\",\n",[32,1116,1117],{"class":34,"line":613},[32,1118,1119],{"class":173},"#     \"computing\"\n",[32,1121,1122],{"class":34,"line":624},[32,1123,1124],{"class":173},"#   ]\n",[32,1126,1127],{"class":34,"line":629},[32,1128,1129],{"class":173},"# }\n",[32,1131,1132],{"class":34,"line":635},[32,1133,117],{"emptyLinePlaceholder":116},[32,1135,1136,1139,1141],{"class":34,"line":657},[32,1137,1138],{"class":46},"parsed ",[32,1140,66],{"class":38},[32,1142,1143],{"class":46}," json.loads(json_string)\n",[32,1145,1146,1148,1151,1153,1156],{"class":34,"line":681},[32,1147,164],{"class":42},[32,1149,1150],{"class":46},"(parsed[",[32,1152,1009],{"class":50},[32,1154,1155],{"class":46},"])   ",[32,1157,1158],{"class":173},"# \"Ada\"\n",[32,1160,1161,1163,1166,1169,1172],{"class":34,"line":924},[32,1162,164],{"class":42},[32,1164,1165],{"class":46},"(parsed ",[32,1167,1168],{"class":38},"==",[32,1170,1171],{"class":46}," data)     ",[32,1173,819],{"class":173},[32,1175,1177],{"class":34,"line":1176},20,[32,1178,117],{"emptyLinePlaceholder":116},[32,1180,1182,1184,1186,1188,1191,1193,1195,1197,1199,1201,1203,1205,1207],{"class":34,"line":1181},21,[32,1183,39],{"class":38},[32,1185,43],{"class":42},[32,1187,47],{"class":46},[32,1189,1190],{"class":50},"\"data.json\"",[32,1192,54],{"class":46},[32,1194,57],{"class":50},[32,1196,54],{"class":46},[32,1198,63],{"class":62},[32,1200,66],{"class":38},[32,1202,69],{"class":50},[32,1204,72],{"class":46},[32,1206,75],{"class":38},[32,1208,78],{"class":46},[32,1210,1212,1215,1217,1219,1221],{"class":34,"line":1211},22,[32,1213,1214],{"class":46},"    json.dump(data, f, ",[32,1216,1070],{"class":62},[32,1218,66],{"class":38},[32,1220,1075],{"class":42},[32,1222,96],{"class":46},[32,1224,1226],{"class":34,"line":1225},23,[32,1227,117],{"emptyLinePlaceholder":116},[32,1229,1231,1233,1235,1237,1239,1241,1243,1245,1247,1249,1251],{"class":34,"line":1230},24,[32,1232,39],{"class":38},[32,1234,43],{"class":42},[32,1236,47],{"class":46},[32,1238,1190],{"class":50},[32,1240,54],{"class":46},[32,1242,63],{"class":62},[32,1244,66],{"class":38},[32,1246,69],{"class":50},[32,1248,72],{"class":46},[32,1250,75],{"class":38},[32,1252,78],{"class":46},[32,1254,1256,1259,1261],{"class":34,"line":1255},25,[32,1257,1258],{"class":46},"    loaded ",[32,1260,66],{"class":38},[32,1262,1263],{"class":46}," json.load(f)\n",[1265,1266,1268],"h3",{"id":1267},"the-gotcha-json-has-no-tuple-set-or-datetime-type","The gotcha: JSON has no tuple, set, or datetime type",[19,1270,1271],{"language":21},[23,1272,1274],{"className":25,"code":1273,"language":21,"meta":27,"style":27},"data = {\"coords\": (1, 2), \"tags\": {\"a\", \"b\"}}\n# json.dumps(data)   # TypeError: Object of type set is not JSON serializable\n\ndata_fixed = {\"coords\": [1, 2], \"tags\": list({\"a\", \"b\"})}\nprint(json.dumps(data_fixed))   # {\"coords\": [1, 2], \"tags\": [...]}  — order of set->list is not guaranteed!\n\n# Round-tripping a tuple through JSON silently turns it into a list — JSON has no tuple type at all\nround_tripped = json.loads(json.dumps({\"coords\": (1, 2)}))\nprint(round_tripped)                # {'coords': [1, 2]}\nprint(type(round_tripped[\"coords\"]))  # \u003Cclass 'list'> — NOT tuple!\n",[29,1275,1276,1314,1319,1323,1364,1374,1378,1383,1406,1416],{"__ignoreMap":27},[32,1277,1278,1280,1282,1284,1287,1290,1293,1295,1297,1300,1302,1305,1307,1309,1311],{"class":34,"line":35},[32,1279,1001],{"class":46},[32,1281,66],{"class":38},[32,1283,1006],{"class":46},[32,1285,1286],{"class":50},"\"coords\"",[32,1288,1289],{"class":46},": (",[32,1291,1292],{"class":42},"1",[32,1294,54],{"class":46},[32,1296,1075],{"class":42},[32,1298,1299],{"class":46},"), ",[32,1301,1039],{"class":50},[32,1303,1304],{"class":46},": {",[32,1306,201],{"class":50},[32,1308,54],{"class":46},[32,1310,303],{"class":50},[32,1312,1313],{"class":46},"}}\n",[32,1315,1316],{"class":34,"line":81},[32,1317,1318],{"class":173},"# json.dumps(data)   # TypeError: Object of type set is not JSON serializable\n",[32,1320,1321],{"class":34,"line":99},[32,1322,117],{"emptyLinePlaceholder":116},[32,1324,1325,1328,1330,1332,1334,1336,1338,1340,1342,1345,1347,1349,1352,1355,1357,1359,1361],{"class":34,"line":113},[32,1326,1327],{"class":46},"data_fixed ",[32,1329,66],{"class":38},[32,1331,1006],{"class":46},[32,1333,1286],{"class":50},[32,1335,1042],{"class":46},[32,1337,1292],{"class":42},[32,1339,54],{"class":46},[32,1341,1075],{"class":42},[32,1343,1344],{"class":46},"], ",[32,1346,1039],{"class":50},[32,1348,1012],{"class":46},[32,1350,1351],{"class":42},"list",[32,1353,1354],{"class":46},"({",[32,1356,201],{"class":50},[32,1358,54],{"class":46},[32,1360,303],{"class":50},[32,1362,1363],{"class":46},"})}\n",[32,1365,1366,1368,1371],{"class":34,"line":120},[32,1367,164],{"class":42},[32,1369,1370],{"class":46},"(json.dumps(data_fixed))   ",[32,1372,1373],{"class":173},"# {\"coords\": [1, 2], \"tags\": [...]}  — order of set->list is not guaranteed!\n",[32,1375,1376],{"class":34,"line":150},[32,1377,117],{"emptyLinePlaceholder":116},[32,1379,1380],{"class":34,"line":161},[32,1381,1382],{"class":173},"# Round-tripping a tuple through JSON silently turns it into a list — JSON has no tuple type at all\n",[32,1384,1385,1388,1390,1393,1395,1397,1399,1401,1403],{"class":34,"line":170},[32,1386,1387],{"class":46},"round_tripped ",[32,1389,66],{"class":38},[32,1391,1392],{"class":46}," json.loads(json.dumps({",[32,1394,1286],{"class":50},[32,1396,1289],{"class":46},[32,1398,1292],{"class":42},[32,1400,54],{"class":46},[32,1402,1075],{"class":42},[32,1404,1405],{"class":46},")}))\n",[32,1407,1408,1410,1413],{"class":34,"line":177},[32,1409,164],{"class":42},[32,1411,1412],{"class":46},"(round_tripped)                ",[32,1414,1415],{"class":173},"# {'coords': [1, 2]}\n",[32,1417,1418,1420,1422,1425,1428,1430,1433],{"class":34,"line":183},[32,1419,164],{"class":42},[32,1421,47],{"class":46},[32,1423,1424],{"class":42},"type",[32,1426,1427],{"class":46},"(round_tripped[",[32,1429,1286],{"class":50},[32,1431,1432],{"class":46},"]))  ",[32,1434,1435],{"class":173},"# \u003Cclass 'list'> — NOT tuple!\n",[333,1437,1438,1439,1442,1443,1446],{},"For custom objects, dates, or types JSON doesn't natively support, use ",[29,1440,1441],{},"default="," (serialization) and ",[29,1444,1445],{},"object_hook="," (deserialization):",[19,1448,1449],{"language":21},[23,1450,1452],{"className":25,"code":1451,"language":21,"meta":27,"style":27},"from datetime import datetime, date\n\ndef json_default(obj):\n    if isinstance(obj, (datetime, date)):\n        return obj.isoformat()\n    raise TypeError(f\"Object of type {type(obj).__name__} is not JSON serializable\")\n\npayload = {\"created_at\": datetime(2024, 1, 15, 10, 30)}\nprint(json.dumps(payload, default=json_default))\n# {\"created_at\": \"2024-01-15T10:30:00\"}\n",[29,1453,1454,1466,1470,1482,1493,1501,1531,1535,1575,1590],{"__ignoreMap":27},[32,1455,1456,1458,1461,1463],{"class":34,"line":35},[32,1457,703],{"class":38},[32,1459,1460],{"class":46}," datetime ",[32,1462,709],{"class":38},[32,1464,1465],{"class":46}," datetime, date\n",[32,1467,1468],{"class":34,"line":81},[32,1469,117],{"emptyLinePlaceholder":116},[32,1471,1472,1475,1479],{"class":34,"line":99},[32,1473,1474],{"class":38},"def",[32,1476,1478],{"class":1477},"sIsaT"," json_default",[32,1480,1481],{"class":46},"(obj):\n",[32,1483,1484,1487,1490],{"class":34,"line":113},[32,1485,1486],{"class":38},"    if",[32,1488,1489],{"class":42}," isinstance",[32,1491,1492],{"class":46},"(obj, (datetime, date)):\n",[32,1494,1495,1498],{"class":34,"line":120},[32,1496,1497],{"class":38},"        return",[32,1499,1500],{"class":46}," obj.isoformat()\n",[32,1502,1503,1506,1509,1511,1514,1517,1520,1523,1526,1529],{"class":34,"line":150},[32,1504,1505],{"class":38},"    raise",[32,1507,1508],{"class":42}," TypeError",[32,1510,47],{"class":46},[32,1512,1513],{"class":38},"f",[32,1515,1516],{"class":50},"\"Object of type ",[32,1518,1519],{"class":42},"{type",[32,1521,1522],{"class":46},"(obj).",[32,1524,1525],{"class":42},"__name__}",[32,1527,1528],{"class":50}," is not JSON serializable\"",[32,1530,96],{"class":46},[32,1532,1533],{"class":34,"line":161},[32,1534,117],{"emptyLinePlaceholder":116},[32,1536,1537,1540,1542,1544,1547,1550,1553,1555,1557,1559,1562,1564,1567,1569,1572],{"class":34,"line":170},[32,1538,1539],{"class":46},"payload ",[32,1541,66],{"class":38},[32,1543,1006],{"class":46},[32,1545,1546],{"class":50},"\"created_at\"",[32,1548,1549],{"class":46},": datetime(",[32,1551,1552],{"class":42},"2024",[32,1554,54],{"class":46},[32,1556,1292],{"class":42},[32,1558,54],{"class":46},[32,1560,1561],{"class":42},"15",[32,1563,54],{"class":46},[32,1565,1566],{"class":42},"10",[32,1568,54],{"class":46},[32,1570,1571],{"class":42},"30",[32,1573,1574],{"class":46},")}\n",[32,1576,1577,1579,1582,1585,1587],{"class":34,"line":177},[32,1578,164],{"class":42},[32,1580,1581],{"class":46},"(json.dumps(payload, ",[32,1583,1584],{"class":62},"default",[32,1586,66],{"class":38},[32,1588,1589],{"class":46},"json_default))\n",[32,1591,1592],{"class":34,"line":183},[32,1593,1594],{"class":173},"# {\"created_at\": \"2024-01-15T10:30:00\"}\n",[14,1596,1598,1601],{"id":1597},"pickle-full-python-object-serialization",[29,1599,1600],{},"pickle"," — Full Python Object Serialization",[19,1603,1604],{"language":21},[23,1605,1607],{"className":25,"code":1606,"language":21,"meta":27,"style":27},"import pickle\n\nclass Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n\np = Point(3, 4)\n\nwith open(\"point.pkl\", \"wb\") as f:      # ALWAYS binary mode for pickle\n    pickle.dump(p, f)\n\nwith open(\"point.pkl\", \"rb\") as f:\n    loaded = pickle.load(f)\nprint(loaded.x, loaded.y)   # 3 4 — full object, including class identity, restored\n",[29,1608,1609,1616,1620,1631,1642,1661,1665,1685,1689,1714,1719,1723,1743,1752],{"__ignoreMap":27},[32,1610,1611,1613],{"class":34,"line":35},[32,1612,709],{"class":38},[32,1614,1615],{"class":46}," pickle\n",[32,1617,1618],{"class":34,"line":81},[32,1619,117],{"emptyLinePlaceholder":116},[32,1621,1622,1625,1628],{"class":34,"line":99},[32,1623,1624],{"class":38},"class",[32,1626,1627],{"class":1477}," Point",[32,1629,1630],{"class":46},":\n",[32,1632,1633,1636,1639],{"class":34,"line":113},[32,1634,1635],{"class":38},"    def",[32,1637,1638],{"class":42}," __init__",[32,1640,1641],{"class":46},"(self, x, y):\n",[32,1643,1644,1647,1650,1653,1656,1658],{"class":34,"line":120},[32,1645,1646],{"class":42},"        self",[32,1648,1649],{"class":46},".x, ",[32,1651,1652],{"class":42},"self",[32,1654,1655],{"class":46},".y ",[32,1657,66],{"class":38},[32,1659,1660],{"class":46}," x, y\n",[32,1662,1663],{"class":34,"line":150},[32,1664,117],{"emptyLinePlaceholder":116},[32,1666,1667,1670,1672,1675,1678,1680,1683],{"class":34,"line":161},[32,1668,1669],{"class":46},"p ",[32,1671,66],{"class":38},[32,1673,1674],{"class":46}," Point(",[32,1676,1677],{"class":42},"3",[32,1679,54],{"class":46},[32,1681,1682],{"class":42},"4",[32,1684,96],{"class":46},[32,1686,1687],{"class":34,"line":170},[32,1688,117],{"emptyLinePlaceholder":116},[32,1690,1691,1693,1695,1697,1700,1702,1704,1706,1708,1711],{"class":34,"line":177},[32,1692,39],{"class":38},[32,1694,43],{"class":42},[32,1696,47],{"class":46},[32,1698,1699],{"class":50},"\"point.pkl\"",[32,1701,54],{"class":46},[32,1703,313],{"class":50},[32,1705,72],{"class":46},[32,1707,75],{"class":38},[32,1709,1710],{"class":46}," f:      ",[32,1712,1713],{"class":173},"# ALWAYS binary mode for pickle\n",[32,1715,1716],{"class":34,"line":183},[32,1717,1718],{"class":46},"    pickle.dump(p, f)\n",[32,1720,1721],{"class":34,"line":188},[32,1722,117],{"emptyLinePlaceholder":116},[32,1724,1725,1727,1729,1731,1733,1735,1737,1739,1741],{"class":34,"line":218},[32,1726,39],{"class":38},[32,1728,43],{"class":42},[32,1730,47],{"class":46},[32,1732,1699],{"class":50},[32,1734,54],{"class":46},[32,1736,310],{"class":50},[32,1738,72],{"class":46},[32,1740,75],{"class":38},[32,1742,78],{"class":46},[32,1744,1745,1747,1749],{"class":34,"line":613},[32,1746,1258],{"class":46},[32,1748,66],{"class":38},[32,1750,1751],{"class":46}," pickle.load(f)\n",[32,1753,1754,1756,1759],{"class":34,"line":624},[32,1755,164],{"class":42},[32,1757,1758],{"class":46},"(loaded.x, loaded.y)   ",[32,1760,1761],{"class":173},"# 3 4 — full object, including class identity, restored\n",[333,1763,1764,1766,1767,1769,1770,1773,1774,1777,1778,1780],{},[29,1765,1600],{}," can serialize almost any Python object — including custom classes, nested structures, even some functions — which ",[29,1768,977],{}," fundamentally cannot. This power comes with a serious cost, covered fully in chapter 27: ",[269,1771,1772],{},"never unpickle data from an untrusted source"," — deserializing a malicious pickle can execute arbitrary code, since ",[29,1775,1776],{},"pickle.load"," is essentially \"reconstruct and run whatever instructions produced this byte stream.\" Use ",[29,1779,1600],{}," only for trusted, internal data (caches, checkpoints between your own processes) — never for data crossing a security boundary (user uploads, network payloads from external parties).",[14,1782,1784,1787],{"id":1783},"csv-tabular-data",[29,1785,1786],{},"csv"," — Tabular Data",[19,1789,1790],{"language":21},[23,1791,1793],{"className":25,"code":1792,"language":21,"meta":27,"style":27},"import csv\n\nrows = [\n    {\"name\": \"Ada\", \"role\": \"Mathematician\"},\n    {\"name\": \"Grace\", \"role\": \"Rear Admiral\"},\n]\n\nwith open(\"people.csv\", \"w\", newline=\"\", encoding=\"utf-8\") as f:   # newline=\"\" is REQUIRED on all platforms\n    writer = csv.DictWriter(f, fieldnames=[\"name\", \"role\"])\n    writer.writeheader()\n    writer.writerows(rows)\n\nwith open(\"people.csv\", newline=\"\", encoding=\"utf-8\") as f:\n    reader = csv.DictReader(f)\n    for row in reader:\n        print(row)   # OrderedDict-like access: row[\"name\"], row[\"role\"]\n",[29,1794,1795,1802,1806,1816,1840,1862,1867,1871,1914,1941,1946,1951,1955,1987,1997,2009],{"__ignoreMap":27},[32,1796,1797,1799],{"class":34,"line":35},[32,1798,709],{"class":38},[32,1800,1801],{"class":46}," csv\n",[32,1803,1804],{"class":34,"line":81},[32,1805,117],{"emptyLinePlaceholder":116},[32,1807,1808,1811,1813],{"class":34,"line":99},[32,1809,1810],{"class":46},"rows ",[32,1812,66],{"class":38},[32,1814,1815],{"class":46}," [\n",[32,1817,1818,1821,1823,1825,1827,1829,1832,1834,1837],{"class":34,"line":113},[32,1819,1820],{"class":46},"    {",[32,1822,1009],{"class":50},[32,1824,1012],{"class":46},[32,1826,1015],{"class":50},[32,1828,54],{"class":46},[32,1830,1831],{"class":50},"\"role\"",[32,1833,1012],{"class":46},[32,1835,1836],{"class":50},"\"Mathematician\"",[32,1838,1839],{"class":46},"},\n",[32,1841,1842,1844,1846,1848,1851,1853,1855,1857,1860],{"class":34,"line":120},[32,1843,1820],{"class":46},[32,1845,1009],{"class":50},[32,1847,1012],{"class":46},[32,1849,1850],{"class":50},"\"Grace\"",[32,1852,54],{"class":46},[32,1854,1831],{"class":50},[32,1856,1012],{"class":46},[32,1858,1859],{"class":50},"\"Rear Admiral\"",[32,1861,1839],{"class":46},[32,1863,1864],{"class":34,"line":150},[32,1865,1866],{"class":46},"]\n",[32,1868,1869],{"class":34,"line":161},[32,1870,117],{"emptyLinePlaceholder":116},[32,1872,1873,1875,1877,1879,1882,1884,1886,1888,1891,1893,1896,1898,1900,1902,1904,1906,1908,1911],{"class":34,"line":170},[32,1874,39],{"class":38},[32,1876,43],{"class":42},[32,1878,47],{"class":46},[32,1880,1881],{"class":50},"\"people.csv\"",[32,1883,54],{"class":46},[32,1885,57],{"class":50},[32,1887,54],{"class":46},[32,1889,1890],{"class":62},"newline",[32,1892,66],{"class":38},[32,1894,1895],{"class":50},"\"\"",[32,1897,54],{"class":46},[32,1899,63],{"class":62},[32,1901,66],{"class":38},[32,1903,69],{"class":50},[32,1905,72],{"class":46},[32,1907,75],{"class":38},[32,1909,1910],{"class":46}," f:   ",[32,1912,1913],{"class":173},"# newline=\"\" is REQUIRED on all platforms\n",[32,1915,1916,1919,1921,1924,1927,1929,1932,1934,1936,1938],{"class":34,"line":177},[32,1917,1918],{"class":46},"    writer ",[32,1920,66],{"class":38},[32,1922,1923],{"class":46}," csv.DictWriter(f, ",[32,1925,1926],{"class":62},"fieldnames",[32,1928,66],{"class":38},[32,1930,1931],{"class":46},"[",[32,1933,1009],{"class":50},[32,1935,54],{"class":46},[32,1937,1831],{"class":50},[32,1939,1940],{"class":46},"])\n",[32,1942,1943],{"class":34,"line":183},[32,1944,1945],{"class":46},"    writer.writeheader()\n",[32,1947,1948],{"class":34,"line":188},[32,1949,1950],{"class":46},"    writer.writerows(rows)\n",[32,1952,1953],{"class":34,"line":218},[32,1954,117],{"emptyLinePlaceholder":116},[32,1956,1957,1959,1961,1963,1965,1967,1969,1971,1973,1975,1977,1979,1981,1983,1985],{"class":34,"line":613},[32,1958,39],{"class":38},[32,1960,43],{"class":42},[32,1962,47],{"class":46},[32,1964,1881],{"class":50},[32,1966,54],{"class":46},[32,1968,1890],{"class":62},[32,1970,66],{"class":38},[32,1972,1895],{"class":50},[32,1974,54],{"class":46},[32,1976,63],{"class":62},[32,1978,66],{"class":38},[32,1980,69],{"class":50},[32,1982,72],{"class":46},[32,1984,75],{"class":38},[32,1986,78],{"class":46},[32,1988,1989,1992,1994],{"class":34,"line":624},[32,1990,1991],{"class":46},"    reader ",[32,1993,66],{"class":38},[32,1995,1996],{"class":46}," csv.DictReader(f)\n",[32,1998,1999,2001,2004,2006],{"class":34,"line":629},[32,2000,543],{"class":38},[32,2002,2003],{"class":46}," row ",[32,2005,549],{"class":38},[32,2007,2008],{"class":46}," reader:\n",[32,2010,2011,2014,2017],{"class":34,"line":635},[32,2012,2013],{"class":42},"        print",[32,2015,2016],{"class":46},"(row)   ",[32,2018,2019],{"class":173},"# OrderedDict-like access: row[\"name\"], row[\"role\"]\n",[333,2021,2022,2023,2026,2027,2030,2031,2033,2034,2037],{},"The ",[29,2024,2025],{},"newline=\"\""," argument to ",[29,2028,2029],{},"open()"," when reading\u002Fwriting CSV isn't optional boilerplate — it disables Python's own universal-newline translation, letting the ",[29,2032,1786],{}," module handle line endings itself; omitting it can produce extra blank rows on Windows, since both layers would otherwise translate ",[29,2035,2036],{},"\\r\\n"," independently.",[14,2039,2041,2042,2045,2046],{"id":2040},"encoding-pitfalls-str-vs-bytes","Encoding Pitfalls: ",[29,2043,2044],{},"str"," vs ",[29,2047,2048],{},"bytes",[19,2050,2051],{"language":21},[23,2052,2054],{"className":25,"code":2053,"language":21,"meta":27,"style":27},"# WRONG — mixing text mode with binary data, or vice versa, raises TypeError immediately...\nwith open(\"data.bin\", \"rb\") as f:\n    data = f.read()\n# f.write(\"text\")   # TypeError: a bytes-like object is required, not 'str'\n\n# ...but the MORE DANGEROUS mistake is omitting encoding= on text mode, which doesn't error —\n# it silently uses the OS's default encoding, which varies by platform and locale\nwith open(\"notes.txt\", \"w\") as f:      # no encoding= specified!\n    f.write(\"café\")                       # writes using locale.getpreferredencoding(), NOT guaranteed UTF-8\n\n# On a system with a non-UTF-8 default locale, or when the file is later read on a DIFFERENT\n# system, this can raise UnicodeDecodeError or silently produce mojibake (garbled text)\n",[29,2055,2056,2061,2082,2091,2096,2100,2105,2110,2133,2146,2150,2155],{"__ignoreMap":27},[32,2057,2058],{"class":34,"line":35},[32,2059,2060],{"class":173},"# WRONG — mixing text mode with binary data, or vice versa, raises TypeError immediately...\n",[32,2062,2063,2065,2067,2069,2072,2074,2076,2078,2080],{"class":34,"line":81},[32,2064,39],{"class":38},[32,2066,43],{"class":42},[32,2068,47],{"class":46},[32,2070,2071],{"class":50},"\"data.bin\"",[32,2073,54],{"class":46},[32,2075,310],{"class":50},[32,2077,72],{"class":46},[32,2079,75],{"class":38},[32,2081,78],{"class":46},[32,2083,2084,2087,2089],{"class":34,"line":99},[32,2085,2086],{"class":46},"    data ",[32,2088,66],{"class":38},[32,2090,158],{"class":46},[32,2092,2093],{"class":34,"line":113},[32,2094,2095],{"class":173},"# f.write(\"text\")   # TypeError: a bytes-like object is required, not 'str'\n",[32,2097,2098],{"class":34,"line":120},[32,2099,117],{"emptyLinePlaceholder":116},[32,2101,2102],{"class":34,"line":150},[32,2103,2104],{"class":173},"# ...but the MORE DANGEROUS mistake is omitting encoding= on text mode, which doesn't error —\n",[32,2106,2107],{"class":34,"line":161},[32,2108,2109],{"class":173},"# it silently uses the OS's default encoding, which varies by platform and locale\n",[32,2111,2112,2114,2116,2118,2120,2122,2124,2126,2128,2130],{"class":34,"line":170},[32,2113,39],{"class":38},[32,2115,43],{"class":42},[32,2117,47],{"class":46},[32,2119,51],{"class":50},[32,2121,54],{"class":46},[32,2123,57],{"class":50},[32,2125,72],{"class":46},[32,2127,75],{"class":38},[32,2129,1710],{"class":46},[32,2131,2132],{"class":173},"# no encoding= specified!\n",[32,2134,2135,2137,2140,2143],{"class":34,"line":177},[32,2136,84],{"class":46},[32,2138,2139],{"class":50},"\"café\"",[32,2141,2142],{"class":46},")                       ",[32,2144,2145],{"class":173},"# writes using locale.getpreferredencoding(), NOT guaranteed UTF-8\n",[32,2147,2148],{"class":34,"line":183},[32,2149,117],{"emptyLinePlaceholder":116},[32,2151,2152],{"class":34,"line":188},[32,2153,2154],{"class":173},"# On a system with a non-UTF-8 default locale, or when the file is later read on a DIFFERENT\n",[32,2156,2157],{"class":34,"line":218},[32,2158,2159],{"class":173},"# system, this can raise UnicodeDecodeError or silently produce mojibake (garbled text)\n",[333,2161,2162,2164,2165,2168,2169,2171],{},[269,2163,337],{},": always pass ",[29,2166,2167],{},"encoding=\"utf-8\""," explicitly to ",[29,2170,2029],{}," for text files — never rely on the platform default. This single habit prevents an entire category of \"works on my machine, breaks in CI\u002Fproduction\" bugs caused by different default encodings across Linux, macOS, and Windows.",[19,2173,2174],{"language":21},[23,2175,2177],{"className":25,"code":2176,"language":21,"meta":27,"style":27},"# Explicit encode\u002Fdecode when working directly with bytes\ntext = \"héllo wörld\"\nencoded = text.encode(\"utf-8\")\nprint(encoded)                 # b'h\\xc3\\xa9llo w\\xc3\\xb6rld'\nprint(len(text), len(encoded))   # 11 13 — multi-byte characters make byte length > character length\n\ndecoded = encoded.decode(\"utf-8\")\nprint(decoded == text)   # True\n\n# decoding with the WRONG encoding produces garbage or raises\n# encoded.decode(\"ascii\")   # UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3\nprint(encoded.decode(\"latin-1\"))   # \"hÃ©llo wÃ¶rld\" — decodes WITHOUT error, but WRONG text (mojibake)\n",[29,2178,2179,2184,2194,2208,2218,2238,2242,2256,2270,2274,2279,2284],{"__ignoreMap":27},[32,2180,2181],{"class":34,"line":35},[32,2182,2183],{"class":173},"# Explicit encode\u002Fdecode when working directly with bytes\n",[32,2185,2186,2189,2191],{"class":34,"line":81},[32,2187,2188],{"class":46},"text ",[32,2190,66],{"class":38},[32,2192,2193],{"class":50}," \"héllo wörld\"\n",[32,2195,2196,2199,2201,2204,2206],{"class":34,"line":99},[32,2197,2198],{"class":46},"encoded ",[32,2200,66],{"class":38},[32,2202,2203],{"class":46}," text.encode(",[32,2205,69],{"class":50},[32,2207,96],{"class":46},[32,2209,2210,2212,2215],{"class":34,"line":113},[32,2211,164],{"class":42},[32,2213,2214],{"class":46},"(encoded)                 ",[32,2216,2217],{"class":173},"# b'h\\xc3\\xa9llo w\\xc3\\xb6rld'\n",[32,2219,2220,2222,2224,2227,2230,2232,2235],{"class":34,"line":120},[32,2221,164],{"class":42},[32,2223,47],{"class":46},[32,2225,2226],{"class":42},"len",[32,2228,2229],{"class":46},"(text), ",[32,2231,2226],{"class":42},[32,2233,2234],{"class":46},"(encoded))   ",[32,2236,2237],{"class":173},"# 11 13 — multi-byte characters make byte length > character length\n",[32,2239,2240],{"class":34,"line":150},[32,2241,117],{"emptyLinePlaceholder":116},[32,2243,2244,2247,2249,2252,2254],{"class":34,"line":161},[32,2245,2246],{"class":46},"decoded ",[32,2248,66],{"class":38},[32,2250,2251],{"class":46}," encoded.decode(",[32,2253,69],{"class":50},[32,2255,96],{"class":46},[32,2257,2258,2260,2263,2265,2268],{"class":34,"line":170},[32,2259,164],{"class":42},[32,2261,2262],{"class":46},"(decoded ",[32,2264,1168],{"class":38},[32,2266,2267],{"class":46}," text)   ",[32,2269,819],{"class":173},[32,2271,2272],{"class":34,"line":177},[32,2273,117],{"emptyLinePlaceholder":116},[32,2275,2276],{"class":34,"line":183},[32,2277,2278],{"class":173},"# decoding with the WRONG encoding produces garbage or raises\n",[32,2280,2281],{"class":34,"line":188},[32,2282,2283],{"class":173},"# encoded.decode(\"ascii\")   # UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3\n",[32,2285,2286,2288,2291,2294,2297],{"class":34,"line":218},[32,2287,164],{"class":42},[32,2289,2290],{"class":46},"(encoded.decode(",[32,2292,2293],{"class":50},"\"latin-1\"",[32,2295,2296],{"class":46},"))   ",[32,2298,2299],{"class":173},"# \"hÃ©llo wÃ¶rld\" — decodes WITHOUT error, but WRONG text (mojibake)\n",[333,2301,2022,2302,2305,2306,54,2309,2311,2312,2315],{},[29,2303,2304],{},"latin-1"," example is the most dangerous encoding bug: unlike ",[29,2307,2308],{},"ascii",[29,2310,2304],{}," can decode ",[293,2313,2314],{},"any"," byte sequence without ever raising an error — meaning a wrong-encoding bug can silently produce corrupted text instead of a clear, catchable exception.",[14,2317,2319,2322],{"id":2318},"tempfile-safe-temporary-files",[29,2320,2321],{},"tempfile"," — Safe Temporary Files",[19,2324,2325],{"language":21},[23,2326,2328],{"className":25,"code":2327,"language":21,"meta":27,"style":27},"import tempfile\n\nwith tempfile.NamedTemporaryFile(mode=\"w\", suffix=\".txt\", delete=True, encoding=\"utf-8\") as tmp:\n    tmp.write(\"scratch data\")\n    tmp.flush()\n    print(tmp.name)             # the OS-assigned unique path\n# file is automatically deleted here, when the `with` block exits\n\nwith tempfile.TemporaryDirectory() as tmpdir:\n    print(tmpdir)                  # a fresh, unique directory\n    # ... write multiple files into it ...\n# entire directory tree is removed automatically on exit\n",[29,2329,2330,2337,2341,2389,2399,2404,2414,2419,2423,2435,2445,2450],{"__ignoreMap":27},[32,2331,2332,2334],{"class":34,"line":35},[32,2333,709],{"class":38},[32,2335,2336],{"class":46}," tempfile\n",[32,2338,2339],{"class":34,"line":81},[32,2340,117],{"emptyLinePlaceholder":116},[32,2342,2343,2345,2348,2351,2353,2355,2357,2360,2362,2365,2367,2370,2372,2374,2376,2378,2380,2382,2384,2386],{"class":34,"line":99},[32,2344,39],{"class":38},[32,2346,2347],{"class":46}," tempfile.NamedTemporaryFile(",[32,2349,2350],{"class":62},"mode",[32,2352,66],{"class":38},[32,2354,57],{"class":50},[32,2356,54],{"class":46},[32,2358,2359],{"class":62},"suffix",[32,2361,66],{"class":38},[32,2363,2364],{"class":50},"\".txt\"",[32,2366,54],{"class":46},[32,2368,2369],{"class":62},"delete",[32,2371,66],{"class":38},[32,2373,772],{"class":42},[32,2375,54],{"class":46},[32,2377,63],{"class":62},[32,2379,66],{"class":38},[32,2381,69],{"class":50},[32,2383,72],{"class":46},[32,2385,75],{"class":38},[32,2387,2388],{"class":46}," tmp:\n",[32,2390,2391,2394,2397],{"class":34,"line":113},[32,2392,2393],{"class":46},"    tmp.write(",[32,2395,2396],{"class":50},"\"scratch data\"",[32,2398,96],{"class":46},[32,2400,2401],{"class":34,"line":120},[32,2402,2403],{"class":46},"    tmp.flush()\n",[32,2405,2406,2408,2411],{"class":34,"line":150},[32,2407,896],{"class":42},[32,2409,2410],{"class":46},"(tmp.name)             ",[32,2412,2413],{"class":173},"# the OS-assigned unique path\n",[32,2415,2416],{"class":34,"line":161},[32,2417,2418],{"class":173},"# file is automatically deleted here, when the `with` block exits\n",[32,2420,2421],{"class":34,"line":170},[32,2422,117],{"emptyLinePlaceholder":116},[32,2424,2425,2427,2430,2432],{"class":34,"line":177},[32,2426,39],{"class":38},[32,2428,2429],{"class":46}," tempfile.TemporaryDirectory() ",[32,2431,75],{"class":38},[32,2433,2434],{"class":46}," tmpdir:\n",[32,2436,2437,2439,2442],{"class":34,"line":183},[32,2438,896],{"class":42},[32,2440,2441],{"class":46},"(tmpdir)                  ",[32,2443,2444],{"class":173},"# a fresh, unique directory\n",[32,2446,2447],{"class":34,"line":188},[32,2448,2449],{"class":173},"    # ... write multiple files into it ...\n",[32,2451,2452],{"class":34,"line":218},[32,2453,2454],{"class":173},"# entire directory tree is removed automatically on exit\n",[333,2456,2457,2459,2460,2463],{},[29,2458,2321],{}," guarantees unique, race-condition-free filenames — hand-rolling a \"temp file\" with a hardcoded or predictable name (",[29,2461,2462],{},"\u002Ftmp\u002Fmyapp_temp.txt",") is a real security and correctness hazard in any multi-process or multi-user environment, since two processes could collide on the same path.",[14,2465,2467],{"id":2466},"tips-tricks","💡 Tips & Tricks",[2469,2470,2471,2503,2512,2529,2547],"ul",{},[2472,2473,2474,1012,2477,354,2480,2483,2484,354,2487,2490,2491,354,2493,354,2496,2499,2500,2502],"li",{},[269,2475,2476],{},"Idiom",[29,2478,2479],{},"Path.read_text()",[29,2481,2482],{},"Path.write_text()"," and ",[29,2485,2486],{},"Path.read_bytes()",[29,2488,2489],{},"Path.write_bytes()"," replace the ",[29,2492,2029],{},[29,2494,2495],{},"read()",[29,2497,2498],{},"close()"," dance for simple one-shot file operations — shorter, and they close the file automatically even without an explicit ",[29,2501,39],{},".",[2472,2504,2505,1012,2508,2511],{},[269,2506,2507],{},"Debug",[29,2509,2510],{},"json.dumps(data, indent=2, sort_keys=True)"," produces stable, diffable, human-readable output — invaluable when comparing JSON snapshots in tests or debugging serialization differences across runs.",[2472,2513,2514,2517,2518,354,2521,2524,2525,2528],{},[269,2515,2516],{},"Performance",": for very large JSON files, ",[29,2519,2520],{},"json.load",[29,2522,2523],{},"json.dump"," still materialize the entire structure in memory — for genuinely huge files, streaming libraries like ",[29,2526,2527],{},"ijson"," avoid loading everything at once, the same lazy-vs-eager tradeoff from chapter 09's generators.",[2472,2530,2531,1012,2533,354,2536,2539,2540,354,2543,2546],{},[269,2532,2476],{},[29,2534,2535],{},"csv.DictReader",[29,2537,2538],{},"DictWriter"," are almost always preferable to the positional ",[29,2541,2542],{},"csv.reader",[29,2544,2545],{},"writer"," — column-name access survives column reordering in the source file, while positional indexing silently breaks.",[2472,2548,2549,2552,2553,2555,2556,2558],{},[269,2550,2551],{},"Safety",": use ",[29,2554,1600],{}," only for data you fully trust and control end-to-end; for anything crossing a process, network, or user boundary, prefer ",[29,2557,977],{}," (or a schema-validated format) specifically because it cannot execute code during deserialization.",[14,2560,2562],{"id":2561},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[2469,2564,2565,2585,2607,2621,2630],{},[2472,2566,2567,2577,2578,2581,2582,2584],{},[269,2568,2569,2570,2573,2574,2576],{},"Omitting ",[29,2571,2572],{},"encoding="," on ",[29,2575,2029],{}," for text files silently uses the platform's default encoding, not UTF-8"," — this doesn't error locally but can produce ",[29,2579,2580],{},"UnicodeDecodeError"," or silently garbled text when the same file is read on a different OS or locale; always pass ",[29,2583,2167],{}," explicitly.",[2472,2586,2587,2590,2591,2593,2594,2596,2597,354,2599,2602,2603,2606],{},[269,2588,2589],{},"Decoding bytes with the wrong encoding doesn't always raise an error"," — ",[29,2592,2304],{}," in particular can decode any byte sequence without error, silently producing corrupted (mojibake) text instead of a catchable exception; a caught ",[29,2595,2580],{}," from ",[29,2598,2308],{},[29,2600,2601],{},"utf-8"," is actually the ",[293,2604,2605],{},"safer"," failure mode.",[2472,2608,2609,2620],{},[269,2610,2611,2613,2614,2617,2618],{},[29,2612,977],{}," has no native tuple, set, or datetime type — tuples silently become lists on serialization, and sets\u002Fdatetimes raise ",[29,2615,2616],{},"TypeError"," unless handled via ",[29,2619,1441],{}," — round-tripping data through JSON is not guaranteed to preserve the original Python types.",[2472,2622,2623,2629],{},[269,2624,2625,2626,2628],{},"Never unpickle data from an untrusted source — ",[29,2627,1776],{}," can execute arbitrary code as part of reconstructing the object",", making it a genuine remote-code-execution vector when used on network payloads, user uploads, or any data crossing a trust boundary (fully covered in chapter 27).",[2472,2631,2632,2638,2639,2641,2642,2644],{},[269,2633,2634,2635,2637],{},"CSV files written or read without ",[29,2636,2025],{}," can produce extra blank rows on Windows",", because both Python's universal-newline text-mode translation and the ",[29,2640,1786],{}," module's own line-ending handling apply independently, effectively double-translating ",[29,2643,2036],{}," sequences.",[14,2646,2648],{"id":2647},"spot-the-bug","🧠 Spot the Bug",[333,2650,2651],{},"A script writes user-submitted comments to a log file and later reads them back for analysis on a different server. It works in local testing but corrupts non-English text in production. Find the bug.",[19,2653,2654],{"language":21},[23,2655,2657],{"className":25,"code":2656,"language":21,"meta":27,"style":27},"def save_comment(comment, path=\"comments.log\"):\n    with open(path, \"a\") as f:\n        f.write(comment + \"\\n\")\n\ndef load_comments(path=\"comments.log\"):\n    with open(path) as f:\n        return f.readlines()\n\nsave_comment(\"Café con leche, por favor\")\ncomments = load_comments()\nprint(comments)\n",[29,2658,2659,2677,2695,2712,2716,2732,2745,2751,2755,2765,2775],{"__ignoreMap":27},[32,2660,2661,2663,2666,2669,2671,2674],{"class":34,"line":35},[32,2662,1474],{"class":38},[32,2664,2665],{"class":1477}," save_comment",[32,2667,2668],{"class":46},"(comment, path",[32,2670,66],{"class":38},[32,2672,2673],{"class":50},"\"comments.log\"",[32,2675,2676],{"class":46},"):\n",[32,2678,2679,2682,2684,2687,2689,2691,2693],{"class":34,"line":81},[32,2680,2681],{"class":38},"    with",[32,2683,43],{"class":42},[32,2685,2686],{"class":46},"(path, ",[32,2688,201],{"class":50},[32,2690,72],{"class":46},[32,2692,75],{"class":38},[32,2694,78],{"class":46},[32,2696,2697,2700,2703,2706,2708,2710],{"class":34,"line":99},[32,2698,2699],{"class":46},"        f.write(comment ",[32,2701,2702],{"class":38},"+",[32,2704,2705],{"class":50}," \"",[32,2707,90],{"class":42},[32,2709,93],{"class":50},[32,2711,96],{"class":46},[32,2713,2714],{"class":34,"line":113},[32,2715,117],{"emptyLinePlaceholder":116},[32,2717,2718,2720,2723,2726,2728,2730],{"class":34,"line":120},[32,2719,1474],{"class":38},[32,2721,2722],{"class":1477}," load_comments",[32,2724,2725],{"class":46},"(path",[32,2727,66],{"class":38},[32,2729,2673],{"class":50},[32,2731,2676],{"class":46},[32,2733,2734,2736,2738,2741,2743],{"class":34,"line":150},[32,2735,2681],{"class":38},[32,2737,43],{"class":42},[32,2739,2740],{"class":46},"(path) ",[32,2742,75],{"class":38},[32,2744,78],{"class":46},[32,2746,2747,2749],{"class":34,"line":161},[32,2748,1497],{"class":38},[32,2750,621],{"class":46},[32,2752,2753],{"class":34,"line":170},[32,2754,117],{"emptyLinePlaceholder":116},[32,2756,2757,2760,2763],{"class":34,"line":177},[32,2758,2759],{"class":46},"save_comment(",[32,2761,2762],{"class":50},"\"Café con leche, por favor\"",[32,2764,96],{"class":46},[32,2766,2767,2770,2772],{"class":34,"line":183},[32,2768,2769],{"class":46},"comments ",[32,2771,66],{"class":38},[32,2773,2774],{"class":46}," load_comments()\n",[32,2776,2777,2779],{"class":34,"line":188},[32,2778,164],{"class":42},[32,2780,2781],{"class":46},"(comments)\n",[2783,2784,2785,2789,2813,2822,2925],"details",{},[2786,2787,2788],"summary",{},"Answer",[333,2790,2791,2792,2794,2795,2797,2798,2801,2802,2805,2806,2809,2810,2812],{},"Neither ",[29,2793,2029],{}," call specifies ",[29,2796,2572],{}," — both silently use ",[29,2799,2800],{},"locale.getpreferredencoding()",", which defaults to UTF-8 on most modern Linux\u002FmacOS but can default to something else (historically, ",[29,2803,2804],{},"cp1252"," or similar) depending on the OS and locale configuration. The developer's local machine and the production server can easily have different default encodings. Text containing non-ASCII characters (",[29,2807,2808],{},"é",", accented Spanish text) gets written using one encoding's byte representation, then read back assuming a different encoding — producing either garbled characters or a ",[29,2811,2580],{},", and the failure is completely invisible in local testing if the developer's machine happens to default to UTF-8 already.",[333,2814,2815,2816,2818,2819,2821],{},"The fix is to always pass ",[29,2817,2167],{}," explicitly on every text-mode ",[29,2820,2029],{}," call, matching on both the write and read side:",[19,2823,2824],{"language":21},[23,2825,2827],{"className":25,"code":2826,"language":21,"meta":27,"style":27},"def save_comment(comment, path=\"comments.log\"):\n    with open(path, \"a\", encoding=\"utf-8\") as f:\n        f.write(comment + \"\\n\")\n\ndef load_comments(path=\"comments.log\"):\n    with open(path, encoding=\"utf-8\") as f:\n        return f.readlines()\n",[29,2828,2829,2843,2867,2881,2885,2899,2919],{"__ignoreMap":27},[32,2830,2831,2833,2835,2837,2839,2841],{"class":34,"line":35},[32,2832,1474],{"class":38},[32,2834,2665],{"class":1477},[32,2836,2668],{"class":46},[32,2838,66],{"class":38},[32,2840,2673],{"class":50},[32,2842,2676],{"class":46},[32,2844,2845,2847,2849,2851,2853,2855,2857,2859,2861,2863,2865],{"class":34,"line":81},[32,2846,2681],{"class":38},[32,2848,43],{"class":42},[32,2850,2686],{"class":46},[32,2852,201],{"class":50},[32,2854,54],{"class":46},[32,2856,63],{"class":62},[32,2858,66],{"class":38},[32,2860,69],{"class":50},[32,2862,72],{"class":46},[32,2864,75],{"class":38},[32,2866,78],{"class":46},[32,2868,2869,2871,2873,2875,2877,2879],{"class":34,"line":99},[32,2870,2699],{"class":46},[32,2872,2702],{"class":38},[32,2874,2705],{"class":50},[32,2876,90],{"class":42},[32,2878,93],{"class":50},[32,2880,96],{"class":46},[32,2882,2883],{"class":34,"line":113},[32,2884,117],{"emptyLinePlaceholder":116},[32,2886,2887,2889,2891,2893,2895,2897],{"class":34,"line":120},[32,2888,1474],{"class":38},[32,2890,2722],{"class":1477},[32,2892,2725],{"class":46},[32,2894,66],{"class":38},[32,2896,2673],{"class":50},[32,2898,2676],{"class":46},[32,2900,2901,2903,2905,2907,2909,2911,2913,2915,2917],{"class":34,"line":150},[32,2902,2681],{"class":38},[32,2904,43],{"class":42},[32,2906,2686],{"class":46},[32,2908,63],{"class":62},[32,2910,66],{"class":38},[32,2912,69],{"class":50},[32,2914,72],{"class":46},[32,2916,75],{"class":38},[32,2918,78],{"class":46},[32,2920,2921,2923],{"class":34,"line":161},[32,2922,1497],{"class":38},[32,2924,621],{"class":46},[333,2926,2927,2930,2931,2933],{},[269,2928,2929],{},"The lesson",": relying on the platform default encoding is an environment-dependent bug that tests can pass locally while failing in production — always be explicit about ",[29,2932,2167],{}," for text I\u002FO, since \"works on my machine\" is exactly what a default-encoding mismatch produces.",[14,2935,2937],{"id":2936},"key-takeaways","Key Takeaways",[2469,2939,2940,2946,2952,2960,2968,2984],{},[2472,2941,2942,2943,2945],{},"Always use ",[29,2944,341],{}," — it guarantees the file closes (and buffered writes flush) even when an exception is raised mid-block.",[2472,2947,2948,2949,2951],{},"Always pass ",[29,2950,2167],{}," explicitly for text-mode file operations; relying on the platform default is a silent, environment-dependent bug waiting to happen.",[2472,2953,2954,2956,2957,2959],{},[29,2955,937],{}," is the modern, cross-platform way to build and manipulate paths — prefer it over manual string concatenation or ",[29,2958,970],{}," for new code.",[2472,2961,2962,2964,2965,2967],{},[29,2963,977],{}," is safe for untrusted data but loses tuple\u002Fset\u002Fdatetime fidelity; ",[29,2966,1600],{}," preserves full Python object fidelity but must never be used on untrusted input, since deserializing it can execute arbitrary code.",[2472,2969,2970,354,2972,2974,2975,2977,2978,354,2981,2983],{},[29,2971,2535],{},[29,2973,2538],{}," (with ",[29,2976,2025],{}," on the file) are more robust than positional ",[29,2979,2980],{},"reader",[29,2982,2545],{},", surviving column reordering and avoiding platform-specific blank-row bugs.",[2472,2985,2986,2988],{},[29,2987,2321],{}," provides race-condition-free unique temporary files\u002Fdirectories — never hand-roll a \"temp\" filename in shared or multi-process environments.",[2990,2991,2992],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}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);}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}",{"title":27,"searchDepth":81,"depth":81,"links":2994},[2995,2996,2997,2999,3003,3005,3007,3009,3011,3012,3013,3014],{"id":16,"depth":81,"text":17},{"id":458,"depth":81,"text":459},{"id":687,"depth":81,"text":2998},"pathlib — The Modern Way to Handle Paths",{"id":974,"depth":81,"text":3000,"children":3001},"json — The Ubiquitous Interchange Format",[3002],{"id":1267,"depth":99,"text":1268},{"id":1597,"depth":81,"text":3004},"pickle — Full Python Object Serialization",{"id":1783,"depth":81,"text":3006},"csv — Tabular Data",{"id":2040,"depth":81,"text":3008},"Encoding Pitfalls: str vs bytes",{"id":2318,"depth":81,"text":3010},"tempfile — Safe Temporary Files",{"id":2466,"depth":81,"text":2467},{"id":2561,"depth":81,"text":2562},{"id":2647,"depth":81,"text":2648},{"id":2936,"depth":81,"text":2937},"md",{},"\u002Fpython\u002F18-file-io-and-serialization",{"title":5,"description":27},"python\u002F18-file-io-and-serialization","q1isOnAt7ySAcqqpF00BkEirYqcV73sObL8OettG6Jg",1789924651648]