我目前正在使用 Python 进行 OOP 在线类(class),其中一项技能测试是编写密码生成器类。您将在下面找到推荐的答案。
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
# map(lambda x: str(x), random...)
self.password = "".join(map(str, random.choices(population, k=int(length))))
def __init__(self, strength="mid", length=None): #None so there's no need for a value
self.strength = strength
self.length = length
self._generate_password()
当使用该代码创建该类的实例并生成具有高或中安全性的密码时,底层 SAMPLES["letter] 列表会被修改。这会导致低强度密码共享其他密码的所有属性优势。
在调用 show_input_universe 方法时我什至可以看到这种效果。其他列表已添加到原始列表中。
但是这是为什么呢?
据我了解,population = self.SAMPLES["letters"]
创建了population 变量,该变量存储指向 self.SAMPLES["letters"] 列表的指针。
但是串联到底是如何工作的:
if self.strength == "high":
population += self.SAMPLES["numbers"] + self.SAMPLES["punctuation"]
解决方案如下:
from copy import copy
population = Copy(self.SAMPLES["letters"])
这会创建初始列表的副本,该副本仅在特定实例中进行修改。
最佳答案
人口类型为列表
。 List 实现了数据模型钩子(Hook) __iadd__
用于就地序列连接。此方法还返回现有实例:
>>> population = [0, 1]
>>> new = population.__iadd__([2, 3])
>>> new is population
True
>>> population
[0, 1, 2, 3]
因此,增强赋值语句 +=
改变了原始列表,它是类命名空间中字典的值之一。它位于 Password.SAMPLES
中 - 在所有实例之间共享。 尽管population
是方法内的局部变量,但仍然存在共享状态。
请注意,如果 list 没有实现 __iadd__
,那么您的代码将按您的预期工作,因为增强分配将回退到使用 __add__
,连接并返回一个新的实例。将 population += other
替换为 population = Population + other
以查看类似内容。
关于Python:类状态中的可变变量,幕后发生了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75620193/