尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python列表在库存台账中的应用:从数据清洗到批量排序

Python列表在库存台账中的应用:从数据清洗到批量排序 月底对库存最怕看到“数量乱、改不动”这六个字。我见过不少做库管、采购、运营的朋友Excel用得很溜可一核对台账就开始头大同一款商品的规格和数量挤在一个单元格里、进货单和出库单里的名称对不上、想批量把“件”改成“箱”只能一格格拖鼠标。说到底这类活不是靠手工勤快能解决的关键是得有一套能快速定位、批量修改、自动核对的数据处理思路。Python列表就是上手最快的那块敲门砖。它不需要你懂多高深的理论能创建、能切片、能修改、能排序库存台账里的“数量乱”和“改不动”基本都能收拾利索。这篇我就用库存核对这个场景把Python列表的核心操作完整过一遍适合零基础新手跟着敲也适合已经会一点但没落地用过的人把它当作复习清单。1. 先把两座“大山”拆开看库存实战里到底难在哪1.1 “数量乱”通常乱在这些地方很多人觉得“数量乱”就是数字对不上实际接触过台账的人才知道乱法五花八门。第一种乱是列结构不统一。同一个Excel文件里第一张表的数量在第C列第二张表的数量却在第E列还有一张表把“数量*单价”直接写在一个单元格里。这时候你就算用Excel的求和公式也得先手工对齐半天。第二种乱是数据格式很脏。数量栏里有“50件”“30箱”这样的带单位文本有前后空格有全角数字和半角数字混在一起甚至还有“约20”“100”这种根本没法直接参与计算的内容。Python列表拿到这些数据之后第一件事就是清洗清洗完了才能谈核对。第三种乱是顺序不对。品名没有按类别排序日期乱跳同一个商品在不同行反复出现想找某一条数据只能肉眼扫。这时候就轮到列表的排序能力上场了。1.2 “改不动”的本质不是懒是方案不对“改不动”听起来像吐槽其实就是三个字太低效。常见的有这么几种情况一是只在界面里手动改。几百行的商品单价要统一上调0.5元你如果一个个双击单元格去改不仅慢还容易漏改或改错行。二是数据散落在多个小表格里改完A表忘了改B表最后汇总的时候两边对不上。三是不敢批量改怕公式被覆盖、怕格式乱掉、怕改错之后不知道回退到哪里。这些问题的本质其实是你缺一个“在内存里安全操作数据”的思路。Python列表就是把数据从Excel表格里“搬”到一个可以随意操作的结构里你在里面改坏了可以重来改对了再导出覆盖原文件。这样做的好处是操作可重复、结果可复现、中间每一步都能打印出来检查。1.3 为什么先学列表而不是直接上Pandas有些人一听到“用Python处理表格”第一反应就是Pandas这没问题Pandas确实是处理表格数据的利器。但我的建议是新手尤其是非程序员背景的人先别急着上Pandas先用最原生的Python列表把逻辑捋顺了再说。原因是列表的数据结构更直观。一个列表就是一组有序的数据像货架上一排货你从头到尾能看到、能数、能改。Pandas的DataFrame虽然强大但它同时涉及行索引、列索引、Series、DataFrame这些概念一上来容易把人绕晕。另外列表还是Pandas的基础。你后面学的索引、切片、排序、推导式到Pandas里虽然换了写法但思路完全一样。把列表的基本功练扎实了后面接触Pandas、NumPy几乎是丝滑过渡。2. Python列表的核心操作用台账语言讲一遍2.1 列表是一排有顺序的货架创建和访问列表的创建很简单用方括号[]把数据包起来每一项用英文逗号隔开。比如你有一个库存表头和一列商品名head_row [商品ID, 品名, 数量, 单价] products [苹果, 香蕉, 橙子, 葡萄]每个元素都有自己的位置这个位置叫索引index注意索引从0开始。也就是说products[0]取到的是“苹果”products[3]取到的是“葡萄”。很多新手在这栽跟头以为第一个是products[1]结果取出来的全是第二个元素。列表里可以装任意类型的元素甚至可以装子列表。比如你想用一个变量存整张库存表就可以用嵌套列表stock [ [A001, 苹果, 50, 5.5], [A002, 香蕉, 30, 3.2], [A003, 橙子, 20, 4.0], ]这种结构特别像Excel里的表格每一行是一个子列表整张表是一个外层大列表。后面排序、修改、切片都是在这个“大货架”上操作。2.2 列表切片批量取数据的“裁纸刀”切片是列表最重要的操作之一作用是从列表里取出一段连续的数据。写法是列表[起点索引:终点索引]但这里有个关键规则起点包含终点不包含。比如你有一个商品列表想取前三个商品products [苹果, 香蕉, 橙子, 葡萄, 西瓜] print(products[0:3]) # 输出[苹果, 香蕉, 橙子]这里[0:3]取了索引0、1、2三个位置索引3的“葡萄”没被包含。很多新手写[0:5]想取前五个结果发现取到了索引4就没多想其实这个规则是统一的[起始:结束]表示从起始到结束之前。切片还有其他常见写法。products[:3]表示从头取到索引3之前等价于[0:3]products[2:]表示从索引2一直取到末尾products[:]表示复制整个列表。这在处理台账时很常用比如你有一张带表头的表想跳过表头只取数据行直接data[1:]就搞定了。2.3 改列表元素从“改不动”到“一键改完”列表最有价值的地方在于它是可变的你可以直接修改某个位置的元素也可以批量修改。先看单个修改。比如商品“苹果”的价格从5.5改成6.0stock[0][3] 6.0这里的逻辑是外层stock[0]取出第一行[A001, 苹果, 50, 5.5]然后[3]取出这行的第4个元素单价直接赋新值。改完之后整个外层列表就变了。批量修改就需要用循环。把库存里所有单价超过5元的商品都打上标记“高价”for row in stock: if row[3] 5: row.append(高价)这段代码遍历每一行检查单价如果满足条件就在这一行末尾追加一个标记。追加用append()方法它会把新元素添加到列表末尾。除了append()还有insert()可以在指定位置插入、remove()按值删除、pop()按索引删除。这些方法组合起来基本上手工能干的修改操作代码里都能干而且能一次性处理几百行不喊累。2.4 列表推导式一行代码筛出异常数据列表推导式是Python里非常惊艳的语法它能把“创建新列表条件筛选”压缩成一行代码。新手可能觉得它看起来吓人但其实只要你掌握“表达式循环条件”的结构就很容易理解。比如你想找出库存中所有数量小于20的品名low_stock [row[1] for row in stock if row[2] 20] print(low_stock) # 输出[橙子]这一行代码等价于下面这种写法low_stock [] for row in stock: if row[2] 20: low_stock.append(row[1])列表推导式的好处是简洁、可读性高而且运行速度比普通的for循环要快一些。在处理库存这类重复性很强的数据时用它做筛选、提取、转换都非常顺手。我建议新手不要跳过这个知识点它值得花半小时练熟。3. 实战10分钟完成一次库存台账的全流程核对3.1 第一步把Excel/CSV里的台账搬进Python在实际工作里你的数据大概率躺在Excel文件里。Python标准库里的csv模块可以直接读取CSV格式而Excel另存为CSV也就几秒钟的事。我一般建议先把Excel另存为CSV再用csv模块读进来这样不需要额外安装第三方库。假设你有一份库存表结构是商品ID品名数量带单位单价。读取的代码如下import csv with open(stock.csv, encodingutf-8) as f: reader csv.reader(f) data list(reader) print(data[:3])执行之后你会得到一个嵌套列表第一行是表头后面是数据。打印前3行是为了确认数据是否正确读取。这一步是后面所有操作的基础如果打印出来的内容不对后面的清洗和修改全都会受影响。如果你手里的文件是Excel的.xlsx格式那就需要先另存为CSV或者在确认环境允许的情况下用openpyxl库读取。不过对新手来说先用CSV把流程跑通最重要。3.2 第二步清洗“数量乱”的脏数据读取进来的数据有个问题从CSV读出来的每一格都是字符串而且数量列里还带着“件”“箱”这样的单位。直接拿来算是不行的必须清洗。先看数据长什么样data [ [商品ID, 品名, 数量, 单价], [A001, 苹果, 50件, 5.5], [A002, 香蕉, 30箱, 3.2], [A003, 橙子, 约20件, 4.0], [A004, 葡萄, 10, 6.5], ]清洗的目的是把“数量”这一列变成纯数字。我一般这么处理跳过表头对每一行做四步操作——去掉首尾空格、去掉单位、判断是否为有效内容、转换成整数或浮点数。def clean_qty(text): text text.strip() text text.replace(件, ).replace(箱, ).replace(个, ) if text.startswith(约): text text[1:] return int(text) for row in data[1:]: row[2] clean_qty(row[2]) row[3] float(row[3])这段代码把“50件”变成50“约20件”变成20无法转换的数据会直接报错提醒你回去检查原始表。清洗完之后再打印一遍print(data) # 输出 # [[商品ID, 品名, 数量, 单价], # [A001, 苹果, 50, 5.5], # [A002, 香蕉, 30, 3.2], # [A003, 橙子, 20, 4.0], # [A004, 葡萄, 10, 6.5]]到这一步“数量乱”的问题基本解决了。数字不再带单位类型也对后续可以计算、可以排序。3.3 第三步批量修改——统一单位、修正价格、更新批次清洗完之后紧接着就是“改”。这一步我特别建议大家先做一个动作复制一份原始列表再进行修改防止改完发现不对却回不去。复制列表用切片就能实现backup data[:]如果你想把库存表中的所有单价都上调10%可以这样for row in data[1:]: row[3] round(row[3] * 1.1, 2)如果你想把A002这个ID对应的商品数量改成40先找到它所在的位置再改for row in data[1:]: if row[0] A002: row[2] 40批量修改的逻辑就是循环遍历每一行用条件判断定位目标行然后直接给对应位置赋新值。这个思路和Excel里的“查找替换”很像但适用范围广得多你可以根据ID、品名、价格区间、数量范围任意条件来定位修改后的结果也统一、可验证。3.4 第四步排序——一级二级三级列表排序一次说清排序是台账核对里特别高频的操作。你可能希望按数量从大到小排或者按品类再按数量双字段排。Python里最简单的方式是sorted()函数和列表的sort()方法。先说单字段排序。按数量从高到低排sorted_data sorted(data[1:], keylambda row: row[2], reverseTrue)sorted()返回一个新的排序后的列表不影响原列表。key参数指定排序依据lambda row: row[2]的意思是“取这一行的第3个元素数量作为排序依据”reverseTrue表示降序。如果你想两级排序先按品类再按数量sorted_data sorted(data[1:], keylambda row: (row[1], row[2]))这里key返回的是一个元组Python会先按第一个元素排等同时再按第二个元素排。这个写法在处理带分类的数据时非常实用。现在讲热搜词里提到的“依据三级列表元素排序一级列表”。乍一听很玄其实就是嵌套列表的层层取值。举个例子你有这样的数据第一层是仓库第二层是仓库里的商品列表第三层是商品的“数量”字段。你想按每个仓库中第一个商品的数量来给仓库排序stock_by_warehouse [ [上海仓, [[苹果, 30, 5.5], [香蕉, 12, 3.2]]], [北京仓, [[苹果, 20, 5.8], [香蕉, 25, 3.0]]], [广州仓, [[苹果, 40, 5.2], [香蕉, 18, 3.5]]], ] sorted_warehouses sorted( stock_by_warehouse, keylambda x: x[1][0][1] )我们来拆解这个lambda x: x[1][0][1]。x代表外层列表里的一个仓库x[1]取的是该仓库下的商品子列表x[0]取子列表里的第一个商品也就是苹果最后的[1]取苹果的数量。合起来就是“取出每个仓苹果的数量作为排序依据”。sorted之后外层的一级列表就会按三级元素苹果数量重新排列。这个例子想表达的核心是嵌套列表的排序本质是搞清每一层索引到底指向什么。你只要把层级关系捋清楚代码写起来并不复杂。3.5 第五步核对汇总——算差异、找负数、出结果最后一步是核对汇总。常见的需求有三个对比两组数据找差异、找出库存为负数的商品、计算总库存金额。先说找差异。比如你有两个列表一个是系统账面数一个是盘点实际数都是[品名, 数量]的结构system_data [[苹果, 50], [香蕉, 30], [橙子, 20]] actual_data [[苹果, 48], [香蕉, 30], [橙子, 22]] for sys_row, act_row in zip(system_data, actual_data): diff act_row[1] - sys_row[1] if diff ! 0: print(f{sys_row[0]} 差异{diff})zip()函数会把两个列表里位置对应的行配对起来非常方便。如果两个列表的顺序不一致那就先各自按品名排序再用zip对比这也是排序的一个典型应用场景。找负库存更简单negative [row for row in data[1:] if row[2] 0] if negative: print(发现负库存, negative) else: print(没有负库存数量全部正常)算库存总额就是遍历累加total_value 0 for row in data[1:]: total_value row[2] * row[3] print(f库存总金额{total_value:.2f}元)到这一步一次完整台账核对的流程就跑通了读取、清洗、修改、排序、核对、汇总。你说它能不能10分钟做完如果数据规模不大、清洗逻辑不复杂熟练之后确实可以。4. 新手最容易踩的坑我都替大家摔过了4.1 索引从0开始还有那个“含头不含尾”的切片索引从0开始这个事几乎所有新手都要错一次。我到现在写Python偶尔还会顺手把products[1]当成第一个元素好在我每次都习惯先打印看一下。另外一个高频坑是切片“含头不含尾”。比如你想取前三条数据写成data[0:3]是对的但如果你写成data[0:4]就会多取一行。这里有个小技巧当你拿不准切片范围时先取一个小范围打印出来确认边界再放大别嫌麻烦出错了更麻烦。4.2 嵌套列表复制时整个列表“一起变”的坑前面我提到用data[:]复制列表这里有个隐藏坑如果列表里的元素是子列表浅拷贝只会复制外层列表里面的子列表仍然是同一个对象。也就是说你改了备份里的某个子列表原列表也会跟着变。a [[1, 2], [3, 4]] b a[:] b[0][0] 99 print(a) # 输出[[99, 2], [3, 4]]要想完全独立复制可以用copy模块的deepcopyimport copy b copy.deepcopy(a)在库存这种场景里数据行都是子列表所以备份时我建议直接用deepcopy。虽然性能稍微慢一点但安全性高很多尤其是你要大规模修改数据的时候多花零点几秒换一个回滚机会值。4.3 排序key参数用不对数字按字典序排这是排序里最隐蔽的坑。如果列表里的数字还是字符串类型你直接排序Python会按字典序排结果就会出现“10、100、11、2、20”这种看起来很离谱的顺序但它认为是正常的。我自己遇到过这样的情况从CSV读进来的数据全是字符串没做类型转换就排序结果“100件”排到了“20件”前面差点把盘点结果搞错。解决办法就是在排序之前先把数据转换成数字类型或者排序时用keylambda x: int(x[2])临时转换。4.4 修改列表时重新赋值和原地修改的区别新手经常分不清sorted()和sort()的区别。我简单总结一下sort()是原地修改列表原列表的顺序会变sorted()是返回一个新列表原列表不动。这个区别在备份时尤其重要。如果你还想保留原始顺序就一定要用sorted()或者先copy.deepcopy一份再原地排序。否则你后面想再看原始录入顺序的数据会发现已经找不回来了。4.5 常见问题速查表症状原因解决办法取第一个元素却取到第二个忘记索引从0开始用list[0]取第一个元素多打印验证切片取不到最后一个元素忽略了“含头不含尾”规则结束索引加1或使用list[3:]取到末尾修改备份列表原列表也变了浅拷贝共享了内层子列表使用copy.deepcopy()数字排序变成10、100、2、20元素还是字符串按字典序排序先转int()或用keyint排序后原列表顺序丢了用了sort()原地排序改用sorted()或提前备份CSV读进来全是字符串csv模块默认不转换类型用自定义函数逐列转换这张表我建议新手贴在笔记本上遇到问题先翻一下。4.6 核验结果的小习惯最后分享一个我自己的习惯每次做完批量操作在关键步骤后都加一行print()输出结果。比如清洗完打印前5行改完价格打印改动前后对比排序完打印第一行和最后一行确认边界。很多人嫌麻烦跳过这一步结果出了问题还要反过来一步步查反而更费时间。你可以把打印放在一个“检查函数”里随时调用def show_table(data, n3): for row in data[:n]: print(row) show_table(data) show_table(sorted_data)这个小函数不复杂但在核对台账时特别好用。它让每一步操作都变得透明可见你永远都知道当前数据处于什么状态心里有底了操作起来也就不慌了。写在最后我建议新手从这个小习惯开始如果你现在是零基础别急着把列表的所有方法都背下来那是没有意义的。我建议你先拿一份真实的库存表从读取开始做一次完整的清洗、修改、排序、汇总流程哪怕只处理几十行数据也比干看十篇教程有效。别怕出错出错是正常的。刚开始我用列表处理数据时三天两头被“IndexError”“TypeError”这些报错折磨后来发现问题基本都集中在索引、类型和切片这三个点上。等你把这些坑都踩过一遍再回头看数据乱、改不动这种问题心态会完全不一样那都不是事儿代码里几分钟就能搞定。如果你已经掌握列表的基本操作下一步可以试试把清洗逻辑写成函数存成脚本以后每个月底盘点时直接运行一遍整个过程不到几分钟而且每次操作都可复现、可追溯。这才是Python列表带给台账管理的真正价值不是帮你手动算一次数而是帮你把整套处理流程固化成可重复执行的工具。
返回列表