PyPython 的技术记录
A PYTHON NOTEBOOK

把问题解决,
把过程记下来。

关于 Python 的学习、尝试与日常实践。
从一小段代码开始,积累可以再次使用的经验。

阅读技术笔记 ↓
● ● ● notebook.py
# 一次学习,一点积累
from pathlib import Path

notebook = Path("今天的收获.txt")
notebook.write_text(
    "让代码更清晰一点。",
    encoding="utf-8"
)

技术笔记

5 篇记录 · 持续积累
核心机制

深入解析 Python 生成器与惰性求值

本文探讨 Python 生成器的底层原理及其在内存优化中的应用,重点介绍 yield 关键字的工作机制、迭代协议以及如何在处理大规模数据流时实现高效的惰性求值。

阅读全文

在 Python 编程中,生成器是一种极其强大且优雅的特性,它允许函数返回一个可迭代的对象,而不是直接计算并存储所有结果。理解生成器的关键在于掌握“惰性求值”这一概念。传统的列表或元组会在定义时一次性将所有元素加载到内存中,这在处理海量数据时往往会导致内存溢出或性能瓶颈。而生成器则不同,它仅在需要时才计算下一个值,这种按需计算的机制极大地降低了内存占用。

生成器的实现依赖于 yield 关键字。当函数体中包含 yield 语句时,该函数不再是一个普通函数,而是一个生成器函数。调用生成器函数并不会立即执行函数体内的代码,而是返回一个生成器对象。这个对象实现了迭代器协议,即包含 __iter__ 和 __next__ 方法。每当通过 for 循环或 next() 函数请求下一个值时,生成器会暂停在当前 yield 语句处,保存局部变量状态和执行上下文,并将产出的值返回给调用者。当下一次请求到来时,生成器从上次暂停的地方恢复执行,直到遇到下一个 yield 或函数结束。

除了节省内存,生成器还简化了代码结构。在处理文件读取、网络数据流或无限序列时,使用生成器可以避免编写复杂的索引管理和缓冲区逻辑。例如,逐行读取大文件时,传统方式可能需要将整个文件载入内存,而生成器可以确保每次只在内存中保留当前行。此外,Python 提供了生成器表达式,这是一种类似列表推导式的简洁语法,但返回的是生成器而非列表,进一步提升了代码的可读性和效率。

需要注意的是,生成器是一次性的迭代器。一旦遍历完成,其内部状态耗尽,无法再次从头开始遍历,除非重新创建生成器实例。因此,如果需要多次访问同一数据集,应谨慎选择是否使用生成器。在实际开发中,合理运用生成器结合 itertools 标准库中的工具,可以构建出高效、低内存占用的数据处理管道,这是 Pythonic 风格编程的重要组成部分。

def read_large_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            yield line.strip()

# 示例用法:假设有一个名为 data.txt 的文件
# generator = read_large_file('data.txt')
# for chunk in generator:
#     process(chunk)

本文由 AI 自动生成,尚未经人工审校;请结合官方文档验证。

Python 技术记录

理解 Python 的装饰器机制

本文深入解析 Python 装饰器的核心概念,探讨其基于闭包和高阶函数的实现原理。通过剖析 functools.wraps 的作用及带参数装饰器的构建方法,帮助开发者掌握代码复用与逻辑分离的技巧,提升程序的可维护性与优雅度。

阅读全文

在 Python 编程实践中,装饰器是一种强大且优雅的语法糖,它允许我们在不修改原有函数源代码的前提下,动态地扩展函数的功能。这一机制的核心在于 Python 将函数视为一等公民,即函数可以作为参数传递、作为返回值返回,以及被赋值给变量。装饰器本质上是一个高阶函数,它接收一个函数作为输入,并返回一个新的函数作为输出。这种设计模式极大地促进了代码的模块化,使得日志记录、权限校验、性能监控等横切关注点能够从业务逻辑中剥离出来,从而保持核心代码的简洁与专注。

