Python中构造持有N个独立类实例的最Pythonic实现方式
结论先行
你现在用的列表推导写法本身就是最Pythonic的标准实现,完全没必要刻意修改:
things = [lib.Thing() for _ in range(NUM_THINGS_TO_CREATE)]
针对你两个顾虑的说明
- 关于
_变量:这不是无意义的冗余写法,是Python全生态通用的约定标识,专门用来标记「循环、解包操作中不需要被后续逻辑引用的丢弃值」。所有熟悉Python的开发者看到_都会立刻明白这个位置的值不需要关注,反而能提升代码可读性,不存在“不优雅”的问题。 - 关于
[]列表容器:你的使用场景(构造多线程实例、需要持有实例引用、仅要求可迭代)下,列表是最优选择:- 列表推导会立刻完成所有实例的构造,不存在延迟初始化的问题
- 列表会持有所有实例的强引用,只要列表本身不被回收,内部的Thread实例就不会被垃圾回收,能避免线程运行时出现无厘头的异常
- 列表只存储实例的内存引用,单条引用仅占8字节左右,哪怕创建上万个实例,列表本身的内存开销也可以忽略
- 列表天然支持迭代,完全满足你对容器的要求
可选调整方案(非必须)
如果你实在对_或者列表有抵触,可以参考以下方案,注意避开常见坑:
- 不想在业务代码里看到
_的话,可以把批量构造逻辑封装成通用工具函数,调用层就不会出现丢弃变量:from itertools import repeat def batch_init(cls, count): return [cls() for _ in repeat(None, count)] things = batch_init(lib.Thing, NUM_THINGS_TO_CREATE) - 如果你不需要后续对实例集合做增删、索引操作,也可以用不可变元组存储,效果和列表一致:
things = tuple(lib.Thing() for _ in range(NUM_THINGS_TO_CREATE)) - 绝对不要踩的坑:别为了省掉方括号直接把列表推导改成生成器表达式(把
[]换成()):
生成器是惰性执行的,写完这行代码时不会创建任何实例,只有迭代# 严重错误写法,禁止在该场景使用 things = (lib.Thing() for _ in range(NUM_THINGS_TO_CREATE))things时才会逐个构造对象;如果迭代时没有额外持有实例引用,刚创建的对象会立刻被GC回收,在多线程场景下会直接导致线程异常退出,问题极难排查。
最后提一句:针对你构造线程实例的场景,只要保证持有实例的容器生命周期覆盖所有线程的运行周期即可,你原本的列表推导写法足够简洁、可靠,不需要为了“看起来更高级”刻意改成冷门写法。
内容的提问来源于stack exchange,提问作者MooseBoys
相关产品推荐
相关产品推荐

