Python range() 的边界:为什么循环有时一次也不执行?

下面这段代码不会打印任何内容,也不会报错: for x in range(10, 7): print(x) 原因很简单:range(10, 7) 使用默认的正步长,起点已经超过终点,因此范围为空,循环体一次也不执行。 本文适合刚开始学习 Python 循环的读者,阅读约需 3 分钟。 先看懂 range() 的三个参数 range() 常见的写法有三种: list(range(4)) # [0, 1, 2, 3] list(range(2, 7)) # [2, 3, 4, 5, 6] list(range(2, 7, 2)) # [2, 4, 6] 完整形式是 range(start, stop, step): start:起点,省略时默认为 0。 stop:终点,不包含在范围内。 step:步长,省略时默认为 1。 range() 返回的是一个范围对象,并不是列表。这里使用 list(),只是为了直接观察其中的值。 对于正步长,每个值都必须满足 x < stop。因此,range(2, 7) 从 2 开始,每次增加 1,到 6 为止,不包含 7。 起点碰到或超过终点,会发生什么? 默认步长是 1,所以如果一开始就有 start >= stop,连第一个值都不符合条件: list(range(7, 7)) # [] list(range(10, 7)) # [] 空范围是正常结果,不是异常。 for 循环没有值可以遍历,就会直接跳过循环体,继续执行后面的代码: ...

2026年10月8日 · 2 分钟 · map[name:Jeanphilo]

Redis 任务队列系列(1):用 Python 和 List 实现最小生产消费模型

这是 Redis 任务队列系列的第一篇:从一个具体的计算任务开始,用 Python 实现生产者和消费者,先掌握 Redis List 的入队与阻塞出队,再看为什么它还不能直接作为可靠的生产任务队列。

2026年10月8日 · 3 分钟 · map[name:Jeanphilo]

后台异步任务什么时候算队列:从生产者与消费者理解 Celery、Redis

接口返回后继续计算,是否就算任务队列?要看任务如何等待、被领取以及在进程中断后恢复。本文从一个五任务场景推导生产者与消费者模型,再讨论并发、积压、数据库租约,以及 Celery 和 Redis 分别负责什么。

2026年10月8日 · 3 分钟 · map[name:Jeanphilo]

LeetCode 139:单词拆分,从前缀是否可达开始

题目要求 输入输出 输入:字符串 s 和字符串列表 wordDict wordDict 中的每个元素都是一个可以使用的单词;同一个单词可以重复使用 输出:如果 s 可以被拆成一个或多个字典单词,返回 True;否则返回 False 拆分必须覆盖整个字符串,不能跳过字符,也不能改变字符顺序 示例 输入:s = "leetcode", wordDict = ["leet", "code"] 输出:True 解释:"leetcode" 可以拆成 "leet" + "code" 输入:s = "applepenapple", wordDict = ["apple", "pen"] 输出:True 解释:"apple" + "pen" + "apple";同一个单词可以重复使用 输入:s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] 输出:False 解释:前面可以拼出一些片段,但最后不能覆盖完整字符串 约束 1 <= s.length <= 300 1 <= wordDict.length <= 1000 1 <= wordDict[i].length <= 20 s 和 wordDict[i] 只包含小写英文字母 wordDict 中的单词互不相同 Step 1:先让“已完成的前缀”变得可见 先只问一个具体问题:对于 s = "leetcode",在确认整串能否拆开之前,我们能不能先记录“已经拆完了哪一段”? ...

2026年9月22日 · 5 分钟 · map[name:Jeanphilo]

LeetCode 213:打家劫舍 II,环形拆成两个线性问题

题目要求 输入输出 输入:整数数组 nums,nums[i] 表示第 i 间房子的金额。 房子首尾相连,不能偷相邻的两间房子,因此第 0 间和第 n - 1 间也不能同时偷。 输出:在不触发警报的前提下,最多能偷到的金额。 约束:1 <= nums.length <= 100,0 <= nums[i] <= 1000。 示例 输入:nums = [2,3,2] 输出:3 解释:第 0 间和第 2 间首尾相邻,不能同时偷;选择金额 3 的房子。 输入:nums = [1,2,3,1] 输出:4 解释:可以偷下标 0 和下标 2,也可以偷下标 1 和下标 3。 输入:nums = [1,2,3] 输出:3 这篇只用 Python,先从最小的首尾冲突开始。 Step 1:先处理首尾相邻的冲突 先问一个具体问题:nums = [2,3,2] 时,能不能沿用直线街道的做法,直接在整个数组上选择不相邻的房子? 当前基线只有一条规则:相邻房子不能同时偷。若把这三个数当成直线,可能会把下标 0 和下标 2 当成彼此不相邻,从而错误地选择两端,得到金额 4。 这在环形输入上就会失败:首尾也相邻,0 和 2 必须互斥。要消除这一个冲突,只增加一个分支规则:任何合法方案至少属于下面一个候选分支。 ...