要真正理解装饰器,必须掌握闭包的概念。当内部函数引用了外部作用域中的变量时,即使外部函数已经执行完毕,这些变量依然会被保留在内存中,供内部函数使用。装饰器正是利用这一特性,在包装函数中捕获原始函数,并在调用包装函数时插入额外的逻辑。然而,简单的装饰器会改变原始函数的元数据,如名称和文档字符串,这可能导致调试困难或依赖元数据的库无法正常工作。为了解决这个问题,标准库提供了 functools.wraps 装饰器,它能自动将原始函数的属性复制到包装后的函数上,确保行为的一致性。

此外,带参数的装饰器进一步增强了灵活性。这类装饰器通常由三层嵌套函数构成:最外层接收装饰器参数,中间层接收被装饰的函数,最内层则是实际执行的包装逻辑。这种结构虽然增加了复杂性,但赋予了开发者在应用装饰器时配置行为的能力,例如设置重试次数或缓存过期时间。在实际开发中,合理使用装饰器不仅能减少重复代码,还能使程序结构更加清晰。需要注意的是,过度使用多层装饰器可能会增加栈深度,影响性能或导致异常追踪变得复杂,因此应在可读性与功能性之间寻找平衡。掌握装饰器是进阶 Python 开发的必经之路,它体现了 Python 语言对简洁性和表达力的追求。

import functools
import time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        print(f"Function {func.__name__} executed in {end_time - start_time:.4f} seconds")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return "Done"

slow_function()

本文由 AI 自动生成,尚未经人工审校;请结合官方文档验证。

标准库

用 pathlib,把文件路径写清楚

从拼接字符串到操作路径对象,让日常文件处理更容易阅读和维护。

阅读全文

处理文件时,可以把路径当作对象,而不是自己拼接斜杠。pathlib 是 Python 标准库的一部分。Path 对象支持使用 / 运算符连接路径片段,也提供读取、写入和遍历目录的方法。

下面的例子在当前工作目录下创建 notes 文件夹,再写入并读取一段中文。显式指定 UTF-8 编码,可以减少不同系统默认编码带来的差异。

注意:write_text 会覆盖同名文件的原有内容。处理重要数据前,请先确认目标路径。相对路径以程序启动时的工作目录为基准,而不一定是脚本所在目录。

from pathlib import Path

folder = Path.cwd() / 'notes'
folder.mkdir(parents=True, exist_ok=True)
file = folder / 'hello.txt'
file.write_text('你好,Python!', encoding='utf-8')
print(file.read_text(encoding='utf-8'))
数据处理

用 Counter 统计重复出现的元素

几行代码完成计数,适合整理标签、日志类别和简单统计。

阅读全文

统计列表中每个元素出现了多少次,可以使用 collections.Counter。它接收可迭代对象,并把元素映射到对应的计数。

most_common 方法按计数从高到低返回元素及其数量。示例会得到 python 出现 3 次、sql 出现 2 次。Counter 适合可以完整遍历的数据;对于很大的文件,可以逐行读取并逐步更新计数,避免一次性读入全部原始内容。

from collections import Counter

tags = ['python', 'sql', 'python', 'web', 'sql', 'python']
counts = Counter(tags)
for tag, count in counts.most_common(2):
    print(tag, count)
日常实践

读写 JSON 时,保留中文的可读性

明确编码、格式化缩进,让小型配置文件也能整洁易读。

阅读全文

JSON 常用于保存配置和交换结构化数据。Python 的 json 模块能够在字典、列表等对象与 JSON 文本之间转换。

ensure_ascii=False 让中文直接显示在输出文本中;indent=2 为嵌套结构添加缩进。这两个参数改善可读性,不改变数据本身。文件读写仍然需要指定 UTF-8 编码。

读取外部 JSON 时,要处理文件不存在、格式错误等情况,并检查字段类型。不要把密码或 API 密钥写进对外提供访问的 JSON 文件。

import json

settings = {'名称': 'Python 的技术记录', '语言': '中文'}
text = json.dumps(settings, ensure_ascii=False, indent=2)
print(text)
restored = json.loads(text)
assert restored == settings