Python实例属性声明问题:TestItems的items列表被多实例共享原因排查
Python实例共享可变默认参数的问题分析与修复
问题场景
你定义了两个类TestItem和TestItems,尝试创建多个TestItems实例并分别导入数据,却发现所有实例的items列表被意外共享了:
你的代码
class TestItem(object): def __init__(self): super(TestItem, self).__init__() def import_(self, value): self.value = value class TestItems(object): def __init__(self, items=[]): super(TestItems, self).__init__() self.items = items def __getitem__(self, index): return self.items[index] def __len__(self): return len(self.items) def import_from_list(self, data): for i in data: item = TestItem() item.import_(i) self.items.append(item) data = [] data.append(TestItems()) data[-1].import_from_list(['A', 'B']) for i, j in enumerate(data): print('Size of data[%i] - %i' % (i, len(j))) data.append(TestItems()) data[-1].import_from_list([1, 2]) for i, j in enumerate(data): print('Size of data[%i] - %i' % (i, len(j))) print('First element of data[%i] - %s' % (i, j[0].value)) print(data[0]) print(data[1])
运行输出
Size of data[0] - 2 Size of data[0] - 4 First element of data[0] - A Size of data[1] - 4 First element of data[1] - A <__main__.TestItems object at 0x000002C0765F51D0> <__main__.TestItems object at 0x000002C0765F52B0>
可以看到两个不同的TestItems实例,它们的items列表长度都是4,且第一个元素都是A,这显然不符合每个实例独立管理数据的预期。
问题根源:可变默认参数的经典陷阱
这是Python里非常常见的坑——可变默认参数(比如列表、字典这类可修改对象)会在函数/方法定义时就完成初始化,而不是每次调用时重新创建。
在你的TestItems类的__init__方法里,items=[]这个默认参数,会在Python加载这个类的时候就创建一个空列表对象。之后每次调用TestItems()(不传items参数时),都会直接复用这个已经存在的列表,而不是新建一个。
所以第一个TestItems实例调用import_from_list往self.items里添加元素时,第二个实例的self.items其实指向的是同一个列表,自然会共享所有元素。
你的修复方案完全正确!
你改成以下代码后成功解决了问题:
class TestItems(object): def __init__(self, items=None): super(TestItems, self).__init__() self.items = items or []
为什么这个方法有效?
- 当不传
items参数时,items会被设为None,items or []会创建一个全新的空列表,每个TestItems实例都会拥有自己独立的items列表。 - 如果传入了非空的
items参数,则直接使用传入的列表;如果传入的是空列表,items or []也会创建新的空列表(若你想保留传入空列表的行为,改用if items is None的判断会更精确)。
补充:更严谨的写法(可选)
如果你想避免传入的外部列表被实例的操作意外修改(比如实例调用append会改变原列表),可以改成更明确的判断,并且对传入的列表做浅拷贝:
def __init__(self, items=None): super(TestItems, self).__init__() if items is None: self.items = [] else: # 浅拷贝,确保实例的items和外部传入的列表彻底独立 self.items = list(items)
这样无论是否传入参数,每个实例的items都是独立的,不会影响外部数据。
内容的提问来源于stack exchange,提问作者 GhostKU
相关产品推荐
相关产品推荐

