写 Python 的前几天所有代码放在一个文件里完全没问题。但很快你会遇到两个问题。第一重复代码越来越多。比如计算总价、校验输入、读取文件这些逻辑到处复制。第二文件越来越长。几百行代码堆在一起想找一个函数都费劲。函数、模块、包和项目结构就是为了解决这两个问题。它们是一条线重复代码函数多个函数放进模块多个模块组成包形成清晰项目结构函数解决重复代码不用函数时price19.9count3totalprice*countprint(total)price9.9count5totalprice*countprint(total)同样的计算逻辑写了两遍。用函数defcalc_total(price:float,count:int)-float:计算商品总价。returnprice*countprint(calc_total(19.9,3))print(calc_total(9.9,5))函数的价值不是少写几行代码而是把一段逻辑集中管理。函数的输入和输出函数最重要的是想清楚输入和输出。defcalc_bmi(height:float,weight:float)-float:计算 BMIheight 单位为米weight 单位为千克。returnweight/(height*height)bmicalc_bmi(1.75,70)print(fBMI{bmi:.2f})输入是身高和体重。输出是 BMI。如果一个函数说不清输入输出就很可能设计得不清楚。print和return不一样defadd(a,b):print(ab)resultadd(10,20)print(result)这段会先打印30再打印None。原因是函数没有返回值。正确写法defadd(a,b):returnab resultadd(10,20)print(result)判断标准如果结果只是给人看用print()。如果结果还要给后续代码使用用return。参数的几种写法位置参数defintroduce(name,age):returnf我叫{name}今年{age}岁print(introduce(小明,18))关键字参数print(introduce(age18,name小明))默认参数defgreet(name,prefix你好):returnf{prefix}{name}print(greet(小明))print(greet(小红,早上好))默认参数要小心可变对象。不要这样写defadd_item(item,items[]):items.append(item)returnitems因为默认列表会被多次调用共享。更安全defadd_item(item,itemsNone):ifitemsisNone:items[]items.append(item)returnitems作用域变量在哪里有效函数内部定义的变量默认只在函数内部有效。defsay_hello():message你好print(message)say_hello()# print(message) 这里会报错函数外面的变量叫全局变量。coursePythondefprint_course():print(course)print_course()能读全局变量不代表应该大量依赖全局变量。函数最好通过参数接收数据通过返回值交出结果。这样函数更独立也更容易测试。函数拆分原则一个函数最好只做一件清楚的事。比如订单处理可以拆成defcalc_discount(amount:float)-float:ifamount200:returnamount*0.8ifamount100:returnamount-20returnamountdefprint_order(amount:float)-None:final_amountcalc_discount(amount)print(f应付金额{final_amount:.2f})print_order(268)calc_discount()只负责计算。print_order()只负责展示。职责拆开后后面要改优惠规则不会影响展示逻辑。Docstring 怎么写才有价值Docstring 不是给显而易见的代码写废话。不推荐defadd(a,b):把 a 和 b 相加。returnab这个注释没有提供新信息。更有价值defcalc_discount(price:float,discount:float)-float:计算折后价格discount 取值范围为 0 到 1。returnprice*discount这里说明了discount的取值范围这个信息单看代码不一定知道。一个.py文件就是一个模块当函数越来越多就应该拆文件。项目结构project ├── main.py └── price_utils.pyprice_utils.pydefcalc_discount(amount:float)-float:ifamount200:returnamount*0.8ifamount100:returnamount-20returnamountmain.pyfromprice_utilsimportcalc_discount amount268final_amountcalc_discount(amount)print(f应付金额{final_amount:.2f})price_utils.py就是模块。导入方式怎么选导入整个模块importmathprint(math.sqrt(16))从模块导入指定函数frommathimportsqrtprint(sqrt(16))起别名importpandasaspd不推荐初学者随便写frommathimport*因为它会把很多名字直接放进当前文件容易冲突也不容易看出函数来源。__name__和程序入口很多文件底部会写defmain():print(程序开始)if__name____main__:main()意思是只有当前文件被直接运行时才执行main()。如果这个文件被别的模块导入不会自动执行。这能避免导入模块时顺手执行一堆测试代码。包是什么多个模块可以放进一个文件夹这个文件夹就可以作为包。book_project ├── main.py └── utils ├── __init__.py ├── file_utils.py └── price_utils.py导入fromutils.price_utilsimportcalc_discount传统 Python 包里常见__init__.py。它可以为空也可以控制包导出内容。__pycache__是什么运行 Python 项目后你可能看到__pycache__这是 Python 生成的字节码缓存目录用来加快模块加载。它不是你手写的源码一般不用管。如果后面使用 Git 管理代码通常会把__pycache__加进忽略文件。虚拟环境和 pip项目开始用第三方库后就需要虚拟环境。创建python-mvenv .venvWindows 激活.venv\Scripts\activate安装依赖pipinstallrequests导出依赖pip freezerequirements.txt别人拿到项目后安装pipinstall-rrequirements.txt虚拟环境的意义是让每个项目有自己的依赖避免项目之间互相污染。一个推荐的小项目结构python-study-project ├── main.py ├── requirements.txt ├── data │ └── books.json ├── services │ ├── __init__.py │ └── book_service.py └── utils ├── __init__.py └── file_utils.pymain.py放入口逻辑。data放数据文件。services放业务逻辑。utils放通用工具函数。项目结构不是越复杂越好。小项目从简单开始随着代码变多再拆。常见错误文件名和标准库重名不要把文件命名为json.py csv.py math.py random.py这些名字和标准库冲突可能导致导入异常。循环导入A 导入 BB 又导入 A可能出现循环导入。解决方法通常不是硬改导入语句而是重新拆职责把共同依赖的内容放到第三个模块。在模块顶层写太多执行代码模块被导入时顶层代码会执行。测试代码放进if__name____main__:...当前目录不对导致导入失败建议始终在项目根目录运行入口文件。不要一会儿在子目录运行一会儿在根目录运行否则导入路径会变得难查。练习拆一个价格计算项目创建结构price_project ├── main.py └── price_utils.pyprice_utils.pydefcalc_discount(amount:float)-float:根据订单金额计算优惠后价格。ifamount200:returnamount*0.8ifamount100:returnamount-20returnamountmain.pyfromprice_utilsimportcalc_discount amountfloat(input(请输入订单金额))final_amountcalc_discount(amount)print(f应付金额{final_amount:.2f})运行python main.py如果能跑通说明你已经从单文件脚本走到了最小项目结构。参考资料Python 官方函数教程https://docs.python.org/3/tutorial/controlflow.html#defining-functionsPython 官方模块教程https://docs.python.org/3/tutorial/modules.htmlPython 虚拟环境教程https://docs.python.org/3/tutorial/venv.htmlPEP 8https://peps.python.org/pep-0008/