{"id":354,"date":"2026-07-15T17:10:43","date_gmt":"2026-07-15T09:10:43","guid":{"rendered":"https:\/\/numsimlab.com\/?p=354"},"modified":"2026-07-15T17:10:43","modified_gmt":"2026-07-15T09:10:43","slug":"python-%e5%ba%93%e5%bc%80%e5%8f%91%e5%ae%8c%e6%95%b4%e6%8c%87%e5%8d%97","status":"publish","type":"post","link":"https:\/\/numsimlab.com\/?p=354","title":{"rendered":"Python \u5e93\u5f00\u53d1\u5b8c\u6574\u6307\u5357"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Python \u5e93\u5f00\u53d1\u5b8c\u6574\u6307\u5357 \u00b7 Python Library Development Guide<\/title>\n<style>\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\nhtml{scroll-behavior:smooth}\n:root{\n  --bg:#f8f9fa;--bg2:#fff;--ink:#1a1a2e;--muted:#6c7086;--rule:#e2e4eb;\n  --accent:#2563eb;--accent2:#16a34a;--accent3:#dc2626;--accent4:#d97706;--accent5:#7c3aed;\n  --card-bg:#fff;--code-bg:#f1f3f5;\n  --tag-g:#dcfce7;--tag-g-t:#166534;--tag-b:#dbeafe;--tag-b-t:#1e40af;\n  --tag-r:#fee2e2;--tag-r-t:#991b1b;--tag-y:#fef9c3;--tag-y-t:#713f12;--tag-p:#f3e8ff;--tag-p-t:#581c87;\n  --shadow:0 1px 3px rgba(0,0,.08);--radius:8px;--max-w:940px\n}\n[data-theme=\"dark\"]{\n  --bg:#0b0b1a;--bg2:#16162a;--ink:#e2e4f0;--muted:#7f8396;--rule:#26263e;\n  --accent:#60a5fa;--accent2:#4ade80;--accent3:#f87171;--accent4:#fbbf24;--accent5:#a78bfa;\n  --card-bg:#16162a;--code-bg:#1e1e36;\n  --tag-g:#14532d;--tag-g-t:#bbf7d0;--tag-b:#1e3a5f;--tag-b-t:#bfdbfe;\n  --tag-r:#7f1d1d;--tag-r-t:#fecaca;--tag-y:#713f12;--tag-y-t:#fef08a;\n  --tag-p:#3b1f6e;--tag-p-t:#e9d5ff;\n  --shadow:0 1px 3px rgba(0,0,.3)\n}\nbody{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",\"Noto Sans SC\",\"PingFang SC\",\"Microsoft YaHei\",sans-serif;background:var(--bg);color:var(--ink);line-height:1.7;font-size:15px;transition:background .3s,color .3s}\nheader{position:fixed;top:0;left:0;right:0;z-index:100;background:var(--bg2);border-bottom:1px solid var(--rule);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);transition:background .3s}\n.header-inner{max-width:var(--max-w);margin:0 auto;display:flex;align-items:center;justify-content:space-between;padding:0 1.5rem;height:56px}\n.logo{font-weight:700;font-size:.95rem;color:var(--accent);display:flex;align-items:center;gap:.4rem}\n.controls{display:flex;align-items:center;gap:.5rem}\n.lang-switch{display:flex;border:1px solid var(--rule);border-radius:6px;overflow:hidden}\n.lang-switch button{background:0 0;border:none;color:var(--muted);padding:.3rem .7rem;font-size:.8rem;cursor:pointer;transition:all .2s;font-family:inherit}\n.lang-switch button.active{background:var(--accent);color:#fff}\n.lang-switch button:not(.active):hover{color:var(--ink);background:var(--rule)}\n.theme-toggle{background:0 0;border:1px solid var(--rule);border-radius:6px;color:var(--muted);width:34px;height:34px;font-size:1rem;cursor:pointer;display:flex;align-items:center;transition:all .2s}\n.theme-toggle:hover{color:var(--ink);background:var(--rule)}\nmain{max-width:var(--max-w);margin:0 auto;padding:80px 1.5rem 3rem}\n.lang-section{display:none}.lang-section.active{display:block}\nh1{font-size:1.9rem;font-weight:800;line-height:1.3;margin-bottom:.5rem;letter-spacing:-.02em}\nh1 .sub{display:block;font-size:.85rem;font-weight:400;color:var(--muted);margin-top:.25rem;letter-spacing:0}\nh2{font-size:1.35rem;font-weight:700;margin:2.5rem 0 1rem;padding-bottom:.5rem;border-bottom:2px solid var(--accent);letter-spacing:-.01em}\nh3{font-size:1.05rem;font-weight:600;margin:1.5rem 0 .6rem;color:var(--accent)}\nh4{font-size:.95rem;font-weight:600;margin:1rem 0 .4rem;color:var(--ink)}\np{margin-bottom:.8rem}\na{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}\n.hero{text-align:center;padding:2rem 0 1rem}\n.hero p{font-size:1rem;color:var(--muted);max-width:700px;margin:0 auto}\n.tag{display:inline-block;padding:.12rem .5rem;border-radius:4px;font-size:.72rem;font-weight:600;margin-right:.25rem;white-space:nowrap}\n.tag-g{background:var(--tag-g);color:var(--tag-g-t)}.tag-b{background:var(--tag-b);color:var(--tag-b-t)}\n.tag-r{background:var(--tag-r);color:var(--tag-r-t)}.tag-y{background:var(--tag-y);color:var(--tag-y-t)}.tag-p{background:var(--tag-p);color:var(--tag-p-t)}\n.card{background:var(--card-bg);border:1px solid var(--rule);border-radius:var(--radius);padding:1.25rem;margin-bottom:1rem;box-shadow:var(--shadow)}\n.info-box{background:var(--bg);border-left:4px solid var(--accent);padding:.9rem 1.1rem;margin:1rem 0;border-radius:0 var(--radius) var(--radius) 0;font-size:.88rem}\n.info-box strong{color:var(--accent)}\n.info-box.warn{border-left-color:var(--accent4)}.info-box.warn strong{color:var(--accent4)}\n.info-box.danger{border-left-color:var(--accent3)}.info-box.danger strong{color:var(--accent3)}\n.toc{background:var(--card-bg);border:1px solid var(--rule);border-radius:var(--radius);padding:1.15rem 1.4rem;margin-bottom:2rem;box-shadow:var(--shadow)}\n.toc h3{margin:0 0 .5rem;font-size:.9rem;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}\n.toc ol{padding-left:1.1rem;columns:2;column-gap:2rem}\n.toc li{margin-bottom:.25rem;font-size:.88rem}.toc a{color:var(--ink)}.toc a:hover{color:var(--accent);text-decoration:none}\npre.code-block{background:var(--code-bg);padding:.85rem 1rem;border-radius:var(--radius);overflow-x:auto;font-family:\"SF Mono\",Consolas,\"Courier New\",monospace;font-size:.82rem;line-height:1.5;margin:.6rem 0;border:1px solid var(--rule)}\ncode,.inline-code{background:var(--code-bg);padding:.12rem .4rem;border-radius:4px;font-family:\"SF Mono\",Consolas,\"Courier New\",monospace;font-size:.83rem;border:1px solid var(--rule)}\n.table-wrap{overflow-x:auto;margin:1.2rem 0}\ntable{width:100%;border-collapse:collapse;font-size:.82rem}\nthead th{background:var(--accent);color:#fff;padding:.6rem .5rem;text-align:center;font-weight:600;white-space:nowrap}\nthead th:first-child{border-radius:6px 0 0 0}thead th:last-child{border-radius:0 6px 0 0}\ntbody td{padding:.55rem .5rem;border-bottom:1px solid var(--rule);text-align:center;vertical-align:middle}\ntbody tr:nth-child(even){background:var(--bg)}\ntbody td:first-child{text-align:left;font-weight:600}\n.terminal{background:#1a1a2e;color:#a8e6cf;border-radius:var(--radius);padding:.9rem 1rem;font-family:\"SF Mono\",Consolas,\"Courier New\",monospace;font-size:.82rem;line-height:1.6;margin:.6rem 0;overflow-x:auto;border:1px solid #333}\n.terminal .prompt{color:#60a5fa}.terminal .comment{color:#6c7086;font-style:italic}.terminal .output{color:#e2e4f0}\n[data-theme=\"dark\"] .terminal{background:#08081a}\n.steps{counter-reset:step;list-style:none;padding:0}\n.steps li{counter-increment:step;position:relative;padding:.85rem 1rem .85rem 2.8rem;margin-bottom:.5rem;border-left:2px solid var(--rule);border-radius:0 var(--radius) 0;background:var(--card-bg);box-shadow:var(--shadow)}\n.steps li::before{content:counter(step);position:absolute;left:-13px;top:.85rem;width:26px;height:26px;border-radius:50%;background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;font-size:.78rem;font-weight:700}\n.steps li .cmd{display:block;background:var(--code-bg);padding:.4rem .7rem;border-radius:5px;font-family:\"SF Mono\",Consolas,\"Courier New\",monospace;font-size:.82rem;margin:.3rem 0;overflow-x:auto;border:1px solid var(--rule)}\n.steps li .note{display:block;color:var(--muted);font-size:.82rem;margin-top:.2rem}\n.flow-diagram{background:var(--card-bg);border:1px solid var(--rule);border-radius:var(--radius);padding:1.25rem;margin:1.25rem 0;text-align:center}\n.flow-row{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:.35rem;margin:.4rem 0}\n.flow-item{display:inline-block;padding:.4rem .8rem;border-radius:6px;font-weight:600;font-size:.82rem;text-align:center;background:var(--accent);color:#fff;min-width:60px}\n.flow-item.green{background:var(--accent2)}.flow-item.red{background:var(--accent3)}.flow-item.yellow{background:var(--accent4);color:#1a1a2e}.flow-item.purple{background:var(--accent5)}\n.flow-arrow{color:var(--muted);font-size:1rem}\n.cmd-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:.75rem;margin:1rem 0}\n.cmd-card{background:var(--card-bg);border:1px solid var(--rule);border-radius:var(--radius);padding:.9rem;box-shadow:var(--shadow)}\n.cmd-card h4{margin:0 0 .35rem;font-size:.88rem;color:var(--accent)}\n.cmd-card .cmd{font-family:\"SF Mono\",Consolas,\"Courier New\",monospace;font-size:.8rem;background:var(--code-bg);padding:.3rem .5rem;border-radius:4px;display:block;margin-bottom:.3rem;border:1px solid var(--rule)}\n.cmd-card .desc{font-size:.8rem;color:var(--muted)}\n.two-col{display:grid;grid-template-columns:1fr 1fr;gap:1.25rem;margin:1.25rem 0}\n.col{background:var(--card-bg);border:1px solid var(--rule);border-radius:var(--radius);padding:1.25rem;box-shadow:var(--shadow)}\n.col h4{margin-top:0}\n.compare-box{display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin:1rem 0}\n.compare-col{background:var(--card-bg);border:1px solid var(--rule);border-radius:var(--radius);padding:1rem;box-shadow:var(--shadow)}\n.compare-col h4{margin:0 0 .4rem;font-size:.88rem}\n.compare-col.bad h4{color:var(--accent3)}\n.compare-col.good h4{color:var(--accent2)}\npre.example{background:var(--code-bg);padding:.6rem .8rem;border-radius:6px;font-family:\"SF Mono\",Consolas,\"Courier New\",monospace;font-size:.78rem;line-height:1.5;margin:.4rem 0;border:1px solid var(--rule);color:var(--ink)}\nfooter{max-width:var(--max-w);margin:3rem auto 0;padding:2rem 1.5rem 3rem;border-top:1px solid var(--rule);text-align:center;font-size:.82rem;color:var(--muted)}\nfooter .links{display:flex;justify-content:center;gap:1.5rem;flex-wrap:wrap;margin-bottom:.8rem}\nfooter .links a{color:var(--muted)}footer .links a:hover{color:var(--accent)}\n@media(max-width:640px){\n  h1{font-size:1.4rem}.toc ol{columns:1}.header-inner{padding:0 1rem}main{padding:72px 1rem 2rem}\n  .steps li{padding-left:2.4rem}.steps li::before{left:-11px;width:22px;height:22px;font-size:.7rem}\n  .two-col{grid-template-columns:1fr}.cmd-grid{grid-template-columns:1fr}.compare-box{grid-template-columns:1fr}\n}\n<\/style>\n<\/head>\n<body>\n<header>\n  <div class=\"header-inner\">\n    <div class=\"logo\"><span>\ud83d\udce6<\/span> Python \u5e93\u5f00\u53d1\u5b8c\u6574\u6307\u5357<\/div>\n    <div class=\"controls\">\n      <div class=\"lang-switch\">\n        <button class=\"active\" onclick=\"setLang('zh')\" id=\"btn-zh\">\u4e2d\u6587<\/button>\n        <button onclick=\"setLang('en')\" id=\"btn-en\">English<\/button>\n      <\/div>\n      <button class=\"theme-toggle\" onclick=\"toggleTheme()\" title=\"\u5207\u6362\u660e\u6697\u4e3b\u9898\">&#9788;<\/button>\n    <\/div>\n  <\/div>\n<\/header>\n<main>\n<!-- ======== \u4e2d\u6587\u7248 ======== -->\n<div class=\"lang-section active\" id=\"lang-zh\">\n<div class=\"hero\">\n  <h1>Python \u5e93\u5f00\u53d1\u96f6\u57fa\u7840\u5b8c\u6574\u6559\u7a0b\uff08\u9879\u76ee\u2192\u6253\u5305\u2192\u53d1\u5e03\u5168\u6d41\u7a0b\uff09<span class=\"sub\">\u4ece\u9879\u76ee\u7ed3\u6784\u8bbe\u8ba1\u5230 PyPI \u53d1\u5e03\uff0c\u4e00\u7ad9\u5f0f\u638c\u63e1 Python \u5e93\u5f00\u53d1<\/span><\/h1>\n  <p>\u4ec0\u4e48\u662f Python \u5e93\u3001\u9879\u76ee\u7ed3\u6784\u3001pyproject.toml \u914d\u7f6e\u3001src \u5e03\u5c40\u3001\u7f16\u5199\u4ee3\u7801\u3001\u6253\u5305\u5206\u53d1\u3001PyPI \u53d1\u5e03\u3001\u7248\u672c\u7ba1\u7406\u3001\u6d4b\u8bd5\u4e0e\u6587\u6863\u5168\u8986\u76d6<\/p>\n<\/div>\n\n<div class=\"toc\">\n  <h3>\u76ee\u5f55<\/h3>\n  <ol>\n    <li><a href=\"#zh-1\">\u4e00\u3001\u4ec0\u4e48\u662f Python \u5e93\uff0c\u89e3\u51b3\u4ec0\u4e48\u6838\u5fc3\u95ee\u9898<\/a><\/li>\n    <li><a href=\"#zh-2\">\u4e8c\u3001\u9879\u76ee\u7ed3\u6784\u8bbe\u8ba1\uff08src layout \u6807\u51c6\uff09<\/a><\/li>\n    <li><a href=\"#zh-3\">\u4e09\u3001pyproject.toml \u5b8c\u6574\u914d\u7f6e<\/a><\/li>\n    <li><a href=\"#zh-4\">\u56db\u3001\u7f16\u5199\u5e93\u4ee3\u7801\u4e0e __init__.py<\/a><\/li>\n    <li><a href=\"#zh-5\">\u4e94\u3001\u4f9d\u8d56\u7ba1\u7406\u4e0e\u7248\u672c\u63a7\u5236<\/a><\/li>\n    <li><a href=\"#zh-6\">\u516d\u3001\u6784\u5efa\u4e0e\u6253\u5305\uff08uv \/ setuptools\uff09<\/a><\/li>\n    <li><a href=\"#zh-7\">\u4e03\u3001\u53d1\u5e03\u5230 PyPI \u5168\u6d41\u7a0b<\/a><\/li>\n    <li><a href=\"#zh-8\">\u516b\u3001\u6d4b\u8bd5\u4e0e CI \u96c6\u6210<\/a><\/li>\n    <li><a href=\"#zh-9\">\u4e5d\u3001\u6587\u6863\u7f16\u5199\u4e0e\u81ea\u52a8\u5316<\/a><\/li>\n    <li><a href=\"#zh-10\">\u5341\u3001\u6700\u4f73\u5b9e\u8df5\u4e0e\u5e38\u89c1\u95ee\u9898<\/a><\/li>\n  <\/ol>\n<\/div>\n\n<!-- \u4e00\u3001\u4ec0\u4e48\u662f Python \u5e93 -->\n<h2 id=\"zh-1\">\u4e00\u3001\u4ec0\u4e48\u662f Python \u5e93\uff0c\u89e3\u51b3\u4ec0\u4e48\u6838\u5fc3\u95ee\u9898<\/h2>\n<p>Python \u5e93\uff08Library \/ Package\uff09\u662f\u4e00\u7ec4\u53ef\u590d\u7528\u4ee3\u7801\u7684\u96c6\u5408\uff0c\u5c01\u88c5\u4e3a\u7edf\u4e00\u63a5\u53e3\u4f9b\u5176\u4ed6\u9879\u76ee\u901a\u8fc7 <code>pip install<\/code> \u5b89\u88c5\u4f7f\u7528\u3002<\/p>\n\n<div class=\"compare-box\">\n  <div class=\"compare-col bad\">\n    <h4>\u274c \u811a\u672c\u65b9\u5f0f\uff08\u96be\u4ee5\u590d\u7528\uff09<\/h4>\n<pre class=\"example\"># \u590d\u5236\u7c98\u8d34 utils.py \u5230\u6bcf\u4e2a\u9879\u76ee\n# \u7248\u672c\u6df7\u4e71\uff0c\u65e0\u7edf\u4e00\u7ba1\u7406\n# \u624b\u52a8\u5904\u7406\u4f9d\u8d56\n# \u65e0\u6cd5 pip install<\/pre>\n    <p>\u4ee3\u7801\u6563\u843d\u5728\u5404\u9879\u76ee\u4e2d\uff0c\u66f4\u65b0\u9700\u9010\u4e00\u4fee\u6539\uff0c\u7f3a\u4e4f\u7248\u672c\u63a7\u5236\u548c\u4f9d\u8d56\u7ba1\u7406\u3002<\/p>\n  <\/div>\n  <div class=\"compare-col good\">\n    <h4>\u2705 \u6807\u51c6\u5316\u5e93\u5f00\u53d1<\/h4>\n<pre class=\"example\"># pip install mylib\nfrom mylib import helper\nhelper.do_something()\n# \u7248\u672c\u7ba1\u7406\uff0c\u4f9d\u8d56\u58f0\u660e\n# \u81ea\u52a8\u5904\u7406\u4f9d\u8d56\u6811<\/pre>\n    <p>\u4ee3\u7801\u96c6\u4e2d\u7ef4\u62a4\uff0cpip \u4e00\u952e\u5b89\u88c5\uff0c\u7248\u672c\u8bed\u4e49\u5316\uff0c\u4f9d\u8d56\u81ea\u52a8\u89e3\u6790\u3002<\/p>\n  <\/div>\n<\/div>\n\n<div class=\"card\">\n  <h4>Python \u5e93\u56db\u5927\u6838\u5fc3\u4ef7\u503c<\/h4>\n  <p><span class=\"tag tag-b\">\u4ee3\u7801\u590d\u7528<\/span> \u4e00\u6b21\u7f16\u5199\uff0cpip install \u968f\u5904\u4f7f\u7528\uff0c\u65e0\u9700\u590d\u5236\u7c98\u8d34<\/p>\n  <p><span class=\"tag tag-g\">\u7248\u672c\u7ba1\u7406<\/span> \u8bed\u4e49\u5316\u7248\u672c\u53f7\uff0c\u7528\u6237\u9501\u5b9a\u7279\u5b9a\u7248\u672c\uff0c\u907f\u514d\u7834\u574f\u6027\u53d8\u66f4<\/p>\n  <p><span class=\"tag tag-y\">\u4f9d\u8d56\u58f0\u660e<\/span> \u5728 pyproject.toml \u4e2d\u58f0\u660e\u4f9d\u8d56\uff0cpip \u81ea\u52a8\u89e3\u6790\u5b89\u88c5<\/p>\n  <p><span class=\"tag tag-p\">\u751f\u6001\u5206\u53d1<\/span> \u53d1\u5e03\u5230 PyPI \u540e\u5168\u7403\u5f00\u53d1\u8005\u53ef\u901a\u8fc7 pip \u76f4\u63a5\u5b89\u88c5<\/p>\n<\/div>\n\n<div class=\"info-box\">\n  <strong>\u6838\u5fc3\u5b9a\u4f4d\uff1a\u5c06\u53ef\u590d\u7528\u7684\u529f\u80fd\u5c01\u88c5\u4e3a\u5e93\uff0c\u901a\u8fc7\u6807\u51c6\u5316\u7684\u6253\u5305\u53d1\u5e03\u6d41\u7a0b\uff0c\u4f9b\u6574\u4e2a Python \u751f\u6001\u4f7f\u7528\u3002<\/strong>\n<\/div>\n\n<!-- \u4e8c\u3001\u9879\u76ee\u7ed3\u6784 -->\n<h2 id=\"zh-2\">\u4e8c\u3001\u9879\u76ee\u7ed3\u6784\u8bbe\u8ba1\uff08src layout \u6807\u51c6\uff09<\/h2>\n\n<h3>2.1 \u63a8\u8350\u9879\u76ee\u76ee\u5f55\u7ed3\u6784<\/h3>\n<p>\u73b0\u4ee3 Python \u5e93\u63a8\u8350\u4f7f\u7528 <strong>src layout<\/strong>\uff0c\u5c06\u5e93\u4ee3\u7801\u653e\u5728 <code>src\/<\/code> \u76ee\u5f55\u4e0b\uff0c\u907f\u514d\u6253\u5305\u65f6\u610f\u5916\u5bfc\u5165\u672c\u5730\u5f00\u53d1\u73af\u5883\u4e2d\u7684\u540c\u540d\u5305\u3002<\/p>\n\n<pre class=\"code-block\">mylib\/                          # \u9879\u76ee\u6839\u76ee\u5f55\n\u251c\u2500\u2500 src\/                        # \u6e90\u7801\u76ee\u5f55\uff08src layout\uff09\n\u2502   \u2514\u2500\u2500 mylib\/                  # \u5e93\u5305\u540d\uff0c\u4e0e\u9879\u76ee\u540d\u4e00\u81f4\u6216\u76f8\u5173\n\u2502       \u251c\u2500\u2500 __init__.py         # \u5305\u5165\u53e3\uff0c\u66b4\u9732\u5bf9\u5916\u63a5\u53e3\n\u2502       \u251c\u2500\u2500 core.py             # \u6838\u5fc3\u529f\u80fd\u6a21\u5757\n\u2502       \u251c\u2500\u2500 utils.py            # \u5de5\u5177\u51fd\u6570\n\u2502       \u2514\u2500\u2500 subpkg\/             # \u5b50\u5305\uff08\u53ef\u9009\uff09\n\u2502           \u251c\u2500\u2500 __init__.py\n\u2502           \u2514\u2500\u2500 helpers.py\n\u251c\u2500\u2500 tests\/                      # \u6d4b\u8bd5\u76ee\u5f55\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 test_core.py\n\u2502   \u2514\u2500\u2500 test_utils.py\n\u251c\u2500\u2500 docs\/                       # \u6587\u6863\u76ee\u5f55\uff08\u53ef\u9009\uff09\n\u2502   \u251c\u2500\u2500 guide.md\n\u2502   \u2514\u2500\u2500 api.md\n\u251c\u2500\u2500 pyproject.toml              # \u9879\u76ee\u5143\u6570\u636e\u4e0e\u6784\u5efa\u914d\u7f6e\u3010\u6838\u5fc3\u3011\n\u251c\u2500\u2500 README.md                   # \u9879\u76ee\u8bf4\u660e\n\u251c\u2500\u2500 LICENSE                     # \u5f00\u6e90\u8bb8\u53ef\u8bc1\n\u251c\u2500\u2500 .gitignore                  # Git \u5ffd\u7565\u89c4\u5219\n\u2514\u2500\u2500 CHANGELOG.md                # \u7248\u672c\u53d8\u66f4\u65e5\u5fd7\n<\/pre>\n\n<div class=\"compare-box\">\n  <div class=\"compare-col bad\">\n    <h4>\u274c \u65e7\u7248 flat layout<\/h4>\n<pre class=\"example\">mylib\/\n\u251c\u2500\u2500 mylib\/          # \u4ee3\u7801\u4e0e\u9879\u76ee\u6839\u6df7\u5728\u4e00\u8d77\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u2514\u2500\u2500 core.py\n\u251c\u2500\u2500 setup.py        # \u65e7\u5f0f\u914d\u7f6e\n\u2514\u2500\u2500 setup.cfg<\/pre>\n    <p>\u6253\u5305\u65f6\u53ef\u80fd\u610f\u5916\u5bfc\u5165\u672c\u5730\u5df2\u5b89\u88c5\u7684\u7248\u672c\uff0c\u5bfc\u81f4\u96be\u4ee5\u6392\u67e5\u7684 Bug\u3002<\/p>\n  <\/div>\n  <div class=\"compare-col good\">\n    <h4>\u2705 \u63a8\u8350 src layout<\/h4>\n<pre class=\"example\">mylib\/\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 mylib\/      # \u4ee3\u7801\u5728 src\/ \u4e0b\n\u2502       \u251c\u2500\u2500 __init__.py\n\u2502       \u2514\u2500\u2500 core.py\n\u251c\u2500\u2500 pyproject.toml   # \u73b0\u4ee3\u914d\u7f6e\n\u2514\u2500\u2500 tests\/<\/pre>\n    <p>\u5f3a\u5236\u4ece\u6253\u5305\u540e\u7684\u5b89\u88c5\u7248\u672c\u5bfc\u5165\uff0c\u907f\u514d\u672c\u5730\u73af\u5883\u5e72\u6270\uff0c\u6784\u5efa\u66f4\u53ef\u9760\u3002<\/p>\n  <\/div>\n<\/div>\n\n<h3>2.2 .gitignore \u6807\u51c6\u914d\u7f6e<\/h3>\n<pre class=\"code-block\"># Python\n__pycache__\/\n*.py[cod]\n*.egg-info\/\ndist\/\nbuild\/\n*.egg\n\n# Virtual Env\n.venv\/\nvenv\/\n.env\n\n# IDE\n.vscode\/\n.idea\/\n\n# OS\n.DS_Store\nThumbs.db\n<\/pre>\n\n<!-- \u4e09\u3001pyproject.toml -->\n<h2 id=\"zh-3\">\u4e09\u3001pyproject.toml \u5b8c\u6574\u914d\u7f6e<\/h2>\n\n<div class=\"info-box\">\n  <strong>pyproject.toml \u662f Python \u9879\u76ee\u7684\u73b0\u4ee3\u6807\u51c6\u914d\u7f6e\u6587\u4ef6\uff0c\u66ff\u4ee3\u4e86\u65e7\u7684 setup.py \u548c setup.cfg\u3002PEP 621 \u5b9a\u4e49\u4e86\u5176\u6807\u51c6\u683c\u5f0f\u3002<\/strong>\n<\/div>\n\n<h3>3.1 \u5b8c\u6574 pyproject.toml \u793a\u4f8b<\/h3>\n<pre class=\"code-block\">[build-system]\nrequires = [\"setuptools>=75.0\", \"wheel\"]\nbuild-backend = \"setuptools.backends._legacy:_Backend\"\n\n[project]\nname = \"mylib\"\nversion = \"0.1.0\"\ndescription = \"A short description of your library\"\nreadme = \"README.md\"\nrequires-python = \">=3.10\"\nlicense = {text = \"MIT\"}\nkeywords = [\"example\", \"template\"]\n\nauthors = [\n  {name = \"Your Name\", email = \"your@email.com\"},\n]\n\nclassifiers = [\n  \"Development Status :: 3 - Alpha\",\n  \"Intended Audience :: Developers\",\n  \"License :: OSI Approved :: MIT License\",\n  \"Programming Language :: Python :: 3.10\",\n  \"Programming Language :: Python :: 3.11\",\n  \"Programming Language :: Python :: 3.12\",\n  \"Programming Language :: Python :: 3.13\",\n]\n\ndependencies = [\n  \"requests>=2.31\",\n  \"pydantic>=2.0\",\n]\n\n[project.optional-dependencies]\ndev = [\n  \"pytest>=8.0\",\n  \"pytest-cov>=5.0\",\n  \"ruff>=0.5\",\n  \"mypy>=1.10\",\n  \"pre-commit>=3.0\",\n]\ndocs = [\n  \"mkdocs>=1.6\",\n  \"mkdocs-material>=9.0\",\n]\n\n[project.urls]\nHomepage = \"https:\/\/github.com\/yourname\/mylib\"\nDocumentation = \"https:\/\/mylib.readthedocs.io\"\nRepository = \"https:\/\/github.com\/yourname\/mylib\"\n\"Bug Tracker\" = \"https:\/\/github.com\/yourname\/mylib\/issues\"\n\n[tool.setuptools.packages.find]\nwhere = [\"src\"]          # src layout \u5173\u952e\u914d\u7f6e\ninclude = [\"mylib*\"]     # \u5305\u542b\u7684\u5305\u540d\u6a21\u5f0f\n\n[tool.pytest.ini_options]\ntestpaths = [\"tests\"]\naddopts = \"-v --cov=mylib\"\n\n[tool.ruff]\ntarget-version = \"py310\"\nline-length = 100\n\n[tool.mypy]\npython_version = \"3.10\"\nstrict = true\n<\/pre>\n\n<h3>3.2 \u5b57\u6bb5\u8bf4\u660e\u901f\u67e5<\/h3>\n<div class=\"table-wrap\">\n<table>\n  <thead>\n    <tr><th>\u5b57\u6bb5<\/th><th>\u4f5c\u7528<\/th><th>\u662f\u5426\u5fc5\u586b<\/th><\/tr>\n  <\/thead>\n  <tbody>\n    <tr><td>[build-system]<\/td><td>\u58f0\u660e\u6784\u5efa\u5de5\u5177\uff08setuptools \/ hatchling \/ flit\uff09<\/td><td>\u662f<\/td><\/tr>\n    <tr><td>[project] name<\/td><td>PyPI \u4e0a\u7684\u5305\u540d\uff0c\u4e0d\u80fd\u4e0e\u5df2\u6709\u5305\u51b2\u7a81<\/td><td>\u662f<\/td><\/tr>\n    <tr><td>[project] version<\/td><td>\u5f53\u524d\u7248\u672c\u53f7\uff0c\u9075\u5faa\u8bed\u4e49\u5316\u7248\u672c<\/td><td>\u662f<\/td><\/tr>\n    <tr><td>[project] dependencies<\/td><td>\u8fd0\u884c\u65f6\u4f9d\u8d56\u5217\u8868<\/td><td>\u63a8\u8350<\/td><\/tr>\n    <tr><td>[project] requires-python<\/td><td>\u6700\u4f4e Python \u7248\u672c\u7ea6\u675f<\/td><td>\u63a8\u8350<\/td><\/tr>\n    <tr><td>[project.optional-dependencies]<\/td><td>\u53ef\u9009\u4f9d\u8d56\u7ec4\uff08dev\/docs\/test\uff09<\/td><td>\u53ef\u9009<\/td><\/tr>\n    <tr><td>[tool.setuptools.packages.find]<\/td><td>src layout \u65f6\u5fc5\u987b\u914d\u7f6e where<\/td><td>src layout \u5fc5\u586b<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<\/div>\n\n<!-- \u56db\u3001\u7f16\u5199\u5e93\u4ee3\u7801 -->\n<h2 id=\"zh-4\">\u56db\u3001\u7f16\u5199\u5e93\u4ee3\u7801\u4e0e __init__.py<\/h2>\n\n<h3>4.1 __init__.py \u7684\u804c\u8d23<\/h3>\n<p><code>__init__.py<\/code> \u662f\u5305\u7684\u5165\u53e3\u6587\u4ef6\uff0c\u63a7\u5236\u5bf9\u5916\u66b4\u9732\u7684\u63a5\u53e3\u3002\u597d\u7684\u5e93\u53ea\u66b4\u9732\u7528\u6237\u9700\u8981\u4f7f\u7528\u7684 API\uff0c\u9690\u85cf\u5185\u90e8\u5b9e\u73b0\u7ec6\u8282\u3002<\/p>\n\n<pre class=\"code-block\"># src\/mylib\/__init__.py\n\"\"\"mylib - A short description of your library.\"\"\"\n\nfrom .core import MyClass, my_function\nfrom .utils import helper\n\n__all__ = [\n    \"MyClass\",\n    \"my_function\",\n    \"helper\",\n]\n\n__version__ = \"0.1.0\"\n<\/pre>\n\n<h3>4.2 \u6838\u5fc3\u6a21\u5757\u7f16\u5199\u793a\u4f8b<\/h3>\n<pre class=\"code-block\"># src\/mylib\/core.py\n\"\"\"Core module - implements the main functionality.\"\"\"\n\nfrom typing import Optional\n\n\nclass MyClass:\n    \"\"\"Main class of the library.\"\"\"\n\n    def __init__(self, name: str, value: int = 0):\n        self.name = name\n        self._value = value\n\n    @property\n    def value(self) -> int:\n        return self._value\n\n    @value.setter\n    def value(self, val: int) -> None:\n        if val < 0:\n            raise ValueError(\"Value must be non-negative\")\n        self._value = val\n\n    def greet(self) -> str:\n        return f\"Hello from {self.name}!\"\n\n\ndef my_function(param: str, *, flag: bool = False) -> dict:\n    \"\"\"Main processing function.\n\n    Args:\n        param: Input parameter.\n        flag: Whether to enable extra processing.\n\n    Returns:\n        A dictionary with processed results.\n    \"\"\"\n    result = {\"input\": param, \"processed\": param.upper()}\n    if flag:\n        result[\"extra\"] = \"enabled\"\n    return result\n<\/pre>\n\n<h3>4.3 \u5de5\u5177\u6a21\u5757<\/h3>\n<pre class=\"code-block\"># src\/mylib\/utils.py\n\"\"\"Utility functions.\"\"\"\n\nimport os\nfrom pathlib import Path\nfrom typing import Union\n\n\ndef ensure_dir(path: Union[str, Path]) -> Path:\n    \"\"\"Ensure a directory exists, creating it if needed.\n\n    Args:\n        path: Directory path to ensure.\n\n    Returns:\n        The Path object of the directory.\n    \"\"\"\n    p = Path(path)\n    p.mkdir(parents=True, exist_ok=True)\n    return p\n\n\ndef read_file(path: Union[str, Path]) -> str:\n    \"\"\"Read a text file content.\n\n    Args:\n        path: Path to the file.\n\n    Returns:\n        File content as string.\n    \"\"\"\n    with open(path, \"r\", encoding=\"utf-8\") as f:\n        return f.read()\n<\/pre>\n\n<div class=\"info-box warn\">\n  <strong>\u8bbe\u8ba1\u539f\u5219\uff1a\u6bcf\u4e2a\u6a21\u5757\u53ea\u505a\u4e00\u4ef6\u4e8b\uff0c\u51fd\u6570\u548c\u7c7b\u804c\u8d23\u5355\u4e00\u3002__init__.py \u53ea\u5bfc\u5165\u7528\u6237\u9700\u8981\u7684\u516c\u5f00\u63a5\u53e3\uff0c\u5185\u90e8\u5b9e\u73b0\u6a21\u5757\u4ee5\u4e0b\u5212\u7ebf\u5f00\u5934\u6216\u653e\u7f6e\u5728\u5b50\u5305\u4e2d\u3002<\/strong>\n<\/div>\n\n<!-- \u4e94\u3001\u4f9d\u8d56\u7ba1\u7406\u4e0e\u7248\u672c\u63a7\u5236 -->\n<h2 id=\"zh-5\">\u4e94\u3001\u4f9d\u8d56\u7ba1\u7406\u4e0e\u7248\u672c\u63a7\u5236<\/h2>\n\n<h3>5.1 \u4f9d\u8d56\u58f0\u660e\u539f\u5219<\/h3>\n<div class=\"card\">\n  <h4>\u4f9d\u8d56\u58f0\u660e\u4e09\u6761\u89c4\u5219<\/h4>\n  <p>1. <span class=\"tag tag-b\">\u6700\u5c0f\u7248\u672c\u7ea6\u675f<\/span> \u53ea\u58f0\u660e\u4e0b\u754c\uff08\u5982 <code>requests>=2.31<\/code>\uff09\uff0c\u4e0d\u9501\u6b7b\u4e0a\u9650\uff0c\u907f\u514d\u4e0e\u7528\u6237\u5176\u4ed6\u4f9d\u8d56\u51b2\u7a81<\/p>\n  <p>2. <span class=\"tag tag-g\">\u6309\u529f\u80fd\u5206\u7ec4<\/span> \u8fd0\u884c\u65f6\u4f9d\u8d56\u653e dependencies\uff0c\u5f00\u53d1\u5de5\u5177\u653e [dev]\uff0c\u6587\u6863\u5de5\u5177\u653e [docs]<\/p>\n  <p>3. <span class=\"tag tag-y\">\u5e73\u53f0\u7279\u5b9a\u4f9d\u8d56<\/span> \u4f7f\u7528 environment markers \u5b9e\u73b0\u6761\u4ef6\u4f9d\u8d56<\/p>\n<\/div>\n\n<pre class=\"code-block\"># \u6761\u4ef6\u4f9d\u8d56\u793a\u4f8b\n[project.dependencies]\nrequests>=2.31\n# \u4ec5 Windows \u9700\u8981\ncolorama>=0.4 ; sys_platform == \"win32\"\n\n[project.optional-dependencies]\ndev = [\"pytest>=8\", \"ruff>=0.5\", \"mypy>=1.10\"]\ndocs = [\"mkdocs>=1.6\", \"mkdocs-material>=9\"]\n<\/pre>\n\n<h3>5.2 \u8bed\u4e49\u5316\u7248\u672c\uff08SemVer\uff09<\/h3>\n<div class=\"flow-diagram\">\n  <div class=\"flow-row\">\n    <span class=\"flow-item purple\">MAJOR<\/span>\n    <span class=\"flow-arrow\">.<\/span>\n    <span class=\"flow-item green\">MINOR<\/span>\n    <span class=\"flow-arrow\">.<\/span>\n    <span class=\"flow-item yellow\">PATCH<\/span>\n  <\/div>\n  <div class=\"flow-row\" style=\"font-size:.82rem;color:var(--muted)\">\n    <span>\u4e0d\u517c\u5bb9 API \u53d8\u66f4<\/span>\n    <span>&nbsp;&nbsp;&nbsp;&nbsp;<\/span>\n    <span>\u5411\u4e0b\u517c\u5bb9\u7684\u65b0\u529f\u80fd<\/span>\n    <span>&nbsp;&nbsp;&nbsp;&nbsp;<\/span>\n    <span>\u5411\u4e0b\u517c\u5bb9\u7684 Bug \u4fee\u590d<\/span>\n  <\/div>\n<\/div>\n\n<pre class=\"code-block\">1.0.0  \u2192  \u9996\u6b21\u7a33\u5b9a\u53d1\u5e03\n1.2.0  \u2192  \u65b0\u589e\u529f\u80fd\uff0c\u5411\u540e\u517c\u5bb9\n1.2.1  \u2192  Bug \u4fee\u590d\n2.0.0  \u2192  \u7834\u574f\u6027 API \u53d8\u66f4\n\n# \u9884\u53d1\u5e03\u6807\u7b7e\n1.0.0a1  \u2192  Alpha \u5185\u90e8\u6d4b\u8bd5\n1.0.0b1  \u2192  Beta \u516c\u5f00\u6d4b\u8bd5\n1.0.0rc1 \u2192  Release Candidate \u5019\u9009\u7248\n<\/pre>\n\n<h3>5.3 CHANGELOG.md \u6a21\u677f<\/h3>\n<pre class=\"code-block\"># Changelog\n\n## [0.2.0] - 2026-07-15\n\n### Added\n- \u65b0\u589e `MyClass.greet()` \u65b9\u6cd5\n- \u652f\u6301 Python 3.13\n\n### Changed\n- \u91cd\u6784 utils \u6a21\u5757\uff0c\u5e9f\u5f03 `old_helper()`\n- \u63d0\u5347\u6838\u5fc3\u7b97\u6cd5\u6027\u80fd\u7ea6 30%\n\n### Fixed\n- \u4fee\u590d Windows \u4e0b\u8def\u5f84\u5206\u9694\u7b26\u95ee\u9898\n\n## [0.1.0] - 2026-06-01\n\n### Added\n- \u521d\u59cb\u7248\u672c\u53d1\u5e03\n- \u6838\u5fc3\u7c7b `MyClass` \u548c\u5de5\u5177\u51fd\u6570\n<\/pre>\n\n<!-- \u516d\u3001\u6784\u5efa\u4e0e\u6253\u5305 -->\n<h2 id=\"zh-6\">\u516d\u3001\u6784\u5efa\u4e0e\u6253\u5305\uff08uv \/ setuptools\uff09<\/h2>\n\n<h3>6.1 \u6784\u5efa\u5de5\u5177\u5bf9\u6bd4<\/h3>\n<div class=\"table-wrap\">\n<table>\n  <thead>\n    <tr><th>\u5de5\u5177<\/th><th>\u7279\u70b9<\/th><th>\u9002\u7528\u573a\u666f<\/th><\/tr>\n  <\/thead>\n  <tbody>\n    <tr><td>setuptools<\/td><td>Python \u751f\u6001\u6700\u6210\u719f\uff0c\u517c\u5bb9\u6027\u6700\u597d<\/td><td>\u901a\u7528\u9879\u76ee\uff0c\u63a8\u8350\u65b0\u624b<\/td><\/tr>\n    <tr><td>hatchling<\/td><td>\u73b0\u4ee3\u7b80\u6d01\uff0cHatch \u751f\u6001<\/td><td>\u8ffd\u6c42\u7b80\u6d01\u914d\u7f6e\u7684\u65b0\u9879\u76ee<\/td><\/tr>\n    <tr><td>flit_core<\/td><td>\u6781\u7b80\uff0c\u7eaf Python \u5305<\/td><td>\u7eaf Python \u65e0 C \u6269\u5c55\u7684\u9879\u76ee<\/td><\/tr>\n    <tr><td>pdm-backend<\/td><td>PDM \u751f\u6001\uff0cPEP 621 \u539f\u751f<\/td><td>\u4f7f\u7528 PDM \u7ba1\u7406\u7684\u9879\u76ee<\/td><\/tr>\n    <tr><td>uv<\/td><td>\u6781\u901f\uff0cRust \u7f16\u5199\uff0c\u73b0\u4ee3\u5de5\u5177\u94fe<\/td><td>\u8ffd\u6c42\u901f\u5ea6\u7684\u65b0\u9879\u76ee\uff08\u63a8\u8350\uff09<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<\/div>\n\n<h3>6.2 \u4f7f\u7528 uv \u6784\u5efa<\/h3>\n<pre class=\"terminal\"><span class=\"comment\"># \u5b89\u88c5 uv\uff08\u63a8\u8350\u73b0\u4ee3\u65b9\u5f0f\uff09<\/span>\n<span class=\"prompt\">pip install uv<\/span>\n\n<span class=\"comment\"># \u6784\u5efa wheel \u548c sdist<\/span>\n<span class=\"prompt\">uv build<\/span>\n<span class=\"output\">Successfully built dist\/mylib-0.1.0-py3-none-any.whl\nSuccessfully built dist\/mylib-0.1.0.tar.gz<\/span>\n\n<span class=\"comment\"># \u67e5\u770b\u6784\u5efa\u4ea7\u7269<\/span>\n<span class=\"prompt\">ls dist\/<\/span>\n<span class=\"output\">mylib-0.1.0-py3-none-any.whl\nmylib-0.1.0.tar.gz<\/span>\n<\/pre>\n\n<h3>6.3 \u4f7f\u7528 setuptools \u6784\u5efa<\/h3>\n<pre class=\"terminal\"><span class=\"comment\"># \u5b89\u88c5\u6784\u5efa\u5de5\u5177<\/span>\n<span class=\"prompt\">pip install build<\/span>\n\n<span class=\"comment\"># \u6784\u5efa<\/span>\n<span class=\"prompt\">python -m build<\/span>\n<span class=\"output\">* Creating venv isolated environment...\n* Installing packages in isolated environment...\n* Building wheel...\nSuccessfully built mylib-0.1.0-py3-none-any.whl\n* Building sdist...\nSuccessfully built mylib-0.1.0.tar.gz<\/span>\n<\/pre>\n\n<h3>6.4 \u6784\u5efa\u4ea7\u7269\u8bf4\u660e<\/h3>\n<div class=\"two-col\">\n  <div class=\"col\">\n    <h4>Wheel (.whl)<\/h4>\n    <p>\u9884\u6784\u5efa\u7684\u4e8c\u8fdb\u5236\u5206\u53d1\u683c\u5f0f\uff0c\u5b89\u88c5\u65f6\u65e0\u9700\u6267\u884c\u6784\u5efa\u6b65\u9aa4\uff0c\u5373\u88c5\u5373\u7528\u3002<\/p>\n    <pre class=\"example\">mylib-0.1.0-py3-none-any.whl\n\u251c\u2500\u2500 mylib\/\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 core.py\n\u2502   \u2514\u2500\u2500 utils.py\n\u2514\u2500\u2500 mylib-0.1.0.dist-info\/\n    \u251c\u2500\u2500 METADATA\n    \u251c\u2500\u2500 WHEEL\n    \u2514\u2500\u2500 RECORD<\/pre>\n  <\/div>\n  <div class=\"col\">\n    <h4>Source Distribution (.tar.gz)<\/h4>\n    <p>\u6e90\u7801\u5206\u53d1\u683c\u5f0f\uff0c\u5305\u542b\u6e90\u7801\u548c pyproject.toml\uff0c\u5b89\u88c5\u65f6\u5728\u76ee\u6807\u673a\u5668\u4e0a\u6784\u5efa\u3002<\/p>\n    <pre class=\"example\">mylib-0.1.0.tar.gz\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 mylib\/\n\u251c\u2500\u2500 pyproject.toml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 LICENSE\n\u2514\u2500\u2500 PKG-INFO<\/pre>\n  <\/div>\n<\/div>\n\n<!-- \u4e03\u3001\u53d1\u5e03\u5230 PyPI -->\n<h2 id=\"zh-7\">\u4e03\u3001\u53d1\u5e03\u5230 PyPI \u5168\u6d41\u7a0b<\/h2>\n\n<h3>7.1 \u53d1\u5e03\u6d41\u7a0b\u6982\u89c8<\/h3>\n<div class=\"flow-diagram\">\n  <div class=\"flow-row\">\n    <span class=\"flow-item green\">\u6ce8\u518c PyPI<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item\">\u521b\u5efa Token<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item yellow\">\u6784\u5efa\u5305<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item purple\">\u4e0a\u4f20\u53d1\u5e03<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item green\">pip \u5b89\u88c5<\/span>\n  <\/div>\n<\/div>\n\n<ol class=\"steps\">\n  <li>\u6ce8\u518c PyPI \u8d26\u53f7\uff1a<a href=\"https:\/\/pypi.org\/account\/register\/\" target=\"_blank\" rel=\"noopener\">pypi.org\/account\/register<\/a>\n    <span class=\"note\">\u63a8\u8350\u542f\u7528\u53cc\u56e0\u7d20\u8ba4\u8bc1\uff082FA\uff09<\/span>\n  <\/li>\n  <li>\u521b\u5efa API Token\uff1a<strong>Account Settings \u2192 API tokens \u2192 Add API token<\/strong>\n    <span class=\"note\">Token \u683c\u5f0f\uff1a<code>pypi-xxxxxxxx<\/code>\uff0c\u4fdd\u5b58\u5230\u672c\u5730\u5b89\u5168\u4f4d\u7f6e<\/span>\n  <\/li>\n  <li>\u6784\u5efa wheel \u548c sdist\uff1a<code>uv build<\/code> \u6216 <code>python -m build<\/code>\n    <span class=\"note\">\u4ea7\u7269\u751f\u6210\u5728 dist\/ \u76ee\u5f55\u4e0b<\/span>\n  <\/li>\n  <li>\u4e0a\u4f20\u5230 PyPI\uff1a\n    <span class=\"cmd\">uv publish  # \u65b9\u5f0f\u4e00\uff1auv \u4e00\u952e\u53d1\u5e03<\/span>\n    <span class=\"cmd\">twine upload dist\/*  # \u65b9\u5f0f\u4e8c\uff1a\u4f20\u7edf twine \u4e0a\u4f20<\/span>\n    <span class=\"note\">\u9996\u6b21\u53d1\u5e03\u9700\u8f93\u5165\u7528\u6237\u540d <code>__token__<\/code> \u548c\u5bc6\u7801\uff08Token \u503c\uff09<\/span>\n  <\/li>\n  <li>\u9a8c\u8bc1\u53d1\u5e03\uff1a<code>pip install mylib<\/code>\n    <span class=\"note\">PyPI \u7d22\u5f15\u66f4\u65b0\u540e\u5373\u53ef\u5b89\u88c5\uff0c\u901a\u5e38 1-2 \u5206\u949f<\/span>\n  <\/li>\n<\/ol>\n\n<h3>7.2 \u5148\u53d1\u5e03\u5230 TestPyPI \u6d4b\u8bd5<\/h3>\n<div class=\"info-box warn\">\n  <strong>\u5f3a\u70c8\u5efa\u8bae\uff1a\u9996\u6b21\u53d1\u5e03\u524d\u5148\u4e0a\u4f20\u5230 TestPyPI \u9a8c\u8bc1\uff0c\u907f\u514d\u6b63\u5f0f\u4ed3\u5e93\u51fa\u73b0\u9519\u8bef\u3002<\/strong>\n<\/div>\n\n<pre class=\"terminal\"><span class=\"comment\"># \u914d\u7f6e TestPyPI \u4ed3\u5e93<\/span>\n<span class=\"prompt\">uv publish<\/span> --publish-url https:\/\/test.pypi.org\/legacy\/\n\n<span class=\"comment\"># \u4ece TestPyPI \u5b89\u88c5\u6d4b\u8bd5<\/span>\n<span class=\"prompt\">pip install<\/span> --index-url https:\/\/test.pypi.org\/simple\/ mylib\n<\/pre>\n\n<h3>7.3 \u53d1\u5e03\u540e\u9a8c\u8bc1<\/h3>\n<pre class=\"terminal\"><span class=\"comment\"># \u4ece PyPI \u5b89\u88c5<\/span>\n<span class=\"prompt\">pip install mylib<\/span>\n\n<span class=\"comment\"># \u68c0\u67e5\u7248\u672c<\/span>\n<span class=\"prompt\">pip show mylib<\/span>\n<span class=\"output\">Name: mylib\nVersion: 0.1.0\nSummary: A short description of your library\nHome-page: https:\/\/github.com\/yourname\/mylib<\/span>\n\n<span class=\"comment\"># \u67e5\u770b\u4f9d\u8d56<\/span>\n<span class=\"prompt\">pip show mylib | Select-String \"Requires\"<\/span>\n<span class=\"output\">Requires: requests, pydantic<\/span>\n<\/pre>\n\n<!-- \u516b\u3001\u6d4b\u8bd5 -->\n<h2 id=\"zh-8\">\u516b\u3001\u6d4b\u8bd5\u4e0e CI \u96c6\u6210<\/h2>\n\n<h3>8.1 pytest \u6d4b\u8bd5\u793a\u4f8b<\/h3>\n<pre class=\"code-block\"># tests\/test_core.py\nimport pytest\nfrom mylib import MyClass, my_function\n\n\nclass TestMyClass:\n    def test_init(self):\n        obj = MyClass(\"test\", 42)\n        assert obj.name == \"test\"\n        assert obj.value == 42\n\n    def test_greet(self):\n        obj = MyClass(\"world\")\n        assert obj.greet() == \"Hello from world!\"\n\n    def test_negative_value(self):\n        obj = MyClass(\"test\")\n        with pytest.raises(ValueError):\n            obj.value = -1\n\n\nclass TestMyFunction:\n    def test_basic(self):\n        result = my_function(\"hello\")\n        assert result[\"input\"] == \"hello\"\n        assert result[\"processed\"] == \"HELLO\"\n\n    def test_with_flag(self):\n        result = my_function(\"hello\", flag=True)\n        assert result[\"extra\"] == \"enabled\"\n\n    @pytest.mark.parametrize(\"input_val,expected\", [\n        (\"abc\", \"ABC\"),\n        (\"123\", \"123\"),\n        (\"\", \"\"),\n    ])\n    def test_parametrized(self, input_val, expected):\n        result = my_function(input_val)\n        assert result[\"processed\"] == expected\n<\/pre>\n\n<h3>8.2 \u8fd0\u884c\u6d4b\u8bd5<\/h3>\n<pre class=\"terminal\"><span class=\"comment\"># \u5b89\u88c5\u6d4b\u8bd5\u4f9d\u8d56<\/span>\n<span class=\"prompt\">pip install -e \".[dev]\"<\/span>\n\n<span class=\"comment\"># \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5<\/span>\n<span class=\"prompt\">pytest<\/span>\n<span class=\"output\">============================= test session starts =============================\ncollected 6 items\n\ntests\/test_core.py ......                                              [100%]\n\n============================== 6 passed in 0.32s ==============================<\/span>\n\n<span class=\"comment\"># \u5e26\u8986\u76d6\u7387\u62a5\u544a<\/span>\n<span class=\"prompt\">pytest --cov=mylib --cov-report=term-missing<\/span>\n<\/pre>\n\n<h3>8.3 GitHub Actions CI \u914d\u7f6e<\/h3>\n<pre class=\"code-block\"># .github\/workflows\/ci.yml\nname: CI\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n\n    steps:\n      - uses: actions\/checkout@v4\n      - uses: actions\/setup-python@v5\n        with:\n          python-version: ${{ matrix.python-version }}\n      - run: pip install uv\n      - run: uv sync --group dev\n      - run: uv run pytest --cov=mylib\n      - run: uv run ruff check\n      - run: uv run mypy src\n<\/pre>\n\n<!-- \u4e5d\u3001\u6587\u6863 -->\n<h2 id=\"zh-9\">\u4e5d\u3001\u6587\u6863\u7f16\u5199\u4e0e\u81ea\u52a8\u5316<\/h2>\n\n<h3>9.1 README.md \u89c4\u8303<\/h3>\n<pre class=\"code-block\"># mylib\n\n[![PyPI](https:\/\/img.shields.io\/pypi\/v\/mylib)](https:\/\/pypi.org\/project\/mylib\/)\n[![Python](https:\/\/img.shields.io\/pypi\/pyversions\/mylib)](https:\/\/pypi.org\/project\/mylib\/)\n[![CI](https:\/\/github.com\/yourname\/mylib\/workflows\/CI\/badge.svg)](https:\/\/github.com\/yourname\/mylib\/actions)\n\nA short description of your library.\n\n## Installation\n\n```bash\npip install mylib\n```\n\n## Quick Start\n\n```python\nfrom mylib import MyClass\n\nobj = MyClass(\"world\")\nprint(obj.greet())\n# Output: Hello from world!\n```\n\n## Documentation\n\nFull documentation: https:\/\/mylib.readthedocs.io\n\n## License\n\nThis project is licensed under the MIT License.\n<\/pre>\n\n<h3>9.2 MkDocs \u6587\u6863\u7ad9\u70b9<\/h3>\n<pre class=\"code-block\"># mkdocs.yml\nsite_name: mylib\nsite_description: A short description of your library\ntheme:\n  name: material\n  features:\n    - navigation.tabs\n    - navigation.sections\n\nnav:\n  - Home: index.md\n  - Guide:\n    - Getting Started: guide\/getting-started.md\n    - Usage: guide\/usage.md\n  - API Reference: api\/\n  - Changelog: changelog.md\n\nplugins:\n  - mkdocstrings:\n      handlers:\n        python:\n          paths: [src]\n<\/pre>\n\n<pre class=\"terminal\"><span class=\"comment\"># \u5b89\u88c5\u6587\u6863\u4f9d\u8d56<\/span>\n<span class=\"prompt\">pip install -e \".[docs]\"<\/span>\n\n<span class=\"comment\"># \u672c\u5730\u9884\u89c8\u6587\u6863<\/span>\n<span class=\"prompt\">mkdocs serve<\/span>\n<span class=\"output\">INFO    -  Building documentation...\nINFO    -  Serving on http:\/\/127.0.0.1:8000<\/span>\n\n<span class=\"comment\"># \u6784\u5efa\u9759\u6001\u6587\u6863<\/span>\n<span class=\"prompt\">mkdocs build<\/span>\n<span class=\"output\">INFO    -  Documentation built in site\/<\/span>\n<\/pre>\n\n<!-- \u5341\u3001\u6700\u4f73\u5b9e\u8df5 -->\n<h2 id=\"zh-10\">\u5341\u3001\u6700\u4f73\u5b9e\u8df5\u4e0e\u5e38\u89c1\u95ee\u9898<\/h2>\n\n<h3>10.1 \u5f00\u53d1\u5b8c\u6574\u5de5\u4f5c\u6d41<\/h3>\n<div class=\"flow-diagram\">\n  <div class=\"flow-row\">\n    <span class=\"flow-item green\">\u8bbe\u8ba1\u63a5\u53e3<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item\">\u7f16\u5199\u4ee3\u7801<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item yellow\">\u7f16\u5199\u6d4b\u8bd5<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item\">\u672c\u5730\u6784\u5efa<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item purple\">TestPyPI<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item green\">PyPI \u53d1\u5e03<\/span>\n  <\/div>\n<\/div>\n\n<h3>10.2 \u6700\u4f73\u5b9e\u8df5\u6e05\u5355<\/h3>\n<div class=\"card\">\n  <h4>\u5f00\u53d1\u89c4\u8303<\/h4>\n  <p>\u2705 \u4f7f\u7528 <strong>src layout<\/strong> \u7ec4\u7ec7\u9879\u76ee\u7ed3\u6784\uff0c\u907f\u514d\u5bfc\u5165\u6b67\u4e49<\/p>\n  <p>\u2705 \u5728 <code>__init__.py<\/code> \u4e2d\u663e\u5f0f\u5b9a\u4e49 <code>__all__<\/code>\uff0c\u63a7\u5236\u66b4\u9732\u63a5\u53e3<\/p>\n  <p>\u2705 \u4f7f\u7528\u6709\u610f\u4e49\u7684\u7c7b\u578b\u6ce8\u89e3\uff08Type Hints\uff09\uff0c\u63d0\u5347\u4ee3\u7801\u53ef\u8bfb\u6027<\/p>\n  <p>\u2705 \u6240\u6709\u516c\u6709\u51fd\u6570\u7f16\u5199 docstring\uff0c\u9075\u5faa Google\/NumPy \u98ce\u683c<\/p>\n  <p>\u2705 \u4f7f\u7528 <strong>ruff<\/strong> \u89c4\u8303\u4ee3\u7801\u98ce\u683c\uff0c<strong>mypy<\/strong> \u505a\u9759\u6001\u7c7b\u578b\u68c0\u67e5<\/p>\n  <p>\u2705 \u914d\u7f6e <strong>pre-commit<\/strong> \u81ea\u52a8\u5728\u63d0\u4ea4\u524d\u68c0\u67e5\u4ee3\u7801<\/p>\n  <p>\u2705 \u9075\u5faa\u8bed\u4e49\u5316\u7248\u672c\uff0c\u53d1\u5e03\u524d\u66f4\u65b0 CHANGELOG<\/p>\n  <p>\u2705 \u4f7f\u7528 <strong>GitHub Actions<\/strong> \u505a CI \u81ea\u52a8\u6d4b\u8bd5\u548c\u591a\u7248\u672c\u9a8c\u8bc1<\/p>\n<\/div>\n\n<h3>10.3 \u5e38\u89c1\u95ee\u9898\u4e0e\u89e3\u51b3\u65b9\u6848<\/h3>\n<div class=\"table-wrap\">\n<table>\n  <thead>\n    <tr><th>\u95ee\u9898\u73b0\u8c61<\/th><th>\u539f\u56e0<\/th><th>\u89e3\u51b3\u65b9\u6848<\/th><\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <td>pip install \u540e\u627e\u4e0d\u5230\u6a21\u5757<\/td>\n      <td>src layout \u672a\u914d\u7f6e packages.find.where<\/td>\n      <td>pyproject.toml \u4e2d\u6dfb\u52a0 [tool.setuptools.packages.find] where = [&#8220;src&#8221;]<\/td>\n    <\/tr>\n    <tr>\n      <td>\u6784\u5efa\u51fa\u7684\u5305\u4e0d\u5305\u542b\u5b50\u5305<\/td>\n      <td>packages.find.include \u6a21\u5f0f\u4e0d\u5339\u914d<\/td>\n      <td>\u8bbe\u7f6e include = [&#8220;mylib*&#8221;] \u6216\u5df2\u5f03\u7528\u7684 namespace_packages<\/td>\n    <\/tr>\n    <tr>\n      <td>PyPI \u53d1\u5e03\u5931\u8d25\uff1a\u540d\u79f0\u5df2\u5b58\u5728<\/td>\n      <td>\u5305\u540d\u88ab\u5360\u7528<\/td>\n      <td>\u4fee\u6539 project.name \u4e3a\u672a\u4f7f\u7528\u7684\u540d\u79f0\uff0c\u5148\u641c\u7d22 PyPI \u786e\u8ba4<\/td>\n    <\/tr>\n    <tr>\n      <td>twine \u4e0a\u4f20\u8ba4\u8bc1\u5931\u8d25<\/td>\n      <td>Token \u8fc7\u671f\u6216\u6743\u9650\u4e0d\u8db3<\/td>\n      <td>\u91cd\u65b0\u751f\u6210 API Token\uff0c\u786e\u8ba4\u4f7f\u7528 __token__ \u4f5c\u4e3a\u7528\u6237\u540d<\/td>\n    <\/tr>\n    <tr>\n      <td>\u672c\u5730\u6d4b\u8bd5\u901a\u8fc7\u4f46 CI \u5931\u8d25<\/td>\n      <td>\u672c\u5730\u73af\u5883\u4e0e CI \u4e0d\u4e00\u81f4<\/td>\n      <td>\u4f7f\u7528 requirements.txt \u6216 uv.lock \u9501\u5b9a\u7248\u672c\uff0c\u786e\u4fdd CI \u4e0e\u672c\u5730\u4e00\u81f4<\/td>\n    <\/tr>\n    <tr>\n      <td>\u7248\u672c\u53f7\u51b2\u7a81<\/td>\n      <td>\u624b\u52a8\u4fee\u6539\u7248\u672c\u53f7\u540e\u5fd8\u8bb0\u66f4\u65b0<\/td>\n      <td>\u4f7f\u7528 bumpversion \u6216 hatch version \u7b49\u5de5\u5177\u81ea\u52a8\u7ba1\u7406\u7248\u672c\u53f7<\/td>\n    <\/tr>\n    <tr>\n      <td>\u4f9d\u8d56\u5730\u72f1<\/td>\n      <td>\u4f9d\u8d56\u7248\u672c\u7ea6\u675f\u8fc7\u4e8e\u4e25\u683c<\/td>\n      <td>\u53ea\u8bbe\u4e0b\u754c\u4e0d\u8bbe\u4e0a\u754c\uff0c\u4f7f\u7528\u8bed\u4e49\u5316\u7248\u672c\u8303\u56f4\u517c\u5bb9<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n<\/div>\n\n<h3>10.4 \u5b8c\u6574\u9879\u76ee\u6a21\u677f<\/h3>\n<pre class=\"code-block\">mylib\/\n\u251c\u2500\u2500 .github\/workflows\/ci.yml\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 mylib\/\n\u2502       \u251c\u2500\u2500 __init__.py\n\u2502       \u251c\u2500\u2500 core.py\n\u2502       \u2514\u2500\u2500 utils.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 test_core.py\n\u2502   \u2514\u2500\u2500 test_utils.py\n\u251c\u2500\u2500 docs\/\n\u2502   \u251c\u2500\u2500 index.md\n\u2502   \u2514\u2500\u2500 guide\/\n\u251c\u2500\u2500 pyproject.toml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 LICENSE\n\u251c\u2500\u2500 CHANGELOG.md\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 .pre-commit-config.yaml\n\u2514\u2500\u2500 mkdocs.yml\n<\/pre>\n\n<div class=\"info-box\">\n  <strong>\u5168\u6d41\u7a0b\u603b\u7ed3\uff1a\u8bbe\u8ba1\u63a5\u53e3 \u2192 src layout \u7ec4\u7ec7\u4ee3\u7801 \u2192 pyproject.toml \u914d\u7f6e\u5143\u6570\u636e \u2192 \u7f16\u5199\u6838\u5fc3\u529f\u80fd\u4e0e\u7c7b\u578b\u6ce8\u89e3 \u2192 pytest \u6d4b\u8bd5\u8986\u76d6 \u2192 \u672c\u5730\u6784\u5efa\u9a8c\u8bc1 \u2192 TestPyPI \u9884\u53d1\u5e03 \u2192 \u6b63\u5f0f\u53d1\u5e03 PyPI \u2192 CI \u81ea\u52a8\u6d4b\u8bd5 \u2192 MkDocs \u6587\u6863\u7ad9\u70b9\u3002<\/strong>\n<\/div>\n\n<\/div>\n\n<!-- ======== \u82f1\u6587\u7248 ======== -->\n<div class=\"lang-section\" id=\"lang-en\">\n<div class=\"hero\">\n  <h1>Python Library Development Complete Guide<span class=\"sub\">Project Structure \u2192 Build \u2192 PyPI Release \u2014 Full Workflow<\/span><\/h1>\n  <p>What is a Python library, project structure, pyproject.toml, src layout, coding, packaging, publishing to PyPI, versioning, testing &#038; documentation<\/p>\n<\/div>\n\n<div class=\"toc\">\n  <h3>Table of Contents<\/h3>\n  <ol>\n    <li><a href=\"#en-1\">1 What is a Python Library<\/a><\/li>\n    <li><a href=\"#en-2\">2 Project Structure &#038; src Layout<\/a><\/li>\n    <li><a href=\"#en-3\">3 pyproject.toml Configuration<\/a><\/li>\n    <li><a href=\"#en-4\">4 Writing Library Code &#038; __init__.py<\/a><\/li>\n    <li><a href=\"#en-5\">5 Dependency &#038; Version Management<\/a><\/li>\n    <li><a href=\"#en-6\">6 Building &#038; Packaging<\/a><\/li>\n    <li><a href=\"#en-7\">7 Publishing to PyPI<\/a><\/li>\n    <li><a href=\"#en-8\">8 Testing &#038; CI Integration<\/a><\/li>\n    <li><a href=\"#en-9\">9 Documentation &#038; Automation<\/a><\/li>\n    <li><a href=\"#en-10\">10 Best Practices &#038; FAQ<\/a><\/li>\n  <\/ol>\n<\/div>\n\n<h2 id=\"en-1\">1 What is a Python Library<\/h2>\n<p>A Python library (package) is a reusable collection of code, packaged with a standardized interface so other projects can install it via <code>pip install<\/code>.<\/p>\n\n<div class=\"card\">\n  <h4>Four Core Values<\/h4>\n  <p><span class=\"tag tag-b\">Reusability<\/span> Write once, pip install anywhere \u2014 no copy-paste<\/p>\n  <p><span class=\"tag tag-g\">Versioning<\/span> Semantic versioning, users pin exact versions, no breaking surprises<\/p>\n  <p><span class=\"tag tag-y\">Declared Dependencies<\/span> List dependencies in pyproject.toml, pip auto-resolves<\/p>\n  <p><span class=\"tag tag-p\">Ecosystem Distribution<\/span> Publish to PyPI, reachable by every Python developer globally<\/p>\n<\/div>\n\n<h2 id=\"en-2\">2 Project Structure &#038; src Layout<\/h2>\n<p>Modern Python libraries use <strong>src layout<\/strong> \u2014 placing source code under <code>src\/<\/code> to avoid accidental imports from the development environment.<\/p>\n\n<pre class=\"code-block\">mylib\/\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 mylib\/\n\u2502       \u251c\u2500\u2500 __init__.py\n\u2502       \u251c\u2500\u2500 core.py\n\u2502       \u251c\u2500\u2500 utils.py\n\u2502       \u2514\u2500\u2500 subpkg\/\n\u2502           \u251c\u2500\u2500 __init__.py\n\u2502           \u2514\u2500\u2500 helpers.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 test_core.py\n\u2502   \u2514\u2500\u2500 test_utils.py\n\u251c\u2500\u2500 docs\/\n\u251c\u2500\u2500 pyproject.toml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 LICENSE\n\u251c\u2500\u2500 .gitignore\n\u2514\u2500\u2500 CHANGELOG.md\n<\/pre>\n\n<h2 id=\"en-3\">3 pyproject.toml Configuration<\/h2>\n<p>pyproject.toml is the modern standard configuration file (PEP 621), replacing the legacy setup.py.<\/p>\n\n<pre class=\"code-block\">[build-system]\nrequires = [\"setuptools>=75.0\", \"wheel\"]\nbuild-backend = \"setuptools.backends._legacy:_Backend\"\n\n[project]\nname = \"mylib\"\nversion = \"0.1.0\"\ndescription = \"A short description of your library\"\nreadme = \"README.md\"\nrequires-python = \">=3.10\"\nlicense = {text = \"MIT\"}\ndependencies = [\"requests>=2.31\"]\n\n[tool.setuptools.packages.find]\nwhere = [\"src\"]           # Critical for src layout\ninclude = [\"mylib*\"]\n<\/pre>\n\n<h2 id=\"en-4\">4 Writing Library Code &#038; __init__.py<\/h2>\n<p>The <code>__init__.py<\/code> controls the public API surface. Expose only what users need.<\/p>\n\n<pre class=\"code-block\"># src\/mylib\/__init__.py\nfrom .core import MyClass, my_function\n\n__all__ = [\"MyClass\", \"my_function\"]\n__version__ = \"0.1.0\"\n\n# src\/mylib\/core.py\nclass MyClass:\n    def __init__(self, name: str, value: int = 0):\n        self.name = name\n        self._value = value\n\n    def greet(self) -> str:\n        return f\"Hello from {self.name}!\"\n<\/pre>\n\n<h2 id=\"en-5\">5 Dependency &#038; Version Management<\/h2>\n\n<h3>Semantic Versioning<\/h3>\n<div class=\"flow-diagram\">\n  <div class=\"flow-row\">\n    <span class=\"flow-item purple\">MAJOR<\/span>\n    <span class=\"flow-arrow\">.<\/span>\n    <span class=\"flow-item green\">MINOR<\/span>\n    <span class=\"flow-arrow\">.<\/span>\n    <span class=\"flow-item yellow\">PATCH<\/span>\n  <\/div>\n  <div class=\"flow-row\" style=\"font-size:.82rem;color:var(--muted)\">\n    <span>Breaking API change<\/span>\n    <span>&nbsp;&nbsp;&nbsp;&nbsp;<\/span>\n    <span>Backward-compatible feature<\/span>\n    <span>&nbsp;&nbsp;&nbsp;&nbsp;<\/span>\n    <span>Backward-compatible bug fix<\/span>\n  <\/div>\n<\/div>\n\n<h3>Dependency Rules<\/h3>\n<ul>\n  <li>Specify only lower bounds: <code>requests>=2.31<\/code><\/li>\n  <li>Group optional deps: dev, docs, test<\/li>\n  <li>Use environment markers for platform-specific deps<\/li>\n<\/ul>\n\n<h2 id=\"en-6\">6 Building &#038; Packaging<\/h2>\n\n<pre class=\"terminal\"><span class=\"comment\"># Build with uv (recommended)<\/span>\n<span class=\"prompt\">pip install uv<\/span>\n<span class=\"prompt\">uv build<\/span>\n<span class=\"output\">Successfully built dist\/mylib-0.1.0-py3-none-any.whl\nSuccessfully built dist\/mylib-0.1.0.tar.gz<\/span>\n\n<span class=\"comment\"># Or build with setuptools<\/span>\n<span class=\"prompt\">pip install build<\/span>\n<span class=\"prompt\">python -m build<\/span>\n<\/pre>\n\n<h2 id=\"en-7\">7 Publishing to PyPI<\/h2>\n\n<div class=\"flow-diagram\">\n  <div class=\"flow-row\">\n    <span class=\"flow-item green\">Register PyPI<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item\">Create Token<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item yellow\">Build Package<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item purple\">Upload &#038; Publish<\/span>\n    <span class=\"flow-arrow\">&rarr;<\/span>\n    <span class=\"flow-item green\">pip install<\/span>\n  <\/div>\n<\/div>\n\n<ol class=\"steps\">\n  <li>Register at <a href=\"https:\/\/pypi.org\/account\/register\/\" target=\"_blank\" rel=\"noopener\">pypi.org<\/a> (enable 2FA)<\/li>\n  <li>Create API Token: <strong>Account Settings \u2192 API tokens \u2192 Add API token<\/strong><\/li>\n  <li>Build: <code>uv build<\/code> or <code>python -m build<\/code><\/li>\n  <li>Upload: <code>uv publish<\/code> or <code>twine upload dist\/*<\/code><\/li>\n  <li>Verify: <code>pip install mylib<\/code><\/li>\n<\/ol>\n\n<div class=\"info-box warn\">\n  <strong>Always test on TestPyPI first: <code>uv publish --publish-url https:\/\/test.pypi.org\/legacy\/<\/code><\/strong>\n<\/div>\n\n<h2 id=\"en-8\">8 Testing &#038; CI Integration<\/h2>\n\n<pre class=\"code-block\"># tests\/test_core.py\nimport pytest\nfrom mylib import MyClass\n\ndef test_greet():\n    obj = MyClass(\"world\")\n    assert obj.greet() == \"Hello from world!\"\n<\/pre>\n\n<pre class=\"terminal\"><span class=\"prompt\">pip install -e \".[dev]\"<\/span>\n<span class=\"prompt\">pytest --cov=mylib<\/span>\n<span class=\"output\">============================== 6 passed in 0.32s ==============================<\/span>\n<\/pre>\n\n<h4>GitHub Actions CI<\/h4>\n<pre class=\"code-block\"># .github\/workflows\/ci.yml\nname: CI\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n    steps:\n      - uses: actions\/checkout@v4\n      - uses: actions\/setup-python@v5\n        with: {python-version: \"${{ matrix.python-version }}\"}\n      - run: pip install uv && uv sync --group dev\n      - run: uv run pytest --cov=mylib\n      - run: uv run ruff check && uv run mypy src\n<\/pre>\n\n<h2 id=\"en-9\">9 Documentation<\/h2>\n\n<h4>README.md with badges<\/h4>\n<pre class=\"code-block\"># mylib\n\n[![PyPI](https:\/\/img.shields.io\/pypi\/v\/mylib)](https:\/\/pypi.org\/project\/mylib\/)\n[![Python](https:\/\/img.shields.io\/pypi\/pyversions\/mylib)](https:\/\/pypi.org\/project\/mylib\/)\n\nA short description of your library.\n\n## Installation\n```bash\npip install mylib\n```\n<\/pre>\n\n<h4>MkDocs site<\/h4>\n<pre class=\"terminal\"><span class=\"prompt\">pip install -e \".[docs]\"<\/span>\n<span class=\"prompt\">mkdocs serve<\/span>\n<span class=\"output\">Serving on http:\/\/127.0.0.1:8000<\/span>\n<\/pre>\n\n<h2 id=\"en-10\">10 Best Practices &#038; FAQ<\/h2>\n\n<div class=\"card\">\n  <h4>Best Practices Checklist<\/h4>\n  <p>\u2705 Use <strong>src layout<\/strong> to avoid import ambiguity<\/p>\n  <p>\u2705 Define <code>__all__<\/code> in <code>__init__.py<\/code> to control the public API<\/p>\n  <p>\u2705 Add type hints and docstrings to all public functions<\/p>\n  <p>\u2705 Use <strong>ruff<\/strong> for linting, <strong>mypy<\/strong> for type checking<\/p>\n  <p>\u2705 Follow semantic versioning, update CHANGELOG per release<\/p>\n  <p>\u2705 Set up <strong>GitHub Actions<\/strong> CI for multi-version testing<\/p>\n<\/div>\n\n<div class=\"table-wrap\">\n<table>\n  <thead>\n    <tr><th>Issue<\/th><th>Cause<\/th><th>Solution<\/th><\/tr>\n  <\/thead>\n  <tbody>\n    <tr><td>Module not found after install<\/td><td>Missing packages.find in src layout<\/td><td>Add [tool.setuptools.packages.find] where = [&#8220;src&#8221;]<\/td><\/tr>\n    <tr><td>PyPI name conflict<\/td><td>Package name taken<\/td><td>Search PyPI first, choose a unique name<\/td><\/tr>\n    <tr><td>Twine auth failure<\/td><td>Token expired<\/td><td>Regenerate API token, use __token__ as username<\/td><\/tr>\n    <tr><td>CI fails but local passes<\/td><td>Environment mismatch<\/td><td>Lock dependencies with uv.lock or requirements.txt<\/td><\/tr>\n    <tr><td>Dependency hell<\/td><td>Overly strict version constraints<\/td><td>Only set lower bounds, use semver ranges<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<\/div>\n\n<div class=\"info-box\">\n  <strong>Full workflow: Design API \u2192 src layout \u2192 pyproject.toml \u2192 Write code with type hints \u2192 pytest coverage \u2192 Build locally \u2192 TestPyPI \u2192 PyPI release \u2192 CI automation \u2192 MkDocs site.<\/strong>\n<\/div>\n\n<\/div>\n<\/main>\n\n<footer>\n  <div class=\"links\">\n    <a href=\"https:\/\/packaging.python.org\/en\/latest\/tutorials\/packaging-projects\/\" target=\"_blank\" rel=\"noopener\">Python Packaging Guide<\/a>\n    <a href=\"https:\/\/pyproject.readthedocs.io\/\" target=\"_blank\" rel=\"noopener\">pyproject.toml Docs<\/a>\n    <a href=\"https:\/\/pypi.org\/\" target=\"_blank\" rel=\"noopener\">PyPI<\/a>\n    <a href=\"https:\/\/docs.astral.sh\/uv\/\" target=\"_blank\" rel=\"noopener\">uv Docs<\/a>\n  <\/div>\n  <p>Python Library Development Complete Guide<\/p>\n<\/footer>\n\n<script>\nfunction setLang(lang){\n  document.querySelectorAll('.lang-section').forEach(el=>el.classList.remove('active'));\n  document.getElementById('lang-'+lang).classList.add('active');\n  document.querySelectorAll('.lang-switch button').forEach(btn=>btn.classList.remove('active'));\n  document.getElementById('btn-'+lang).classList.add('active');\n  document.documentElement.lang = lang==='zh'?'zh-CN':'en';\n  const prefix = lang==='zh'?'zh':'en';\n  const newHash = window.location.hash.replace(\/^#(zh|en)-\/, prefix+\"-\");\n  if(newHash) window.location.hash = newHash;\n}\nfunction toggleTheme(){\n  const html = document.documentElement;\n  if(html.hasAttribute('data-theme')){\n    html.removeAttribute('data-theme');\n    localStorage.removeItem('theme');\n  }else{\n    html.setAttribute('data-theme','dark');\n    localStorage.setItem('theme','dark');\n  }\n}\n(function(){\n  if(localStorage.getItem('theme')==='dark') document.documentElement.setAttribute('data-theme','dark');\n  const hash = window.location.hash;\n  if(hash){\n    const target = document.querySelector(hash);\n    if(target) setTimeout(()=>target.scrollIntoView({behavior:'smooth'}),100);\n  }\n})();\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Python \u5e93\u5f00\u53d1\u5b8c\u6574\u6307\u5357 \u00b7 Python Library Development Guide \ud83d\udce6 Pyt [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,9],"tags":[],"class_list":["post-354","post","type-post","status-publish","format-standard","hentry","category-cad","category-ai"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/numsimlab.com\/index.php?rest_route=\/wp\/v2\/posts\/354","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/numsimlab.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/numsimlab.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/numsimlab.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/numsimlab.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=354"}],"version-history":[{"count":1,"href":"https:\/\/numsimlab.com\/index.php?rest_route=\/wp\/v2\/posts\/354\/revisions"}],"predecessor-version":[{"id":355,"href":"https:\/\/numsimlab.com\/index.php?rest_route=\/wp\/v2\/posts\/354\/revisions\/355"}],"wp:attachment":[{"href":"https:\/\/numsimlab.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=354"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/numsimlab.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=354"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/numsimlab.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=354"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}