题目:定义一个简单的在线聊天室类,允许用户加入聊天室、发送消息、查看聊天记录以及离开聊天室。
这四个动作听起来简单,但真动手时我发现,第一个要想清楚的其实是:聊天室里的东西,到底该怎么存?
一、我的思路:一个类,两个列表
我把聊天室的数据拆成了两类完全不一样的东西:
self.users(用户列表) :记录"现在谁在场",是状态 ------人在增减,列表随时变化;self.messages(消息列表) :记录"发生过什么",是历史------只增不减,按时间顺序排队
def init(self):
print("欢迎使用在线聊天室")
name = input("请输入聊天室名称:")
self.name = name
self.users = \[\]
self.messages = \[\]
初始化时顺便问了聊天室名称存进 self.name,两个列表都从空的开始。想通"状态归状态、历史归历史"之后,四个方法一下就清晰了。
二、四个方法的设计要点
1. join(user) 加入:先查重,再双双记录
用 if user not in self.users 判断这个人在不在场。不在就把他 append 进用户列表,同时往消息列表记一条"Alice 加入聊天室";已在的话也不报错,只提示并记录"已经在聊天室中"。
重复加入不会把人塞进列表两次,这是列表防重的第一课。
2. leave(user) 离开:移除人,但保留"离开"这件事
用 if user in self.users 判断后,用 self.users.remove(user) 把人移出,同时在消息列表记一条"离开聊天室"。
这里我特意想明白一件事:人走了,历史不能跟着走。 离开后再次 show_message(),从他进群到说过的每句话都还在------这就是"状态列表"和"历史列表"分开存放的好处,消息记录完整覆盖了整个生命周期。
3. send_message(user, message) 发消息:先验"在场资格"
只有 user in self.users 才允许发言,消息按 "{user}: {message}" 的格式入列;不在场的人想说话,只会被记录一句"不在聊天室中",他的消息进不了聊天记录正文。
一个小 if 就完成了最朴素的"权限控制"。
4. show_message() 查看:空房要有空房的说法
直接用 if self.messages: 判断列表是否有内容(空列表的布尔值是 False,这个写法比 len(...) > 0 更 Pythonic),有就逐条打印,没有就提示"聊天室中没有消息!",不会冷场。
三、示例运行:用五个场景讲完一个故事
主程序我没有随手调用,而是按真实剧情排了五个场景:
-
Alice、Bob 先后加入。
-
互相问好。
-
查看记录。
-
两人离开。
-
再查一次记录。
最后一幕是我最满意的:两个人都不在场了,self.users 已经清空,但 self.messages 里从"加入"、"Hello Bob!"到"离开聊天室",一条不少。 一个聊天室的完整一生,全在历史列表里躺着。
(另外,每个场景前还用 print("=" * 40) 加了分隔标题,终端输出的层次非常清楚------这个小习惯从之前的演示程序里受益良多。)
四、写完给自己挑的几条刺
对照前面 73、74 题的经验,这版其实还有进化的空间:
-
类里"又打印又记录" :每个方法都既
append进消息列表、又print到屏幕,台词还是类自己抢的。按"类只负责逻辑,菜单只负责交互"的原则,其实可以让方法只维护数据、由主程序决定怎么展示。这版保留打印是为了演示直观,我知道欠了什么。 -
消息没有时间和序号:所有记录都是裸字符串,将来想按用户筛选、按时间排序就麻烦了,理想格式应该带上时间戳。
-
"在场名单"用列表查人每次都要从头扫 :人多了以后用集合(
set)判断in会更快,不过这个知识点我还没学到,先记在账上。
五、小结
这道题最大的收获,是那个数据结构的拆分决定:users 管"现在",messages 管"过去"。 加入、离开改变的是前者,一切事件都沉淀到后者。把"状态"和"历史"分开存之后,查重、验资格、留记录这些需求各自落位,代码思路一下就顺了。
另外一个有趣的发现,是 in 判断的三次妙用:join 防重复、leave 防错删、send_message 防未入群发言------仅仅一个运算符,就撑起了整个聊天室的秩序。