Python 函数、模块、包与项目结构,从重复代码到可维护小项目
2026/6/17 7:58:13 网站建设 项目流程

写 Python 的前几天,所有代码放在一个文件里完全没问题。

但很快你会遇到两个问题。

第一,重复代码越来越多。比如计算总价、校验输入、读取文件,这些逻辑到处复制。

第二,文件越来越长。几百行代码堆在一起,想找一个函数都费劲。

函数、模块、包和项目结构,就是为了解决这两个问题。

它们是一条线:

重复代码

函数

多个函数放进模块

多个模块组成包

形成清晰项目结构

函数解决重复代码

不用函数时:

price=19.9count=3total=price*countprint(total)price=9.9count=5total=price*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:"""计算 BMI,height 单位为米,weight 单位为千克。"""returnweight/(height*height)bmi=calc_bmi(1.75,70)print(f"BMI:{bmi:.2f}")

输入是身高和体重。

输出是 BMI。

如果一个函数说不清输入输出,就很可能设计得不清楚。

printreturn不一样

defadd(a,b):print(a+b)result=add(10,20)print(result)

这段会先打印30,再打印None

原因是函数没有返回值。

正确写法:

defadd(a,b):returna+b result=add(10,20)print(result)

判断标准:

如果结果只是给人看,用print()

如果结果还要给后续代码使用,用return

参数的几种写法

位置参数:

defintroduce(name,age):returnf"我叫{name},今年{age}岁"print(introduce("小明",18))

关键字参数:

print(introduce(age=18,name="小明"))

默认参数:

defgreet(name,prefix="你好"):returnf"{prefix}{name}"print(greet("小明"))print(greet("小红","早上好"))

默认参数要小心可变对象。

不要这样写:

defadd_item(item,items=[]):items.append(item)returnitems

因为默认列表会被多次调用共享。

更安全:

defadd_item(item,items=None):ifitemsisNone:items=[]items.append(item)returnitems

作用域,变量在哪里有效

函数内部定义的变量,默认只在函数内部有效。

defsay_hello():message="你好"print(message)say_hello()# print(message) 这里会报错

函数外面的变量叫全局变量。

course="Python"defprint_course():print(course)print_course()

能读全局变量,不代表应该大量依赖全局变量。

函数最好通过参数接收数据,通过返回值交出结果。这样函数更独立,也更容易测试。

函数拆分原则

一个函数最好只做一件清楚的事。

比如订单处理,可以拆成:

defcalc_discount(amount:float)->float:ifamount>=200:returnamount*0.8ifamount>=100:returnamount-20returnamountdefprint_order(amount:float)->None:final_amount=calc_discount(amount)print(f"应付金额:{final_amount:.2f}")print_order(268)

calc_discount()只负责计算。

print_order()只负责展示。

职责拆开后,后面要改优惠规则,不会影响展示逻辑。

Docstring 怎么写才有价值

Docstring 不是给显而易见的代码写废话。

不推荐:

defadd(a,b):"""把 a 和 b 相加。"""returna+b

这个注释没有提供新信息。

更有价值:

defcalc_discount(price:float,discount:float)->float:"""计算折后价格,discount 取值范围为 0 到 1。"""returnprice*discount

这里说明了discount的取值范围,这个信息单看代码不一定知道。

一个.py文件就是一个模块

当函数越来越多,就应该拆文件。

项目结构:

project ├── main.py └── price_utils.py

price_utils.py

defcalc_discount(amount:float)->float:ifamount>=200:returnamount*0.8ifamount>=100:returnamount-20returnamount

main.py

fromprice_utilsimportcalc_discount amount=268final_amount=calc_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 .venv

Windows 激活:

.venv\Scripts\activate

安装依赖:

pipinstallrequests

导出依赖:

pip freeze>requirements.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.py

main.py放入口逻辑。

data放数据文件。

services放业务逻辑。

utils放通用工具函数。

项目结构不是越复杂越好。小项目从简单开始,随着代码变多再拆。

常见错误

文件名和标准库重名

不要把文件命名为:

json.py csv.py math.py random.py

这些名字和标准库冲突,可能导致导入异常。

循环导入

A 导入 B,B 又导入 A,可能出现循环导入。

解决方法通常不是硬改导入语句,而是重新拆职责,把共同依赖的内容放到第三个模块。

在模块顶层写太多执行代码

模块被导入时,顶层代码会执行。

测试代码放进:

if__name__=="__main__":...

当前目录不对导致导入失败

建议始终在项目根目录运行入口文件。

不要一会儿在子目录运行,一会儿在根目录运行,否则导入路径会变得难查。

练习,拆一个价格计算项目

创建结构:

price_project ├── main.py └── price_utils.py

price_utils.py

defcalc_discount(amount:float)->float:"""根据订单金额计算优惠后价格。"""ifamount>=200:returnamount*0.8ifamount>=100:returnamount-20returnamount

main.py

fromprice_utilsimportcalc_discount amount=float(input("请输入订单金额:"))final_amount=calc_discount(amount)print(f"应付金额:{final_amount:.2f}")

运行:

python main.py

如果能跑通,说明你已经从单文件脚本走到了最小项目结构。

参考资料

  • Python 官方函数教程:https://docs.python.org/3/tutorial/controlflow.html#defining-functions
  • Python 官方模块教程:https://docs.python.org/3/tutorial/modules.html
  • Python 虚拟环境教程:https://docs.python.org/3/tutorial/venv.html
  • PEP 8:https://peps.python.org/pep-0008/

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询