735468682 发表于 2026-1-20 11:42:08

openpyxl问题

import os
from openpyxl import load_workbook

file_path = os.path.join("try01","临考记忆点.xlsx")

# 1.获取workbook对象
work_book_object = load_workbook(file_path)

sheet_object = work_book_object["Sheet1"]


# 获取合并的单元格,如果是被合并的单元格 让值默认等于 -
from openpyxl.cell.cell import Cell,MergedCell
row_text_list = []
for row in sheet_object.rows:

    for cell in row:
      if type(cell) == Cell:
            row_text_list.append(cell.value)
      elif type(cell) == MergedCell:
            row_text_list.append("-")
# print(row_text_list)
      for i in row_text_list:
            if i == "-" or i == None:
                row_text_list.remove(i)
print(row_text_list)

import os
from openpyxl import load_workbook

file_path = os.path.join("try01","临考记忆点.xlsx")

# 1.获取workbook对象
work_book_object = load_workbook(file_path)

sheet_object = work_book_object["Sheet1"]


# 获取合并的单元格,如果是被合并的单元格 让值默认等于 -
from openpyxl.cell.cell import Cell,MergedCell
row_text_list = []
for row in sheet_object.rows:

    for cell in row:
      if type(cell) == Cell:
            row_text_list.append(cell.value)
      elif type(cell) == MergedCell:
            row_text_list.append("-")
# print(row_text_list)
for i in row_text_list:
    if i == "-" or i == None:
      row_text_list.remove(i)
print(row_text_list)
我有一个excel表格,其中有很多合并后的单元格,为什么这两段代码处理后 输出的结果会不一样,第一段代码是我想要的结果,而第二段代码处理的结果前面是我想要的结果从中间开始就感觉没有处理了

FishC 发表于 2026-1-20 11:43:15

你遇到的问题是因为在迭代列表的同时修改列表内容导致的。让我们分析一下两段代码的区别:

第一段代码(正确):

for i in row_text_list:
    if i == "-" or i == None:
      row_text_list.remove(i)


第二段代码(有问题):

      for i in row_text_list:
            if i == "-" or i == None:
                row_text_list.remove(i)


关键区别:

第二段代码的 for i in row_text_list: 循环缩进错误,它被放在了 for cell in row: 循环内部,这意味着:
1. 每处理一个单元格,就会对整个 row_text_list 进行一次遍历和删除操作
2. 这会导致循环逻辑混乱,容易跳过某些元素

问题原因:

在遍历列表的同时删除元素会导致问题,因为 Python 的迭代器会"丢失"一些元素。例如:
- 列表:
- 删除第一个 "-" 后:
- 但迭代器已经移动到下一个位置,会跳过 2

解决方案:

推荐使用列表推导式来过滤,这样更清晰且不会出错:


import os
from openpyxl import load_workbook
from openpyxl.cell.cell import Cell, MergedCell

file_path = os.path.join("try01", "临考记忆点.xlsx")
work_book_object = load_workbook(file_path)
sheet_object = work_book_object.active# 注意:这里应该是获取具体的工作表

row_text_list = []
for row in sheet_object.rows:
    for cell in row:
      if isinstance(cell, Cell):
            row_text_list.append(cell.value)
      elif isinstance(cell, MergedCell):
            row_text_list.append("-")

# 使用列表推导式过滤
row_text_list =
print(row_text_list)


注意事项:

1. sheet_object = work_book_object 应该是 sheet_object = work_book_object.active 或其他具体的工作表
2. 使用 isinstance(cell, Cell) 比 type(cell) == Cell 更规范
3. 推荐使用列表推导式而不是在迭代中删除元素

如果你的代码确实需要按照原来的逻辑,可以这样修复第二段代码:


# 创建一个临时列表来存储要删除的索引
to_remove = []
for index, i in enumerate(row_text_list):
    if i == "-" or i is None:
      to_remove.append(index)

# 从后往前删除,避免索引错位
for index in reversed(to_remove):
    del row_text_list


以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

735468682 发表于 2026-1-20 12:00:28


for i in row_text_list:
    if i == "-" or i == None:
      row_text_list.remove(i)
这样写的代码,得到却跟用推导式得到的结果不一样

FishC 发表于 2026-1-20 12:01:15

735468682 发表于 2026-01-20 12:00
for i in row_text_list:
    if i == "-" or i == N ...

你的观察是对的,问题在于在循环中修改正在迭代的列表会导致元素被跳过。让我详细解释一下:

假设 row_text_list =

使用 for i in row_text_list: 遍历:
1. 第一次:i = "-",满足条件,删除 "-" → 列表变为
2. 第二次:i = "A",不满足条件
3. 第三次:i = "B",不满足条件
4. 第四次:i = "-",满足条件,删除 "-" → 列表变为
5. 列表结束

