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

如何正确迭代类中MyItem类型可调用成员并收集为类属性

问题原因

原有迭代逻辑仅通过callable()筛选可返回成员,但Python中实例方法、内置函数等均属于可调用对象,导致类中定义的普通方法func被误纳入迭代范围,访问其不存在的name属性时就会抛出AttributeError。

解决方案

实现两个核心目标:

  • 迭代时仅返回MyItem类型的成员,过滤普通方法、其他属性
  • 类创建阶段自动收集所有MyItem类型成员到_myitems类属性,避免每次迭代重复扫描属性降低性能

推荐通过元类实现类创建阶段的自动收集,兼容性最好,逻辑如下:

# 元类:负责在类定义时自动收集MyItem类型成员
class MyItemCollectorMeta(type):
    def __init__(cls, class_name, bases, namespace, **kwargs):
        super().__init__(class_name, bases, namespace, **kwargs)
        collected_items = []
        # 沿继承链从顶层父类到当前类遍历,避免重复收集父类成员
        for base in reversed(cls.__mro__):
            for attr_value in base.__dict__.values():
                if isinstance(attr_value, MyItem) and attr_value not in collected_items:
                    collected_items.append(attr_value)
        cls._myitems = collected_items

# 原有MyItem类保持不变
class MyItem(object):
    widget = LabelValue()
    name = ""
    data = ""
    def __init__(self, name="", label="", info={}, widget=None):
        if widget is not None:
            self.widget = widget
        self.name = name
        self.label = label
        self.info = info
    def __call__(self, **kwargs):
        self.widget(item =self, **kwargs)

# 给Cls指定元类
class Cls(object, metaclass=MyItemCollectorMeta):
    foo = MyItem()
    bar = 'hello'
    def __init__(self, test):
        self.test = test

    def func(self):
        return 'call me'

    def __iter__(self):
        # 直接返回预收集的MyItem列表迭代器,无需每次遍历全量属性
        return iter(self._myitems)

扩展适配场景

如果存在实例层面动态绑定MyItem成员的需求(即在__init__或其他实例方法中通过self.xxx = MyItem()绑定),可以修改__iter__方法补充扫描实例属性:

def __iter__(self):
    # 先返回类层面预收集的MyItem成员
    yield from self._myitems
    # 扫描实例层面绑定的MyItem成员,自动去重
    for attr_value in self.__dict__.values():
        if isinstance(attr_value, MyItem) and attr_value not in self._myitems:
            yield attr_value

方案说明

  • 类型判断使用isinstance()而非直接比较type(),可以正确识别MyItem子类的实例,符合继承特性
  • 收集逻辑仅在类定义时执行一次,相比每次迭代调用dir()扫描全量属性性能更高
  • 自动过滤双下划线特殊方法、普通字符串/数值属性、普通实例方法,仅返回目标MyItem成员

测试代码运行验证:

r = {}
for i in Cls(test="demo"):
    r[i] = i.name
# 运行无报错,r中仅包含MyItem实例foo对应的键值对

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 09:06:27