Python类状态中的可变对象:密码生成器类列表意外修改解析
Python类列表引用导致的意外修改问题解析
问题场景
使用以下密码生成器类创建不同强度的密码时,发现类变量SAMPLES中的"letters"列表被意外修改:
import re import random from string import ascii_letters, punctuation from copy import copy class Password: SAMPLES = { "letters": list(ascii_letters), "numbers": list(range(10)), "punctuation": list(punctuation) } DEFAULT_SETTINGS = { "low": 8, "mid": 12, "high": 16 } @classmethod def show_input_universe(cls): return cls.SAMPLES def _generate_password(self): population = self.SAMPLES["letters"] length = self.length or self.DEFAULT_SETTINGS.get(self.strength) if self.strength == "high": population += self.SAMPLES["numbers"] + self.SAMPLES["punctuation"] elif self.strength == "mid": population += self.SAMPLES["numbers"] else: pass self.password = "".join(map(str, random.choices(population, k=int(length)))) def __init__(self, strength="mid", length=None): self.strength = strength self.length = length self._generate_password()
当生成高强度/中等强度密码后,再生成低强度密码时,结果会包含数字或符号;调用show_input_universe()也能看到原"letters"列表被追加了其他类型的字符。
核心原因解析
问题的关键在于可变对象的引用特性和列表+=操作的本质:
- 引用指向同一对象:
population = self.SAMPLES["letters"]语句中,self.SAMPLES["letters"]是类变量存储的列表对象(可变类型),赋值操作只是让population变量指向这个同一个列表对象,并没有创建新的副本。 +=是原地修改操作:对于列表这类可变对象,+=运算符等价于调用list.extend()方法,它会直接在原列表对象上追加元素,而不是创建一个新的列表重新赋值给population。
因此,当执行population += self.SAMPLES["numbers"]这类代码时,实际是在修改self.SAMPLES["letters"]指向的原列表对象——也就是类变量SAMPLES里的"letters"列表,所有后续实例都会共享这个被修改后的列表。
解决方案验证
使用copy模块创建列表副本,让population指向一个新的列表对象:
population = copy(self.SAMPLES["letters"])
此时population是原"letters"列表的独立副本,后续的+=操作只会修改这个副本,不会影响类变量中的原列表,从而避免不同强度密码的字符池互相干扰。
内容的提问来源于stack exchange,提问作者maosi100
相关产品推荐
相关产品推荐

