3、第一个模块:用户账户系统(上)——设计用户类,包含用户名、密码、初始资金等属性
好,咱们正式开始动手写代码了。
第一个模块,我选的是用户账户系统。为什么先做这个?你想想看,一个交易模拟器,总得有人来操作吧?不管是买股票、卖基金,还是查看盈亏,所有动作都得挂在一个用户下面。没有用户,后面的交易逻辑就是空中楼阁。
说白了,用户账户系统是整个软件的“地基”。地基打不牢,后面盖多少层楼都悬。我在早期做项目时,就吃过这个亏——用户类设计得太随意,结果后面加功能时,改得我头皮发麻。所以这次,咱们从一开始就把事情做对。
3.1 用户类需要承载什么?
一个用户,在交易系统里,至少得有三个核心信息:
- 用户名:用来标识身份,不能重复。
- 密码:用来验证身份,保护账户安全。
- 初始资金:用户开始交易时有多少钱,这是交易的“弹药”。
嗯,听起来很简单对吧?但实际项目中,这三个属性背后藏着不少细节。我一个个说。
3.2 用户名——别小看这个字段
用户名看起来就是个字符串,但我在项目中遇到过一个问题:用户注册时,有人用“admin”,有人用“Admin”,还有人用“ADMIN”。系统怎么处理?
我的习惯是:用户名统一转成小写存储。这样“Admin”和“admin”就是同一个人,避免重复注册的麻烦。
另外,用户名长度也要限制。太短容易被猜到,太长用户自己都记不住。我个人建议:3到20个字符,只允许字母、数字和下划线。
str.isalnum() 和 '_' in username 来快速校验。但注意,isalnum() 会把下划线判为False,所以得单独处理。
3.3 密码——安全第一
密码这个事,我多说两句。很多新手喜欢直接把密码明文存到数据库里,这是大忌。我曾经接手过一个项目,用户密码全是明文,吓得我连夜加班改代码。
正确的做法是:永远不要存明文密码。存的是密码的哈希值。Python里有个内置库 hashlib,用起来很方便。
咱们这个课程虽然只是模拟器,但养成好习惯很重要。所以从一开始,我就把密码哈希处理写进去。
3.4 初始资金——浮点数还是整数?
资金用浮点数?还是用整数?这是个经典问题。
浮点数有精度问题。0.1 + 0.2 在计算机里不等于 0.3,这个你应该听说过。做金融系统,精度就是生命。差一分钱,用户都可能投诉你。
我的建议是:用整数存储,单位是“分”。比如100元,存成10000分。显示的时候再转成元。这样加减乘除都不会有精度问题。
当然,咱们这个模拟器初期,为了直观,我暂时用浮点数。但我会在代码里加注释,提醒你以后要改。
3.5 代码实现——第一个User类
好了,理论说完了,咱们直接上代码。这是咱们整个课程的第一段代码,我写得尽量清晰。
import hashlib
class User:
"""用户类,存储用户基本信息"""
def __init__(self, username: str, password: str, initial_balance: float = 10000.0):
# 用户名统一转小写,避免大小写问题
self.username = username.lower().strip()
# 密码绝不存明文,存哈希值
self.password_hash = self._hash_password(password)
# 初始资金,暂时用浮点数,后续可改为整数(单位:分)
self.balance = initial_balance
# 记录用户创建时间,方便后续审计
self.created_at = None # 后面章节会补充时间戳
def _hash_password(self, password: str) -> str:
"""对密码进行SHA-256哈希处理"""
# 加盐(salt)可以增加安全性,这里先简单处理
return hashlib.sha256(password.encode('utf-8')).hexdigest()
def check_password(self, password: str) -> bool:
"""验证密码是否正确"""
return self.password_hash == hashlib.sha256(password.encode('utf-8')).hexdigest()
def __str__(self) -> str:
"""友好的字符串表示"""
return f"User({self.username}, 余额: {self.balance:.2f}元)"
这段代码里,我做了几件事:
- 用户名转小写并去空格
- 密码用SHA-256哈希存储
- 提供了
check_password方法,方便后续登录验证 - 重写了
__str__,打印用户信息时更直观
_hash_password 方法名前加了下划线,表示这是内部方法,外部不应该直接调用。这是Python的约定,虽然不是强制的,但建议遵守。
3.6 测试一下
写完了类,咱们得跑一跑,看看效果。我习惯每写一段代码就测试,而不是攒一堆再调试。那样出了问题,找起来太痛苦。
# 创建一个用户
user1 = User("Alice", "my_secret_123")
print(user1) # 输出: User(alice, 余额: 10000.00元)
# 验证密码
print(user1.check_password("my_secret_123")) # True
print(user1.check_password("wrong_password")) # False
# 查看密码哈希值(注意:这是内部属性,一般不应直接访问)
print(user1.password_hash)
# 输出类似: a1b2c3d4...(一串64位的十六进制字符串)
看到没?密码验证成功了,但密码本身并没有被存储。这就是哈希的好处。
3.7 知识结构图
为了让你更直观地理解这个用户类的设计,我画了一张图。它展示了用户类的核心属性和方法,以及它们之间的关系。
3.8 避坑指南
最后,分享几个我踩过的坑:
- 忘记处理用户名大小写:我曾经有个项目,用户“Tom”和“tom”被当成两个人,结果数据乱成一锅粥。
- 密码哈希不加盐:虽然SHA-256比MD5安全,但如果不加盐,两个用户密码相同,哈希值也相同。这会给彩虹表攻击留下漏洞。
- 初始资金用浮点数直接运算:如果你在后续代码里做 balance += 0.1 这样的操作,很快就会出现 0.30000000000000004 这种尴尬情况。
balance 改成整数(单位:分),虽然显示时麻烦一点,但能避免未来99%的精度问题。如果你现在不改,记得在代码里加个TODO注释。
好了,这一节的内容就到这。用户类的骨架已经搭好了,下一节咱们会往里面加更多功能,比如交易记录、持仓管理等等。但今天,先把地基夯实。