2026年9月22日 · 4 分钟 · map[name:Jeanphilo]

LeetCode 322:零钱兑换,从更小金额推出完全背包 DP

题目要求 给定一个整数数组 coins,其中 coins[i] 是一种硬币的面额;再给定一个非负整数 amount,要求凑出刚好等于 amount 的金额。 每种硬币都可以使用任意次。 返回凑出 amount 所需的最少硬币数量。 如果无法凑出,返回 -1。 如果 amount = 0,不需要使用硬币,答案是 0。 约束是:1 <= coins.length <= 12、1 <= coins[i] <= 2^31 - 1、0 <= amount <= 10^4。硬币面额互不相同。 例如: 输入:coins = [1, 2, 5], amount = 11 输出:3 解释:5 + 5 + 1 一共使用 3 枚硬币。 输入:coins = [2], amount = 3 输出:-1 解释:只使用面额 2 无法凑出 3。 输入:coins = [1], amount = 0 输出:0 解释:目标金额已经是 0,不需要再选硬币。 Step 1:先把目标金额拆成更小的问题 先看一个会逼出问题结构的小例子: ...

2026年9月22日 · 5 分钟 · map[name:Jeanphilo]

LeetCode 300:最长递增子序列,从“以 i 结尾”推出序列 DP

题目要求 输入输出 输入:整数数组 nums 输出:返回 nums 的最长严格递增子序列长度 子序列保持原数组中的相对顺序,但不要求连续,可以跳过元素 如果选择的下标是 i_1 < i_2 < ... < i_k,还必须满足 nums[i_1] < nums[i_2] < ... < nums[i_k] 子序列非空,因此至少选择一个元素 约束:1 <= nums.length <= 2500,-10^4 <= nums[i] <= 10^4 示例 输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:[2,5,7,101] 是一个长度为 4 的严格递增子序列;它不要求在原数组中连续。 输入:nums = [0,1,0,3,2,3] 输出:4 解释:[0,1,2,3] 的长度为 4;`[0,1,3]` 也是合法但更短的选择。 这篇只用 Python,从“每个位置结尾能形成多长”这个局部问题开始,逐步写出可运行解法。 Step 1:先问“以哪个位置结束?” 先看第一个例子: 下标: 0 1 2 3 4 5 6 7 数值: 10 9 2 5 3 7 101 18 答案 [2,5,7,101] 跳过了 9 和 3。当我们走到值为 7 的位置时,不能只问“它左边相邻的数是谁”,而要问: ...

2026年9月22日 · 4 分钟 · map[name:Jeanphilo]

LeetCode 416:分割等和子集,从目标总和推到 0/1 背包倒序遍历

题目要求 给定一个只包含正整数的数组 nums,判断能不能把所有元素分成两个子集,使两个子集的元素总和相等。 输入与输出 输入:整数数组 nums 输出:如果能平分,返回 True;否则返回 False 每个元素必须放入两个子集之一,不能丢弃,也不能把同一个元素使用两次 示例 输入:nums = [1,5,11,5] 输出:True 解释:[1,5,5] 和 [11] 的总和都是 11 输入:nums = [1,2,3,5] 输出:False 解释:总和是 11,奇数不能平分 输入:nums = [1,1] 输出:True 解释:两个子集分别拿一个 1 约束 按 LeetCode 416 题面: 1 <= nums.length <= 200 1 <= nums[i] <= 100 sum(nums) <= 20000 Step 1:先排除总和为奇数的数组 先问一个还不涉及选数的问题:两个整数子集的和相等时,整个数组的总和必须满足什么条件? 问题压力 看 nums = [1,2,3,5]。它的总和是 11。如果两个子集的和都叫 x,那么必须有: 2 * x = 11 整数 x 不可能满足这个等式,所以这个输入可以在尝试任何选数之前直接判定为 False。 ...

