You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python如何遍历嵌套的ContractorData层级结构并检查状态?

可行的遍历方案

1. 修复递归遍历函数

你的递归雏形存在逻辑问题:sub.subs是子承包商的列表,不能直接传入walk函数,需要遍历每个子项递归调用:

def walk_contractor(contractor):
    # 处理当前承包商的逻辑
    if contractor.status != 'Enrolled':
        # 执行你的操作,比如标记、触发流程或打印日志
        print(f"处理非Enrolled承包商:{contractor.contractorName}(状态:{contractor.status})")
    
    # 递归遍历所有子承包商
    for sub in contractor.subs:
        walk_contractor(sub)

调用方式:直接传入根节点 walk_contractor(general_contractor)

2. 迭代式遍历(避免递归深度限制)

如果承包商层级极深,递归可能触发RecursionError,用迭代实现的深度/广度优先遍历更稳妥:

深度优先遍历(迭代版)

def dfs_walk(root):
    stack = [root]
    while stack:
        contractor = stack.pop()
        # 处理当前承包商
        if contractor.status != 'Enrolled':
            print(f"处理非Enrolled承包商:{contractor.contractorName}(状态:{contractor.status})")
        # 子项倒序入栈,保证遍历顺序和递归一致
        for sub in reversed(contractor.subs):
            stack.append(sub)

广度优先遍历(按层级顺序遍历)

def bfs_walk(root):
    queue = [root]
    while queue:
        contractor = queue.pop(0)
        # 处理当前承包商
        if contractor.status != 'Enrolled':
            print(f"处理非Enrolled承包商:{contractor.contractorName}(状态:{contractor.status})")
        # 子项入队
        queue.extend(contractor.subs)

架构优化建议

1. 优化ContractorData初始化逻辑

当前代码重复调用soup.find('a', id=True),缓存重复查找的元素可减少IO开销:

class ContractorData():
    def __init__(self, soup:BeautifulSoup, parentId=None):
        # 缓存重复查找的元素
        name_link = soup.find('a', id=True)
        self.contractorName = name_link.getText()
        self.id = int(name_link.get('href').split('&')[-1].split('=')[1])
        
        status_options = ['Enrolled', 'Excluded', 'Pending']
        self.status = 'Unknown'
        for i in range(3):
            cls = f'contractorStatusCol{i+1}'
            status_div = soup.find('div', class_=cls)
            if not status_div:
                continue
            status_img = status_div.find('img')
            if status_img and 'gray' not in status_img.get('src'):
                self.status = status_options[i]
                break
        
        date_col = soup.find('div', class_='contractorStatusCol4')
        self.date_enrolled = date_col.get_text().replace(u'\xa0', '') if date_col else ''
        
        self.has_subs = soup.find('a', class_="expand") is not None
        self.subs = []
        self.parent = parentId

2. 给ContractorData内置遍历能力

把遍历逻辑封装到类中,符合面向对象设计,调用更直观:

class ContractorData():
    # ... 保留原有初始化代码 ...
    
    def walk(self, handler):
        """遍历当前及所有子承包商,对每个实例执行handler回调"""
        handler(self)
        for sub in self.subs:
            sub.walk(handler)
    
    def iter_all(self):
        """生成器,返回当前及所有子承包商的迭代器"""
        yield self
        for sub in self.subs:
            yield from sub.iter_all()

使用示例:

# 用walk方法处理
def handle_contractor(contractor):
    if contractor.status != 'Enrolled':
        print(f"需处理:{contractor.contractorName}")

general_contractor.walk(handle_contractor)

# 用生成器遍历
for contractor in general_contractor.iter_all():
    if contractor.status != 'Enrolled':
        # 执行你的业务操作
        pass

3. 优化层级加载逻辑(可选延迟加载)

当前一次性加载所有层级,可改为延迟加载,减少初始请求量,仅在需要时加载子承包商:

class ContractorData():
    def __init__(self, soup:BeautifulSoup, parentId=None):
        # ... 保留原有初始化代码 ...
        self._subs_loaded = False  # 标记子项是否已加载
    
    def load_subs(self, pid, level):
        if not self.has_subs or self._subs_loaded:
            return
        self.subs = load_subs(pid, self.id, level)
        self._subs_loaded = True

遍历时代按需加载:

def walk_with_lazy_load(contractor, pid, level):
    if contractor.status != 'Enrolled':
        print(f"处理:{contractor.contractorName}")
    if contractor.has_subs:
        contractor.load_subs(pid, level + 1)
        for sub in contractor.subs:
            walk_with_lazy_load(sub, pid, level + 1)

4. 提升状态判断的健壮性

当前依赖固定顺序的列判断状态,若页面布局变化会失效,可改为通过图片的关键词或alt属性判断:

# 示例:通过图片src关键词判断状态
status_img = soup.find('img', class_='status-icon')  # 假设状态图标有统一类名
if status_img:
    src = status_img.get('src', '')
    if 'enrolled' in src:
        self.status = 'Enrolled'
    elif 'excluded' in src:
        self.status = 'Excluded'
    elif 'pending' in src:
        self.status = 'Pending'
    else:
        self.status = 'Unknown'

内容的提问来源于stack exchange,提问作者Mike Dergance

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 03:31:37