项目开发
编程风格¶
变量命名¶
PEP 8要求:
- 用小写字母拼写,多个单词用下划线连接。
- 受保护的实例属性用单个下划线开头。
- 私有的实例属性用两个下划线开头。
__name__ == __'main'__
:在该模块被当作独立模块运行时才执行的语句
具体用法如下:
- 性能测试或调试代码
在开发过程中,可能需要临时添加一些性能测试或调试代码。使用if __name__ == '__main__':
可以确保这些代码只在直接运行时执行。
示例
# performance_test.py
<div markdown="1" style="margin-top: -30px; font-size: 0.75em; opacity: 0.7;">
:material-circle-edit-outline: 约 1353 个字 :fontawesome-solid-code: 170 行代码 :material-clock-time-two-outline: 预计阅读时间 7 分钟
</div>
import time
def slow_function():
time.sleep(2)
return "Done"
if __name__ == '__main__':
start_time = time.time()
result = slow_function()
end_time = time.time()
print(f"Result: {result}, Time taken: {end_time - start_time:.2f} seconds")
直接运行
输出:导入模块
- 脚本与模块的复用
一个文件既可以作为脚本独立运行,也可以作为模块被其他程序导入。if __name__ == '__main__':
是实现这种复用的关键。
示例
# data_processor.py
def process_data(data):
return [x * 2 for x in data]
if __name__ == '__main__':
# 直接运行时处理一些示例数据
data = [1, 2, 3, 4]
print("Processed data:", process_data(data))
直接运行
输出:导入模块
import data_processor
data = [10, 20, 30]
print(data_processor.process_data(data)) # 输出: [20, 40, 60]
- 命令行工具
在编写命令行工具时,通常会将工具的主要逻辑放在if __name__ == '__main__':
块中,以便直接运行脚本时执行工具功能。
示例
# file_utils.py
import sys
def count_lines(filename):
with open(filename, 'r') as file:
return len(file.readlines())
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage: python file_utils.py <filename>")
else:
filename = sys.argv[1]
print(f"Number of lines in {filename}: {count_lines(filename)}")
直接运行
输出:导入模块
- 避免副作用
当模块被导入时,可能会执行一些不必要的代码(如打印日志、初始化变量等)。使用if __name__ == '__main__':
可以避免这些副作用。
示例
# logger.py
def log(message):
print(f"LOG: {message}")
if __name__ == '__main__':
# 直接运行时初始化一些日志
log("Initializing logger...")
直接运行
输出:导入模块
- 初始化日志的代码不会执行。- 多文件项目中的入口文件
在大型项目中,通常有一个主入口文件(如main.py
),用于启动整个程序。其他模块文件可以通过if __name__ == '__main__':
提供独立运行的功能。
示例
# main.py
from utils import helper
def main():
print("Starting the program...")
helper.do_something()
if __name__ == '__main__':
main()
# utils/helper.py
def do_something():
print("Doing something...")
if __name__ == '__main__':
# 直接运行时测试功能
do_something()
运行 main.py
直接运行 helper.py
总结
场景 | 用途 |
---|---|
模块的测试代码 | 确保测试代码只在直接运行时执行 |
脚本与模块的复用 | 让文件既能独立运行,又能被导入 |
命令行工具 | 提供命令行接口,方便直接运行 |
避免副作用 | 防止导入模块时执行不必要的代码 |
多文件项目中的入口文件 | 提供独立运行的功能,同时支持主程序调用 |
性能测试或调试代码 | 临时添加测试代码,不影响模块导入 |
通过 if __name__ == '__main__':
,你可以更好地组织代码,使其更灵活、更易于维护。如果还有其他问题,欢迎继续提问!
通过 嵌套作用域,可以将数据隐藏在函数内部,避免使用全局变量,从而减少代码的耦合性和潜在的错误。这种方式利用了 Python 的作用域规则,使得外层函数的变量对嵌套函数可见,但对全局作用域不可见,从而实现了一种封装的效果。
如何通过嵌套作用域隐藏数据?¶
示例 1:计数器¶
假设我们需要实现一个计数器,但不想使用全局变量。可以通过嵌套作用域将计数器隐藏在函数内部:
def create_counter():
count = 0 # 计数器变量,隐藏在 create_counter 的作用域中
def counter():
nonlocal count # 声明 count 是外层作用域的变量
count += 1
return count
return counter # 返回嵌套函数
# 创建两个独立的计数器
counter1 = create_counter()
counter2 = create_counter()
print(counter1()) # 输出: 1
print(counter1()) # 输出: 2
print(counter2()) # 输出: 1
print(counter2()) # 输出: 2
说明: - count
变量被隐藏在 create_counter
的作用域中,外部无法直接访问或修改。 - 每次调用 create_counter
都会创建一个新的独立计数器,避免了全局变量的污染。
示例 2:缓存函数¶
通过嵌套作用域,可以实现一个缓存函数,避免重复计算:
def create_cached_function():
cache = {} # 缓存数据,隐藏在 create_cached_function 的作用域中
def cached_function(x):
if x in cache:
print("Returning cached result")
return cache[x]
else:
print("Calculating result")
result = x * x # 假设这是一个耗时的计算
cache[x] = result
return result
return cached_function # 返回嵌套函数
# 创建缓存函数
square = create_cached_function()
print(square(2)) # 输出: Calculating result \n 4
print(square(2)) # 输出: Returning cached result \n 4
print(square(3)) # 输出: Calculating result \n 9
说明: - cache
变量被隐藏在 create_cached_function
的作用域中,外部无法直接访问或修改。 - 缓存数据只在 cached_function
内部使用,避免了全局变量的污染。
示例 3:私有数据¶
通过嵌套作用域,可以模拟私有数据:
def create_person(name, age):
data = {"name": name, "age": age} # 私有数据,隐藏在 create_person 的作用域中
def get_name():
return data["name"]
def get_age():
return data["age"]
def set_age(new_age):
nonlocal data
data["age"] = new_age
return get_name, get_age, set_age # 返回嵌套函数
# 创建一个人对象
get_name, get_age, set_age = create_person("Alice", 30)
print(get_name()) # 输出: Alice
print(get_age()) # 输出: 30
set_age(31)
print(get_age()) # 输出: 31
说明: - data
变量被隐藏在 create_person
的作用域中,外部无法直接访问或修改。 - 只能通过返回的嵌套函数(get_name
、get_age
、set_age
)来访问或修改数据,实现了数据的封装。
这就是一种闭包:
闭包(Closure) 是指一个函数能够记住并访问其词法作用域(Lexical Scope)中的变量,即使该函数在其词法作用域之外执行。闭包是函数式编程中的一个重要概念,广泛应用于 Python 等支持嵌套函数的语言中。
闭包的核心特点¶
- 嵌套函数:闭包通常涉及嵌套函数,即在一个函数内部定义另一个函数。
- 访问外层变量:内层函数可以访问外层函数的变量(即使外层函数已经执行完毕)。
- 延长变量生命周期:闭包使得外层函数的变量不会被销毁,即使外层函数已经返回。
闭包的形成条件¶
- 必须有一个嵌套函数(内层函数)。
- 内层函数必须引用外层函数的变量。
- 外层函数必须返回内层函数。
闭包的示例¶
示例 1:闭包的实际应用(计数器)¶
def create_counter():
count = 0 # 外层函数的局部变量
def counter():
nonlocal count # 声明 count 是外层作用域的变量
count += 1
return count
return counter # 返回内层函数
counter1 = create_counter()
print(counter1()) # 输出: 1
print(counter1()) # 输出: 2
counter2 = create_counter()
print(counter2()) # 输出: 1
说明: - counter
是一个闭包,它记住了 create_counter
中的变量 count
。 - 每次调用 create_counter
都会创建一个新的独立计数器。
示例 2:闭包的实际应用(缓存函数)¶
def create_cached_function():
cache = {} # 外层函数的局部变量
def cached_function(x):
if x in cache:
print("Returning cached result")
return cache[x]
else:
print("Calculating result")
result = x * x # 假设这是一个耗时的计算
cache[x] = result
return result
return cached_function # 返回内层函数
square = create_cached_function()
print(square(2)) # 输出: Calculating result \n 4
print(square(2)) # 输出: Returning cached result \n 4
说明: - cached_function
是一个闭包,它记住了 create_cached_function
中的变量 cache
。 - 缓存数据只在 cached_function
内部使用,避免了全局变量的污染。