2026年9月22日 · 5 分钟 · map[name:Jeanphilo]

Python 中 @staticmethod 和 @abstractmethod 的区别

在阅读 Repository 或 Service 代码时,经常会同时看到 @staticmethod 和 @abstractmethod。它们都写在方法上方,很容易被误认为是同一类装饰器。 实际上,两者解决的是不同问题: @staticmethod 解决方法如何被调用 @abstractmethod 约束子类必须提供什么行为 理解这个区别后,方法应该放在类中、模块中,还是抽象基类中,会清楚很多。 @staticmethod:不依赖实例状态的方法 普通实例方法默认接收 self: class KeywordMatcher: def contains(self, text: str, keyword: str) -> bool: return keyword.casefold() in text.casefold() 如果方法体不读取或修改 self,可以写成静态方法: class KeywordMatcher: @staticmethod def contains(text: str, keyword: str) -> bool: return keyword.casefold() in text.casefold() 调用时不需要创建实例: KeywordMatcher.contains("变压器采购项目", "变压器") 静态方法没有自动传入的 self,因此不能访问实例属性: class KeywordMatcher: def __init__(self, case_sensitive: bool): self.case_sensitive = case_sensitive @staticmethod def contains(text: str, keyword: str) -> bool: # 这里不能访问 self.case_sensitive return keyword in text 如果函数只是通用计算,并不属于某个类的概念,直接定义为模块级函数通常更自然: def contains(text: str, keyword: str) -> bool: return keyword.casefold() in text.casefold() 所以“不需要 self”是使用 @staticmethod 的重要条件,但还要问一句:这个行为是否真的属于该类?如果答案是否定的,就不必为了使用装饰器而把函数塞进类里。 ...

2026年9月9日 · 2 分钟 · map[name:Jeanphilo]

让领域模型和仓储模型各自负责

在业务系统逐渐变复杂时,一个对象往往同时被拿来承载业务规则、数据库字段和 HTTP 响应。开始时这很省事,后来却会出现连锁问题:数据库新增一个内部字段,接口结构跟着变化;领域代码为了查询数据而依赖 ORM;仓储把 SQLAlchemy 行对象直接交给控制器。 更稳定的做法,是让不同边界拥有不同模型,并通过转换连接它们: Controller -> Domain Model -> Repository Contract -> ORM Model -> Database | ^ +------ API Request/Response ---+ 这里的重点不是增加更多目录,而是明确每个模型对谁负责。也不需要为每个领域类都复制一套存储类:仓储结果可以直接组合领域对象与读取元数据,只有确实不同的格式或契约才需要独立表示。 四类模型,各自回答一个问题 领域模型回答“什么状态符合业务规则”。例如商品必须有 SKU 和名称,价格不能为负数。SKU 是业务标识,数据库自增 id 则不必进入领域对象。领域模型不应该导入 SQLAlchemy、Pydantic 或 Web 框架。 ORM 模型回答“如何把数据存进数据库”。它可以拥有自增主键、外键、索引、时间戳和数据库约束,但这些细节不必进入领域对象。 Repository 契约回答“应用可以怎样保存和读取数据”。它的参数和返回值属于仓储边界:保存商品可以返回 StoredProduct,其中包含领域对象和 updated_at;分页查询可以返回 ProductPage 这样的查询模型。 API 模型回答“客户端能发送和接收什么”。请求模型负责输入形状,响应模型负责 HTTP 输出。例如商品响应可以包含 updated_at,并将其序列化为日期字符串;这不意味着商品领域对象也需要这个字段。 一个最小例子 假设一个后台允许按 SKU 保存商品,再查询商品信息。下面使用同步 SQLAlchemy 2.x 展示关键代码,省略数据库连接和 HTTP 路由配置。 领域对象放在 app/domain/product.py,只表达业务状态。价格统一使用人民币分,避免浮点数表示金额: from dataclasses import dataclass @dataclass(frozen=True) class Product: sku: str name: str price_cents: int def __post_init__(self): if not self.sku or not self.name: raise ValueError("商品 SKU 和名称不能为空") if self.price_cents < 0: raise ValueError("商品价格不能为负数") 数据库模型放在 infra/db/models.py。自增主键、唯一约束和更新时间属于存储设计: ...

2026年9月9日 · 2 分钟 · map[name:Jeanphilo]