[{"data":1,"prerenderedAt":561},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-import-system-explained":3},{"id":4,"title":5,"body":6,"description":547,"difficulty":548,"extension":549,"framework":550,"frameworkSlug":48,"meta":551,"navigation":97,"order":56,"path":552,"qaPath":553,"seo":554,"stem":555,"subtopic":556,"topic":557,"topicSlug":558,"updated":559,"__hash__":560},"blog\u002Fblog\u002Fpython-import-system-explained.md","Python Import System Explained — Modules, sys.path, Caching, and Absolute vs Relative Imports",{"type":7,"value":8,"toc":537},"minimark",[9,14,22,26,43,138,145,149,160,206,213,217,228,264,275,279,289,344,351,355,365,438,445,449,456,502,506,533],[10,11,13],"h2",{"id":12},"pythons-import-system-explained","Python's import system, explained",[15,16,17,21],"p",{},[18,19,20],"code",{},"import"," looks trivial until a circular import or a \"module not found\" error stops you cold.\nUnderneath, importing is a well-defined process: find the module, load and execute it once,\ncache it, and bind a name. Knowing the steps makes import problems easy to diagnose.",[10,23,25],{"id":24},"what-import-actually-does","What import actually does",[15,27,28,29,33,34,37,38,42],{},"The first time you import a module, Python ",[30,31,32],"strong",{},"finds"," it, ",[30,35,36],{},"executes its top-level code once",",\nbuilds a module object, caches it, and binds a name in your namespace. Re-importing reuses\nthe cache — the code does ",[39,40,41],"em",{},"not"," run again.",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-python shiki shiki-themes github-light github-dark","# greet.py\nprint(\"greet module loading\")\nGREETING = \"hello\"\n\n# main.py\nimport greet      # prints \"greet module loading\"\nimport greet      # prints nothing — already cached\nprint(greet.GREETING)\n","python","",[18,51,52,61,79,92,99,105,116,126],{"__ignoreMap":49},[53,54,57],"span",{"class":55,"line":56},"line",1,[53,58,60],{"class":59},"sJ8bj","# greet.py\n",[53,62,64,68,72,76],{"class":55,"line":63},2,[53,65,67],{"class":66},"sj4cs","print",[53,69,71],{"class":70},"sVt8B","(",[53,73,75],{"class":74},"sZZnC","\"greet module loading\"",[53,77,78],{"class":70},")\n",[53,80,82,85,89],{"class":55,"line":81},3,[53,83,84],{"class":66},"GREETING",[53,86,88],{"class":87},"szBVR"," =",[53,90,91],{"class":74}," \"hello\"\n",[53,93,95],{"class":55,"line":94},4,[53,96,98],{"emptyLinePlaceholder":97},true,"\n",[53,100,102],{"class":55,"line":101},5,[53,103,104],{"class":59},"# main.py\n",[53,106,108,110,113],{"class":55,"line":107},6,[53,109,20],{"class":87},[53,111,112],{"class":70}," greet      ",[53,114,115],{"class":59},"# prints \"greet module loading\"\n",[53,117,119,121,123],{"class":55,"line":118},7,[53,120,20],{"class":87},[53,122,112],{"class":70},[53,124,125],{"class":59},"# prints nothing — already cached\n",[53,127,129,131,134,136],{"class":55,"line":128},8,[53,130,67],{"class":66},[53,132,133],{"class":70},"(greet.",[53,135,84],{"class":66},[53,137,78],{"class":70},[15,139,140,141,144],{},"That \"runs once\" rule is why putting executable side effects at module top level can\nsurprise you, and why the ",[18,142,143],{},"if __name__ == \"__main__\""," guard exists.",[10,146,148],{"id":147},"sysmodules-is-the-cache","sys.modules is the cache",[15,150,151,152,155,156,159],{},"Imported modules are stored in the ",[18,153,154],{},"sys.modules"," dict, keyed by name. Python checks it\n",[39,157,158],{},"before"," searching the filesystem, which is what makes repeat imports instant.",[44,161,163],{"className":46,"code":162,"language":48,"meta":49,"style":49},"import sys\nimport json\n\"json\" in sys.modules      # True\nsys.modules[\"json\"]        # \u003Cmodule 'json' ...>\n",[18,164,165,172,179,193],{"__ignoreMap":49},[53,166,167,169],{"class":55,"line":56},[53,168,20],{"class":87},[53,170,171],{"class":70}," sys\n",[53,173,174,176],{"class":55,"line":63},[53,175,20],{"class":87},[53,177,178],{"class":70}," json\n",[53,180,181,184,187,190],{"class":55,"line":81},[53,182,183],{"class":74},"\"json\"",[53,185,186],{"class":87}," in",[53,188,189],{"class":70}," sys.modules      ",[53,191,192],{"class":59},"# True\n",[53,194,195,198,200,203],{"class":55,"line":94},[53,196,197],{"class":70},"sys.modules[",[53,199,183],{"class":74},[53,201,202],{"class":70},"]        ",[53,204,205],{"class":59},"# \u003Cmodule 'json' ...>\n",[15,207,208,209,212],{},"You can even force a reload with ",[18,210,211],{},"importlib.reload",", but in normal code the cache is exactly\nwhat you want.",[10,214,216],{"id":215},"syspath-where-python-looks","sys.path: where Python looks",[15,218,219,220,223,224,227],{},"To find a module, Python searches the directories in ",[18,221,222],{},"sys.path",", in order: the script's\ndirectory (or cwd), then ",[18,225,226],{},"PYTHONPATH"," entries, then installed\u002Fstandard-library locations.",[44,229,231],{"className":46,"code":230,"language":48,"meta":49,"style":49},"import sys\nfor p in sys.path:\n    print(p)        # first match wins\n",[18,232,233,239,253],{"__ignoreMap":49},[53,234,235,237],{"class":55,"line":56},[53,236,20],{"class":87},[53,238,171],{"class":70},[53,240,241,244,247,250],{"class":55,"line":63},[53,242,243],{"class":87},"for",[53,245,246],{"class":70}," p ",[53,248,249],{"class":87},"in",[53,251,252],{"class":70}," sys.path:\n",[53,254,255,258,261],{"class":55,"line":81},[53,256,257],{"class":66},"    print",[53,259,260],{"class":70},"(p)        ",[53,262,263],{"class":59},"# first match wins\n",[15,265,266,267,270,271,274],{},"This is why a local file named ",[18,268,269],{},"random.py"," can ",[30,272,273],{},"shadow"," the standard library — your\ndirectory is searched first. Naming files after stdlib modules is a classic self-inflicted\nbug.",[10,276,278],{"id":277},"absolute-vs-relative-imports","Absolute vs relative imports",[15,280,281,284,285,288],{},[30,282,283],{},"Absolute"," imports name the full path from a top-level package and are the recommended\ndefault. ",[30,286,287],{},"Relative"," imports use leading dots to reference the current package, handy inside\na package.",[44,290,292],{"className":46,"code":291,"language":48,"meta":49,"style":49},"# absolute — clear and unambiguous\nfrom myapp.utils.text import slugify\n\n# relative — . is current package, .. is parent\nfrom .text import slugify\nfrom ..models import User\n",[18,293,294,299,312,316,321,332],{"__ignoreMap":49},[53,295,296],{"class":55,"line":56},[53,297,298],{"class":59},"# absolute — clear and unambiguous\n",[53,300,301,304,307,309],{"class":55,"line":63},[53,302,303],{"class":87},"from",[53,305,306],{"class":70}," myapp.utils.text ",[53,308,20],{"class":87},[53,310,311],{"class":70}," slugify\n",[53,313,314],{"class":55,"line":81},[53,315,98],{"emptyLinePlaceholder":97},[53,317,318],{"class":55,"line":94},[53,319,320],{"class":59},"# relative — . is current package, .. is parent\n",[53,322,323,325,328,330],{"class":55,"line":101},[53,324,303],{"class":87},[53,326,327],{"class":70}," .text ",[53,329,20],{"class":87},[53,331,311],{"class":70},[53,333,334,336,339,341],{"class":55,"line":107},[53,335,303],{"class":87},[53,337,338],{"class":70}," ..models ",[53,340,20],{"class":87},[53,342,343],{"class":70}," User\n",[15,345,346,347,350],{},"Relative imports only work ",[30,348,349],{},"inside a package"," (a module that was imported as part of one),\nnot in a script run directly — a frequent source of \"attempted relative import\" errors.",[10,352,354],{"id":353},"import-vs-from-import","import vs from import",[15,356,357,360,361,364],{},[18,358,359],{},"import module"," binds the module name; ",[18,362,363],{},"from module import name"," binds the name directly.\nThe latter is convenient but copies a reference at import time, so rebinding the original\nlater won't be seen.",[44,366,368],{"className":46,"code":367,"language":48,"meta":49,"style":49},"import math\nmath.pi                     # access through the module\n\nfrom math import pi, sqrt   # bind names directly\nsqrt(16)\n\nfrom math import sqrt as square_root   # alias to avoid clashes\n",[18,369,370,377,385,389,404,414,418],{"__ignoreMap":49},[53,371,372,374],{"class":55,"line":56},[53,373,20],{"class":87},[53,375,376],{"class":70}," math\n",[53,378,379,382],{"class":55,"line":63},[53,380,381],{"class":70},"math.pi                     ",[53,383,384],{"class":59},"# access through the module\n",[53,386,387],{"class":55,"line":81},[53,388,98],{"emptyLinePlaceholder":97},[53,390,391,393,396,398,401],{"class":55,"line":94},[53,392,303],{"class":87},[53,394,395],{"class":70}," math ",[53,397,20],{"class":87},[53,399,400],{"class":70}," pi, sqrt   ",[53,402,403],{"class":59},"# bind names directly\n",[53,405,406,409,412],{"class":55,"line":101},[53,407,408],{"class":70},"sqrt(",[53,410,411],{"class":66},"16",[53,413,78],{"class":70},[53,415,416],{"class":55,"line":107},[53,417,98],{"emptyLinePlaceholder":97},[53,419,420,422,424,426,429,432,435],{"class":55,"line":118},[53,421,303],{"class":87},[53,423,395],{"class":70},[53,425,20],{"class":87},[53,427,428],{"class":70}," sqrt ",[53,430,431],{"class":87},"as",[53,433,434],{"class":70}," square_root   ",[53,436,437],{"class":59},"# alias to avoid clashes\n",[15,439,440,441,444],{},"Avoid ",[18,442,443],{},"from module import *"," outside the REPL — it hides where names come from and can\nclobber locals.",[10,446,448],{"id":447},"circular-imports","Circular imports",[15,450,451,452,455],{},"If module A imports B while B imports A, one of them runs into a half-initialised module and\nsome names won't exist yet. The fixes: restructure to remove the cycle, move the import\n",[30,453,454],{},"inside the function"," that needs it (deferring it past module load), or import the module\nrather than its names.",[44,457,459],{"className":46,"code":458,"language":48,"meta":49,"style":49},"# Instead of a top-level \"from b import thing\" that cycles:\ndef do_work():\n    from b import thing      # imported lazily, after both modules finish loading\n    return thing()\n",[18,460,461,466,478,494],{"__ignoreMap":49},[53,462,463],{"class":55,"line":56},[53,464,465],{"class":59},"# Instead of a top-level \"from b import thing\" that cycles:\n",[53,467,468,471,475],{"class":55,"line":63},[53,469,470],{"class":87},"def",[53,472,474],{"class":473},"sScJk"," do_work",[53,476,477],{"class":70},"():\n",[53,479,480,483,486,488,491],{"class":55,"line":81},[53,481,482],{"class":87},"    from",[53,484,485],{"class":70}," b ",[53,487,20],{"class":87},[53,489,490],{"class":70}," thing      ",[53,492,493],{"class":59},"# imported lazily, after both modules finish loading\n",[53,495,496,499],{"class":55,"line":94},[53,497,498],{"class":87},"    return",[53,500,501],{"class":70}," thing()\n",[10,503,505],{"id":504},"recap","Recap",[15,507,508,509,512,513,515,516,520,521,524,525,528,529,532],{},"Importing ",[30,510,511],{},"finds, executes once, caches, and binds",": top-level module code runs a single\ntime, and ",[18,514,154],{}," caches the result so re-imports are instant. Modules are located by\nsearching ",[30,517,518],{},[18,519,222],{}," in order (your directory first — beware shadowing stdlib names).\nPrefer ",[30,522,523],{},"absolute"," imports; relative (dotted) imports work only inside packages. ",[18,526,527],{},"from X import name"," binds names directly but copies references. Break ",[30,530,531],{},"circular imports"," by\nrestructuring or deferring the import inside a function.",[534,535,536],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":49,"searchDepth":63,"depth":63,"links":538},[539,540,541,542,543,544,545,546],{"id":12,"depth":63,"text":13},{"id":24,"depth":63,"text":25},{"id":147,"depth":63,"text":148},{"id":215,"depth":63,"text":216},{"id":277,"depth":63,"text":278},{"id":353,"depth":63,"text":354},{"id":447,"depth":63,"text":448},{"id":504,"depth":63,"text":505},"How Python's import system works — what happens on import, module caching in sys.modules, how sys.path is searched, absolute vs relative imports, and avoiding circular import problems.","medium","md","Python",{},"\u002Fblog\u002Fpython-import-system-explained","\u002Fpython\u002Fmodules\u002Fimports",{"title":5,"description":547},"blog\u002Fpython-import-system-explained","The Import System","Modules, Packages & Environments","modules","2026-06-19","jVpfoFX_U0N9A8PHHuB2FAcJZu5awLJR6aj6tHi_Ugk",1782244092209]