结果是 ,而推导式的结果是

看,被跳过了!这就是问题所在。

为什么会这样?
当你在循环中删除元素时,列表索引会发生变化,但循环的迭代器仍按原来的索引前进。

正确的解决方案:
1. 使用列表推导式(最推荐):

row_text_list =


2. 如果要保持 for 循环,可以从后往前删除:

for i in range(len(row_text_list)-1, -1, -1):
    if row_text_list == "-" or row_text_list is None:
      del row_text_list


3. 或者创建新列表:

new_list = []
for i in row_text_list:
    if i != "-" and i is not None:
      new_list.append(i)
row_text_list = new_list


推荐使用列表推导式,因为:
1. 代码更简洁
2. 没有修改迭代中的列表,不会出错
3. 性能通常更好

注意:判断 None 应该用 i is None 而不是 i == None,虽然两者通常结果一样,但 is None 更规范。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

735468682 发表于 2026-1-20 12:07:09

还是有点没懂   
for i in row_text_list:
    if i == "-" or i == None:
      row_text_list.remove(i)

我的意思是这样写得到的结果并不是我想要的

      for i in row_text_list:
            if i == "-" or i == None:
                row_text_list.remove(i)

反而这样写,才得到我想要的结果且与推导式结果一致

FishC 发表于 2026-1-20 12:08:00

735468682 发表于 2026-01-20 12:07
还是有点没懂   
for i in row_text_list:
    if i ...

你观察到的现象完全正确!让我用一个简单的例子来演示这个问题:

假设 row_text_list =

执行这个循环:


for i in row_text_list:
    print(f"检查: {i}")
    if i == "-" or i == None:
      print(f"删除: {i}")
      row_text_list.remove(i)


实际执行过程是这样的:
1. 检查 "-" → 删除 "-" → 列表变为
2. 检查 "B" (不是 "-" 或 None) → 列表不变
3. 检查 "-" → 删除 "-" → 列表变为
4. 检查 "D" (不是 "-" 或 None) → 列表不变
5. 结束

结果:

而推导式: 的结果是

这就是为什么你用 for 循环删除得到的结果不正确。

但如果是这样写的代码:


      for i in row_text_list:
            if i == "-" or i == None:
                row_text_list.remove(i)


注意:这里的关键是 缩进!如果这个循环是在另一个循环内部,每处理一个单元格就执行一次,情况就会不同:

假设 row_text_list 初始为空,逐步添加元素:

第一次添加:"-"
执行循环:删除 "-" → 列表为空

第二次添加:"A"
执行循环:不删除 → 列表为

第三次添加:"B"
执行循环:不删除 → 列表为

第四次添加:"-"
执行循环:删除 "-" → 列表为

这样最终结果可能正确,但这是巧合!

核心问题:
1. 在遍历列表的同时修改列表会导致元素被跳过
2. 推导式创建的是新列表,不会修改正在遍历的列表

建议:
始终使用推导式来过滤列表,这是最可靠的方法。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

735468682 发表于 2026-1-20 12:12:32

还有 推导式中 not in 是什么意思

FishC 发表于 2026-1-20 12:13:04

735468682 发表于 2026-01-20 12:12
还有 推导式中 not in 是什么意思

在推导式中,not in 是一个成员运算符,用来检查某个元素不在指定的序列中。让我用例子来解释:

1. 基本语法:

not in 返回布尔值(True 或 False):
元素 not in 序列 如果元素不在序列中,返回 True

2. 简单例子:


# 检查元素是否不在列表中
fruits =

print("apple" not in fruits)# 输出: False(因为 apple 在列表中)
print("banana" not in fruits)# 输出: True(因为 banana 不在列表中)


3. 在推导式中使用 not in:


# 原始数据
numbers =

# 筛选出不在 中的数字
result =
print(result)# 输出:


4. 应用到你的案例:

对于你的需求(排除 "-" 和 None),可以这样写:

方法1:使用 and(推荐)

row_text_list =


方法2:使用 not in

exclude =
row_text_list =


5. 注意事项:
1. None 的判断最好用 is None 或 is not None,这样更准确
2. 如果要用 not in 判断 None,需要确保序列中包含 None
3. not in 的优先级低于 and 或 or

建议:对于你的具体需求,使用第一种方法(i != "-" and i is not None)更清晰易懂。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

735468682 发表于 2026-1-20 12:18:37

FishC 发表于 2026-1-20 12:08
你观察到的现象完全正确!让我用一个简单的例子来演示这个问题:

假设 row_text_list =


那如果我用循环的话,最好是新建列表,重新添加非“-”和None的元素,才能确保正确?
new_list = []
for i in row_text_list:
    if i != "-" or i is not None:
      new_list.append(i)
