m1 是collect_finetuning.py 中的全局变量:
m1: list = []
当我通过以下方式运行collect_finetuning.py 时:
if __name__ == "__main__":
main()
我们看到这种行为,其中collect_finetuning 中的函数更新了m1,并且它丢失了main() 中的更新值:|| |我能够在第二张图片(主)中获得相同的函数,以包含 m1 的现已更新版本,按预期,只需运行此脚本而不是直接运行collect_finetuning.py:
如果我不这样做这,当我返回到 main 时,在 invoke() 之后,我得到:
from scripts.collect_finetuning import main
main()
我很困惑为什么它会起作用(m1 保持更新,当我们返回到 main() 时我可以继续使用它的更新值,以及为什么直接运行collect_finetuning.py不会保留m1的更新值,请帮助我理解为什么这有效,谢谢!
print("m1 was:", m1)
[]
I am so confused as to why this worked (m1 stays updated and I can continue to use its updated value when we return back to main(), and why running collect_finetuning.py directly doesn't retain the updated value of m1. Please help me understand why this worked, thank you!
这个问题的关键在于你如何运行代码以及 Python 如何处理模块作用域。
行为差异的原因
-
直接运行
collect_finetuning.py
: 当你直接运行collect_finetuning.py
时,它被视为 主模块 。这意味着它的__name__
属性会被设置为"__main__"
。因此,if __name__ == "__main__":
块内的代码会被执行,包括调用main()
函数。任何对m1
的修改都发生在这个主模块的作用域内。 -
导入并调用
main()
: 当你从另一个脚本导入collect_finetuning
并调用main()
时,collect_finetuning.py
被视为一个 模块 。它的__name__
属性会被设置为模块的名称(即 "collect_finetuning")。因此,if __name__ == "__main__":
块内的代码 不会被自动执行 。你只是导入了模块,并调用了其中的main()
函数。在这个情况下,对m1
的修改仍然发生在collect_finetuning
模块的作用域内,而不是调用脚本的作用域内。
为什么添加入口点有效
当你添加一个新的入口点脚本并从中调用
collect_finetuning.main()
时,你实际上是在创建一个新的执行环境。这个新的环境会导入
collect_finetuning
模块,并执行它的
main()
函数。在这个新的环境中,
collect_finetuning.m1
被修改,并且这个修改在新的环境中是可见的。
解决方法
为了避免这种混淆,最好 不要依赖全局变量在模块之间共享状态 。以下是一些替代方法:
-
函数参数和返回值
: 将
m1
作为参数传递给需要它的函数,并在函数完成时返回更新后的值。 -
类
: 使用类来封装数据(
m1
)和操作它的方法。这样可以更清晰地管理状态。
示例
# collect_finetuning.py
def collect_data():
m1 = []
# ... 修改 m1 ...
return m1
def main():
m1 = collect_data()
print("m1 in main:", m1)
# main_script.py
from scripts.collect_finetuning import main
if __name__ == "__main__":
main()
在这个例子中,
collect_data
函数负责修改
m1
,并将更新后的值返回给
main
函数。这种方式更清晰地表达了数据流,避免了全局变量带来的问题。