
简介这份《沪教版小学数学一年级知识点归纳》面向使用沪教版教材的一年级学生、家长及低年级数学教师用于课后复习、单元梳理与期末查漏补缺帮助孩子把零散课堂内容整理成清晰的数学框架。资源包共1个文件为docx文档格式压缩包约40KB下载后可直接打开阅读或按需打印适合作为家庭辅导与课堂教学的随身提纲。内容覆盖加减法五大变化规律、加减法互逆关系、比一比与括号填数方法以及数位、计数单位、数的组成与位值表等认数要点并延伸到相邻数、相邻整十数、百以内数的大小比较、人民币元角分换算以及钟面认识、12时与24时计时法互化、时间计算和两位数加减整十数、一位数等运算方法。已有552人学习适合需要一份系统、条理分明的知识点清单来辅助日常复习与阶段检测的读者。1. 沪教版一年级知识点归纳.docx为什么要先拆成结构化数据家长群里流传的《沪教版小学数学一年级知识点归纳.docx》四十来条内容从加减法五条规律一路排到 24 时计时法互化照着抄一遍做成练习册大概要搭进去一个周末。真动手就会发现卡人的不是 Word 排版而是「括号里最大能填几」「回到整十数」「分针指着 6 是几时半」这些规则必须能被程序判定——写不成函数就只能一直手工出题、手工对答案。这份资料的价值恰好在这里它把一年级下册的考点压缩成了一份可枚举的规则清单加减法规律、数位与计数单位、百以内比较、人民币换算、时间互化每一块都能落成一张判定表。适合两类人一类是做教育工具、题库或小程序需要一份能直接进库的小学数学规则底表另一类是懂点 Python 的家长想按孩子的错题批量生成练习。常见做法是用 python-docx 把段落和表格抽出来先 JSON 化再给每类考点单独写判定引擎最后用断言把规则钉死。2. 用 python-docx 拆解知识点从段落流到 JSON 数据模型一份 .docx 说到底是 zip 包加一堆 XML正文在 word/document.xml 里按顺序排布。python-docx 把这层壳剥掉之后外面只剩两个入口doc.paragraphs 是段落流doc.tables 是表格集合两者互不包含——文档里表格出现的位置在 paragraphs 里是看不见的。这个特性决定了一年级这类「正文 位值表 钟面对照表」混排的资料必须分两趟走先按段落切知识点再把表格按文档顺序挂回去否则抽出来的东西顺序全乱。2.1 先探查文档骨架段落、表格与自动编号环境就是pip install python-docx没有别的依赖。第一趟不要急着写抽取逻辑先把骨架打出来看知识点开头是「1. 加减法中的规律」这种手打编号还是 Word 的自动编号「百位 十位 个位」那张表是真表格还是用空格凑出来的这两种情况的解析路径完全不同。# probe_docx.py先把 docx 的骨架打出来再决定抽取策略 from docx import Document doc Document(沪教版小学数学一年级知识点归纳.docx) for i, p in enumerate(doc.paragraphs): txt p.text.strip() if not txt: # 空段落通常是排版留白直接跳过 continue # style.name 能区分「标题 1」和「正文」方便判断层级 print(i, |, p.style.name, |, txt[:40]) print(表格数量:, len(doc.tables)) for ti, t in enumerate(doc.tables): print(ti, 行数, len(t.rows), 列数, len(t.columns))逻辑说明p.text 只返回可见文字自动编号不在里面。如果打印出来的知识点缺了「1.」「2.」前缀说明序号是 Word 自动编号生成的得换一条路——读 p._p.xml 里的 numPr 节点或者干脆按出现顺序自己补号。参数上没什么可调的遇到合并单元格的表格t.rows[i].cells 会把同一个单元格重复返回后面去重即可。提示如果打印出来只有寥寥几段正文大概率被放进了文本框或页眉python-docx 默认取不到需要解压 docx 直接读 word/document.xml。2.2 知识点切分与字段设计切分的锚点就是编号原文四十多条编号从 1 排到 42。这里有个坑12.1 这类小数、1.5 米这类量词也长得像编号正则必须加一层排除——编号后面紧跟数字的不当知识点开头。字段设计上不建议把整段原文堆进去一年级数学的规则、公式、例题是三种东西后面出题时用法完全不同混在一起就得每次重新抠。字段类型示例说明idstringKP23知识点编号题目溯源用titlestring两位数加一位数的进位加法章节短名做检索键formulaslist[加数加数和]可被判定的等式供校验ruleslist[个位满10向十位进1]自然语言规则生成提示语用sampleslist[27532]原文例题直接当回归用例tagslist[加减法, 进位]组卷和标签筛选用import json, re from docx import Document # 编号开头且编号后面不是数字用否定前瞻排除 12.1、1.5 这类 NUM_RE re.compile(r^\s*(\d{1,2})\s*[.、]\s*(?!\d)(.)$) def split_knowledge_points(path): doc Document(path) items, cur [], None for p in doc.paragraphs: txt p.text.strip() if not txt: continue m NUM_RE.match(txt) if m: # 命中锚点开新条目 cur {id: fKP{int(m.group(1)):02d}, title: m.group(2)[:30], raw: [txt]} items.append(cur) elif cur is not None: # 续行并入当前条目 cur[raw].append(txt) return items items split_knowledge_points(沪教版小学数学一年级知识点归纳.docx) print(len(items)) # ensure_asciiFalse 保留中文indent2 方便人工比对 print(json.dumps(items[22], ensure_asciiFalse, indent2))逻辑说明(?!\d) 是关键缺了它 12.1 会被切成一条独立知识点条数直接超标。cur 为 None 时丢弃的段落是文档开头的标题和页眉残留。参数上编号上限别写死原文用两位数\d{1,2}够用超过 99 条改成\d。切完先 count 一下四十条出头才算正常差得多就说明锚点没匹配上回头去看 2.1 的探查结果。2.3 表格型知识点抽取与数据校验位值表、12 时与 24 时对照表是这份资料里信息密度最高的部分抽取思路是首行当表头、其余行做 dict。这类表格列数不固定用 zip 对齐后取短边比按索引硬取稳遇到行尾多一个空单元格也不会崩。def tables_to_records(doc): 把 docx 里所有表格转成 [{表头: 单元格}] 结构 records [] for t in doc.tables: header [c.text.strip() for c in t.rows[0].cells] for row in t.rows[1:]: cells [c.text.strip() for c in row.cells] records.append(dict(zip(header, cells))) return records def validate(items, expected_min40): 最低限度的数据校验出问题早报早修 assert len(items) expected_min, f只切到 {len(items)} 条检查编号格式 for it in items: assert it[title], f{it[id]} 标题为空 return True doc Document(沪教版小学数学一年级知识点归纳.docx) recs tables_to_records(doc) validate(split_knowledge_points(沪教版小学数学一年级知识点归纳.docx)) print(recs[0])逻辑说明expected_min 按原文条数往少里给42 条的资料给 40 就行留出合并条目的余量。断言放在函数里方便以后直接当单元测试跑而不是散在脚本中。两条时间对照表抽出来就是 12 时到 24 时的映射样本正好用来验证第 5 章的互化函数比手工敲测试用例省事得多。3. 加减法五大规律与「括号里最大能填几」的判定引擎资料前三条全在讲加减法的规律这些规律在纸上读着都懂写进代码却容易变形尤其是「一个加数增加、另一个减少相同的数和不变」这种双变量同时变化的方向写反了照样能跑出结果只是结果是错的。这一章先把五条规律形式化再拿它们去支撑课本 P5 那类「括号里最大能填几」的题。3.1 五条规律的形式化与等式变形原文的五条规律本质上是恒等式用 a、b、n 三个变量就能写全。表格把它摊开右边一列是它在题库里对应的题型出题时按题型取规律比按章节取更准。原文规律形式化表达适用题型一个加数增加几和也增加几(an)b (ab)n规律填空一个加数增加、另一个减少相同数和不变(an)(b-n) ab判断和是否变化减数不变被减数增加几差也增加几(an)-b (a-b)n差的变化规律被减数不变减数增加几差减少几a-(bn) (a-b)-n反向判断被减数和减数同增同减差不变(an)-(bn) a-b快速口算真正兜底的是三个逆运算关系和减第一个加数等于第二个加数被减数减差等于减数差加减数等于被减数。这三条既是解括号题的底层也是后面所有校验断言的依据。把它们写成随机样例测试比写文档靠谱import random def check_invariants(trials1000): 用随机样例把五条规律跑一遍和不变的两条最容易被写反 for _ in range(trials): a random.randint(0, 40) b random.randint(0, a) # 约束 ba一年级不出现负数结果 n random.randint(0, b) # 约束 nb保证 b-n 不出负数 assert (a n) b (a b) n # 规律1 assert (a n) (b - n) a b # 规律2 assert (a n) - b (a - b) n # 规律3 assert a - (b n) (a - b) - n # 规律4 assert (a n) - (b n) a - b # 规律5 return 5 条规律全部通过 print(check_invariants())逻辑说明这三个 assert 在数学上对任意整数都成立但生成器必须按一年级的数域约束b 不能大于 a、n 不能大于 b否则样例里会出现 3-5 这种超纲式子跑出来的题孩子做不了。trials 参数控制样例数量改算法时调到 10000 更容易撞出边界问题。3.2 「括号里最大能填几」从枚举到上界课本 P5 的题型是给出8 ( ) 15这类不等式求括号里最大能填几。原文给的解题步骤是「看符号找大小数 → 确定算式结果 → 根据加减法关系计算」。落到代码上最稳的做法是把括号换成变量 x在数域里枚举取最大值——一年级这个量级下枚举没有性能问题而且不会因为不等式方向写反而悄悄给出错答案。def max_fill(expr: str, lo0, hi99) - int: expr 用 x 代替括号例如 8 x 15返回满足条件的最大整数 env {__builtins__: {}} # 屏蔽内置函数降低表达式注入风险 ok [x for x in range(lo, hi 1) if eval(expr, env, {x: x})] if not ok: raise ValueError(f{expr} 在 {lo}..{hi} 内无解检查符号方向) return max(ok) print(max_fill(8 x 15)) # 6因为 8614 成立而 8715 不成立 print(max_fill(x 12 20)) # 7 print(max_fill(x - 4 9)) # 14逻辑说明lo 和 hi 默认 0 到 99对应百以内的数域一年级题目答案基本落在个位实际传参可以收紧成 0 到 9让不合规的题库更快暴露。eval 的第二个参数把builtins置空第三个参数是变量表表达式里写不出危险调用。真要在生产环境跑常见做法是换成 ast.parse 做节点白名单只放行 Add、Sub、Lt、Gt 几个节点比 eval 干净也能把「8 * x」这类超纲式子直接拦掉。3.3 题目生成与最大性自检答案对还不够还得保证「最大」这两个字站得住填进去的数满足条件再加 1 就不满足。这一步回代断言是能出题和出好题的分水岭。import random def check_maximal(expr: str, ans: int): 验证最大性ans 满足式子ans1 必须不满足 env {__builtins__: {}} assert eval(expr, env, {x: ans}), 答案本身不满足式子 assert not eval(expr, env, {x: ans 1}), 不是最大还能再填 def gen_questions(n5, max_addend9): 生成「一个加数 ( ) 和的上界」题型括号里填一位数 out [] while len(out) n: a random.randint(2, max_addend) # 已知加数 limit random.randint(a 2, 18) # 和的上界留出可填空间 expr f{a} x {limit} ans max_fill(expr, 0, 9) check_maximal(expr, ans) out.append((f{a} ( ) {limit}, ans)) return out for q, a in gen_questions(5): print(q, -, a)逻辑说明limit 的下界取 a2是为了保证括号里至少能填 1不然会出「填不进任何数」的空题上界取 18是为了让答案落在一位数内符合一年级的出题习惯。max_addend 控制已知加数的范围想加难度就调到 20题面直接变成两位数加括号。注意文字题里的「谁比谁多多少用减法、比谁多几用加法」属于语义映射别混进算术函数。语义层单独做一张句式到运算的映射表算术层只收结构化表达式出问题时一眼就能看出是哪层坏了。4. 数位、位值表与进位退位竖式的题型生成与校验数位、计数单位、数的组成这几条看着最简单出题时翻车最多因为全是边界100 是三位数可它只有一个百位0 是最小的一位数但放在两位数的十位上就不是数了相邻整十数碰到 100后面那个就没了。把这些边界先写成函数出题时直接调用比每道题手工判断省事。4.1 数位与计数单位的数据模型规律很清晰从右边起第一位是个位、第二位是十位、第三位是百位个位的计数单位是一、十位的是十、百位的是百56 由 5 个十和 6 个一组成56 里也有 56 个一写数时哪一位一个也没有必须写 0 占位。拆分与合并两个函数要成对出现方便互相验证。def decompose(n: int) - dict: 拆分 0..100 的数位100 的百位是 1、十位个位都是 0 assert 0 n 100, 一年级下册数域到 100 return {百: n // 100, 十: (n // 10) % 10, 个: n % 10} def compose(百0, 十0, 个0) - int: assert 0 十 9 and 0 个 9, 单个数位的值不超过 9 return 百 * 100 十 * 10 个 print(decompose(56)) # {百: 0, 十: 5, 个: 6} print(decompose(100)) # {百: 1, 十: 0, 个: 0} print(compose(十5, 个6)) # 56逻辑说明decompose 里(n // 10) % 10这一步是处理 100 的关键直接写n // 10会得到 10十位的值越界位值表就画错了。compose 的断言故意卡死在 0 到 9防止「7 个一加 5 个一」这类没进位的构造直接拼出 12 个位的脏数据——进位该由第 4.2 节的竖式函数负责不该在组装层偷偷消化掉。4.2 进位加与退位减从凑十法到竖式分步资料给了三条加法分拆路径十加十再加个加个、先加十再加个、先凑整再加剩下的。竖式的要求是相同数位对齐、先个位后十位、个位满 10 向十位进 1、个位不够减从十位退 1 作 10。把中间量吐出来出题时才能给分步提示而不是只丢一个答案。def column_add(a: int, b: int): 两位数竖式加法返回 (结果, 分步说明) steps [] unit a % 10 b % 10 carry unit // 10 # 个位满 10 才有进位 steps.append(f个位 {a%10}{b%10}{unit} (满10向十位进1 if carry else )) tens a // 10 b // 10 carry steps.append(f十位 {a//10}{b//10} (f{carry} if carry else ) f{tens}) return tens * 10 unit % 10, steps def column_sub(a: int, b: int): 两位数竖式减法强调退位 assert a b, 一年级不处理负数结果 steps [] u1, u2 a % 10, b % 10 borrow 1 if u1 u2 else 0 unit u1 10 * borrow - u2 # 退位后个位临时变成 u110 steps.append(f个位 {u1}-{u2} (不够减从十位退1作10 if borrow else ) f{unit}) tens a // 10 - borrow - b // 10 steps.append(f十位减 {b//10}{tens}) return tens * 10 unit, steps print(column_add(38, 25)) # (63, [个位 8513满10向十位进1, 十位 3216]) print(column_sub(32, 5)) # (27, [个位 2-5不够减从十位退1作107, 十位减 02])逻辑说明carry 和 borrow 都取// 10或条件判断得到 0/1不做进位判断的简化是为了让分步文案能直接用。column_sub 的断言 a b 是硬约束退位减在一一年级下册只要求结果非负超过这个范围应该由出题层拦掉而不是让函数返回个负差。三条加法分拆路径必须落到同一个结果这条一致性断言是回归测试里性价比最高的一条def three_ways(a: int, b: int) - int: 3712 的三种分拆路径结果必须一致 w1 (a // 10 b // 10) * 10 (a % 10 b % 10) # 十加十、个加个 w2 a (b // 10) * 10 b % 10 # 先加十、再加个 w3 (a // 10 * 10 b // 10 * 10) (a % 10 b % 10) # 整十先合再加个 assert w1 w2 w3 a b, (w1, w2, w3) return w1 print(three_ways(37, 12), three_ways(38, 25))逻辑说明w1 在 3825 这种进位题上会出现 8513十加十得 50501363和竖式结果一致这条路正好对应「先分拆、再处理进位」的讲法。三条路径断言相等改分拆逻辑时跑一遍就知道有没有把哪条写歪。退位减的凑十法同理32-5 先减到整十数 30再减掉剩下的 3两种写法结果都是 27可以用同样的方式做一致性校验。4.3 相邻数、整十数与百以内比较的边界这一类题的规律都不难难在端点。下面的表把规则和边界一并列出出题生成器直接照着取。题型规则边界样例相邻数前一个减 1后一个加 147 → 46、480 和 100 只有单向相邻整十数个位 1-9 时个位归 0 为前一个前一个加 10 为后一个56 → 50、60相邻整十数本身是整十数减 10、加 1050 → 40、60100 没有后一个回到整十数减去个位上的数87 - 7 80进到整十数个位补到 1087 3 90百以内比大小先比数位再比十位最后比个位99 10045 与 54 看十位def neighbors(n: int): 相邻数0 和 100 只有一侧邻居用 None 占位 return (n - 1 if n 0 else None, n 1 if n 100 else None) def tens_neighbors(n: int): 相邻整十数个位非 0 时向下取整十本身是整十数时 ±10 if n % 10: prev n // 10 * 10 return prev, prev 10 return (n - 10 if n 10 else None, n 10 if n 90 else None) def cmp_within_100(a: int, b: int) - int: 严格按「先数位、后十位、后个位」返回 1 / 0 / -1 if len(str(a)) ! len(str(b)): return 1 if len(str(a)) len(str(b)) else -1 return (a b) - (a b) print(neighbors(47), neighbors(100)) # (46, 48) (99, None) print(tens_neighbors(56), tens_neighbors(100)) # (50, 60) (90, None) print(cmp_within_100(99, 100), cmp_within_100(45, 54)) # -1 -1逻辑说明cmp_within_100 用字符串长度判断数位是为了贴合课本「数位多的这个数就大」的说法直接写 a b 结果一样但讲解时拿不出理由。tens_neighbors(100) 的后一个返回 None是因为百以内没有 110 这个整十数生成器碰到要跳过而不是当成 0否则会出一堆孩子没法答的怪题。5. 时间互化与人民币换算把 24 时计时法写成可测函数时间部分是这份资料里篇幅最长的12 时计时法和 24 时计时法的对照表从凌晨 1 时一路排到晚上 12 时。这张表看着直白写代码时最容易被忽略的是两个端点中午 12 时和晚上 12 时它们既不属于典型的上下午加减 12 又会绕回原点。原文那句「一个钟面可以表示 2 个时间」也得留意——6 时和 18 时共用一个盘面生成题目时不带定语就是歧义题。5.1 12 时与 24 时互化的判定规则原文给的三步是看清哪种计时法区分第一圈还是第二圈12 时变 24 时第一圈去定语、第二圈去定语加 1224 时变 12 时第一圈加定语、第二圈加定语减 12。把定语按圈次归类代码就不需要写一长串 if。12 时计时法定语归类转 24 时凌晨 1-5 时第一圈去掉定语1-5早上 6-7 时、上午 8-11 时第一圈去掉定语6-11中午 12 时端点12下午、傍晚、晚上 1-11 时第二圈加 12晚上 12 时端点24排时间轴时归一成次日 0 时AM_PERIODS (凌晨, 早上, 上午) PM_PERIODS (下午, 傍晚, 晚上) def to24(period: str, hour: int) - int: 12 时计时法转 24 时计时法 assert 1 hour 12, 12 时计时法的数字范围是 1..12 if period 中午 or (period in AM_PERIODS and hour 12): return 12 # 中午 12 时就是 12 时 if period in PM_PERIODS: return 24 if hour 12 else hour 12 # 晚上 12 时按 24 时记 return hour def to12(hour24: int): 24 时计时法转 12 时计时法返回 (定语, 数字) assert 0 hour24 24 if hour24 12: return 中午, 12 if hour24 0: return 晚上, 12 # 0 时对应晚上 12 时 if hour24 12: return 下午, hour24 - 12 return 上午, hour24 print(to24(下午, 1), to24(晚上, 11), to24(晚上, 12)) # 13 23 24 print(to12(16), to12(0)) # (下午, 4) (晚上, 12)逻辑说明断言把小时范围卡死能挡住「下午 13 时」这类混写这类脏数据一旦进库做时间轴时会直接错位。晚上 12 时的处理按原文的「24 时」走下游要做排序或差值时再单独做一次 24 到 0 的归一别在互化函数里偷偷改语义。5.2 时间计算与人民币换算的整数方案时间计算靠三个关系式开始时刻加经过时间等于结束时刻反过来两条是减法。跨天用取模最省事22 时到次日 6 时经过 8 小时直接算差值会得到负数。def elapsed(start: int, end: int) - int: 经过时间取模 24 后天然支持跨天 return (end - start) % 24 def end_time(start: int, hours: int) - int: return (start hours) % 24 print(elapsed(10, 16), elapsed(22, 6), end_time(16, 4)) # 6 8 20人民币部分千万别用浮点数。1 元等于 10 角等于 100 分用「分」做整数单位1.2 元这类值在二进制里存不精确两个价格比大小会得到反直觉的结果。UNIT {元: 100, 角: 10, 分: 1} def to_fen(元0, 角0, 分0) - int: return 元 * UNIT[元] 角 * UNIT[角] 分 def from_fen(fen: int): 还原成 (元, 角, 分)从低位往上取 return fen // 100, fen // 10 % 10, fen % 10 assert to_fen(1) to_fen(角10) to_fen(分100) 100 print(from_fen(to_fen(元1, 角2, 分5))) # (1, 2, 5)断言里那一行连续相等同时验证了三条原文换算关系比写三个独立用例更能说明问题。把这几个断言连同前面的 max_fill、column_add、cmp_within_100 一起丢进 pytest改规则表时先跑一遍比手工翻练习册快得多出题量一上来最先坏掉的往往不是算法而是没人管的边界。本文还有配套的精品资源点击获取