print(new_list)

FishC 发表于 2026-1-20 12:19:07

735468682 发表于 2026-01-20 12:18
FishC 发表于 2026-1-20 12:08
你观察到的现象完全正 ...

是的,用循环创建新列表是最安全的方法。但是你的代码逻辑需要修正一下。

你的代码:
new_list = []
for i in row_text_list:
    if i != "-" or i is not None:
      new_list.append(i)
print(new_list)

这个逻辑有问题!用 or 连接会导致:
- 如果 i 是 "-":i != "-" 为 False,但 i is not None 为 True,结果 True → 会被添加(错误!)
- 如果 i 是 None:i != "-" 为 True,但 i is not None 为 False,结果 True → 会被添加(错误!)

正确的应该是用 and:

new_list = []
for i in row_text_list:
    if i != "-" and i is not None:
      new_list.append(i)
print(new_list)

或者更简洁的写法:

new_list = []
for i in row_text_list:
    # 只有当元素不是 "-" 且不是 None 时才添加
    if i not in ("-", None):
      new_list.append(i)
print(new_list)

这个方法的优点是:
1. 不会修改正在遍历的列表,所以不会出现跳过的错误
2. 逻辑清晰,易于理解
3. 结果与推导式完全一致

另一种思考方式是反过来写:

new_list = []
for i in row_text_list:
    # 如果元素是 "-" 或 None,就跳过
    if i == "-" or i is None:
      continue# 跳过本次循环,不添加到新列表
    new_list.append(i)
print(new_list)

总结:用循环新建列表时,关键是:
1. 不要修改正在遍历的列表
2. 正确使用逻辑运算符(and 而不是 or)
3. 可以结合 continue 语句使逻辑更清晰

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

735468682 发表于 2026-1-20 16:57:58

import os
from openpyxl import load_workbook

file_path = os.path.join("try01","临考记忆点.xlsx")

# 1.获取workbook对象
work_book_object = load_workbook(file_path)

sheet_object = work_book_object["Sheet1"]


# 获取合并的单元格,如果是被合并的单元格 让值默认等于 -
from openpyxl.cell.cell import Cell,MergedCell
row_text_list = []
for row in sheet_object.rows:

    for cell in row:
      if type(cell) == Cell and cell.value is not None:
            row_text_list.append(cell.value)
print(row_text_list):

如果我只想得到不含"None"和“-”的列表直接这样写是不是连推导式都可以不需要了

FishC 发表于 2026-1-20 16:59:26

735468682 发表于 2026-01-20 16:57
import os
from openpyxl import load_workbook

f ...

是的,完全正确!你现在的代码已经实现了相同的过滤效果。

让我们分析一下你修改后的代码:


import os
from openpyxl import load_workbook

file_path = os.path.join("try01","临考记忆点.xlsx")

# 1.获取workbook对象
work_book_object = load_workbook(file_path)

sheet_object = work_book_object

# 获取合并的单元格,如果是被合并的单元格 让值默认等于 -
from openpyxl.cell.cell import Cell,MergedCell
row_text_list =
for row in sheet_object.rows:

    for cell in row:
      if type(cell) == Cell and cell.value is not None:
            row_text_list.append(cell.value)
print(row_text_list)


这个代码的优点:
1. 在添加的时候就进行过滤:只添加类型为 Cell 且值不为 None 的元素
2. 直接跳过了合并单元格(MergedCell 类型)
3. 不需要后续的推导式或循环再过滤

这与推导式的效果是等价的。这两种方式你会得到相同的结果:

推导式方法:

row_text_list =
row_text_list =
print(row_text_list)


你的方法:

row_text_list =
for cell in sheet_object.rows中的所有cell:
    if 是普通单元格且值不为None:
      row_text_list.append(cell.value)
print(row_text_list)


但是要注意几个小细节:

1. sheet_object = work_book_object 应该是 sheet_object = work_book_object.active(或指定具体的工作表)

2. 对于合并单元格,你现在的代码完全跳过了它们,包括合并区域左上角的那个有值的单元格也会被跳过,因为它虽然是 Cell 类型,但如果你不检查它是合并单元格的一部分,可能会丢失数据。

更完整的写法应该是:

from openpyxl.cell.cell import Cell, MergedCell

row_text_list =
for row in sheet_object.rows:
    for cell in row:
      # 如果是普通单元格且有值
      if isinstance(cell, Cell) and not isinstance(cell, MergedCell) and cell.value is not None:
            row_text_list.append(cell.value)
      # 如果是合并单元格且是左上角的单元格(有值)
      elif isinstance(cell, Cell) and cell.value is not None:
            # 这里需要额外检查是否为合并单元格的左上角
            # 或者直接添加
            row_text_list.append(cell.value)


