如何正确迭代类中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
相关产品推荐
相关产品推荐