不过对于你的需求(不含None和"-"),你的代码已经满足了!推导式确实可以不需要了。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

735468682 发表于 2026-1-20 17:10:47

isinstance(cell, Cell) 这是什么意思和用法?
对于合并单元格,你现在的代码完全跳过了它们。是因为我没有判断被合并的单元格是否是Mergedcell类型吗?

735468682 发表于 2026-1-20 17:12:54

还有 我用`start_cell.value`时有问题

FishC 发表于 2026-1-20 17:13:55

735468682 发表于 2026-01-20 17:10
isinstance(cell, Cell) 这是什么意思和用法?
对于 ...

是的,`isinstance(cell, Cell)` 是检查一个对象是否属于某个类的实例的常用方法。让我详细解释:

1. isinstance() 函数:

`isinstance(object, class)` 用来检查一个对象是否是某个类(或其子类)的实例。

用法:
isinstance(obj, ClassName)
- 如果 obj 是 ClassName 类或其子类的实例,返回 True
- 否则返回 False

例子:
from openpyxl.cell.cell import Cell, MergedCell

cell1 = Cell(sheet, column=1, row=1)
cell2 = MergedCell(sheet, column=1, row=1)

print(isinstance(cell1, Cell))# 输出: True
print(isinstance(cell2, Cell))# 输出: True(因为 MergedCell 继承自 Cell)
print(isinstance(cell2, MergedCell))# 输出: True

2. type() 与 isinstance() 的区别:

print(type(cell1) == Cell)# 输出: True
print(type(cell2) == Cell)# 输出: False(因为 cell2 是 MergedCell 类型)
print(isinstance(cell2, Cell))# 输出: True

使用 `type(cell) == Cell` 严格检查类型,而 `isinstance(cell, Cell)` 更宽松,会检查继承关系。

3. 关于合并单元格:

你说得对!你的代码 `if type(cell) == Cell and cell.value is not None:` 会完全跳过合并单元格,因为:
- 合并区域的左上角单元格:类型是 Cell,有值
- 合并区域的其他单元格:类型是 MergedCell,值通常是 None

让我们看看正确的处理方式:

from openpyxl.cell.cell import Cell, MergedCell

row_text_list = []
for row in sheet_object.rows:
    for cell in row:
      # 检查是否为合并单元格(但非左上角)
      if isinstance(cell, MergedCell):
            # 这是合并单元格的一部分,但不是左上角
            continue# 跳过它
      elif isinstance(cell, Cell) and cell.value is not None:
            # 这是普通单元格或合并单元格的左上角
            row_text_list.append(cell.value)

但是,这样写还是有一个问题:我们如何判断一个 Cell 是否是合并单元格的左上角?

更好的写法是使用 openpyxl 的 merged_cells 属性:

row_text_list = []
merged_ranges = sheet_object.merged_cells.ranges# 获取所有合并范围

for row in sheet_object.rows:
    for cell in row:
      # 检查单元格是否在合并范围内
      is_in_merged = False
      for merged_range in merged_ranges:
            if cell.coordinate in merged_range:
                # 如果是合并范围左上角,保留值;否则跳过
                if cell.coordinate == merged_range.min_row:
                  # 是左上角,保留值
                  pass
                else:
                  # 不是左上角,跳过
                  is_in_merged = True
                  break
      
      if is_in_merged:
            continue
      
      # 添加非None的值
      if cell.value is not None:
            row_text_list.append(cell.value)

不过,对于你的需求(不含None和"-"),如果你只想保留合并区域左上角的值,最简单的方法是:

row_text_list = []
for row in sheet_object.rows:
    for cell in row:
      # 跳过值为None的单元格
      if cell.value is None:
            continue
      
      # 如果单元格在合并范围内但不是左上角,跳过
      # 这里需要检查合并范围
      is_top_left = True
      for merged_range in sheet_object.merged_cells:
            if cell.coordinate in merged_range:
                # 检查是否为左上角
                top_left_cell = f"{merged_range.min_column}{merged_range.min_row}"
                if cell.coordinate != top_left_cell:
                  is_top_left = False
                  break
      
      if is_top_left:
            row_text_list.append(cell.value)

最简单的方法其实是利用 openpyxl 的特性:只有合并区域左上角的单元格有值,其他合并单元格的值都是 None。所以你可以直接:

row_text_list = []
for row in sheet_object.rows:
    for cell in row:
      if cell.value is not None:
            row_text_list.append(cell.value)

这样就能获取所有有值的单元格,包括合并区域的左上角单元格。

总结:你最初的代码已经可以满足需求,因为合并区域中只有左上角单元格有值,其他单元格的值都是 None。所以用 `if cell.value is not None` 就能过滤掉所有空值。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: openpyxl问题