用Python实现一个电影订票系统!
用 Python 写个电影订票系统,说难也不难,说简单也有一堆小细节,尤其是选座那块,稍不注意就乱了套。下面就按“一个脚本就能跑起来”的思路,把核心流程撸一遍,中间穿插代码,你照着改一改就能成自己用的小工具。
电影订票最基本就那么几步:
看有哪些电影、哪些场次 选一个场次 打开座位图,挑座位 下单,占住座位
我们先不搞复杂的用户登录、支付、退票之类,把“选座 + 占座”打通就够了,后面你要扩展成 Web、接数据库啥的,再往上堆就行。
所以可以抽象出几个核心概念:
电影场次:放什么片、几点、在哪个影厅 座位:在第几排第几列,是否已经被占 订票逻辑:给定场次 + 座位,把它标记为已售出
用 Python 最自然的方式就是给“场次”做一个类,把座位信息也塞进去。
来一个比较轻的类,别搞太复杂,命令行玩玩够用了:
from typing import ListclassScreening:
def__init__(self, sid: int, movie_title: str, time: str, hall: str,
rows: int = 5, cols: int = 8):
self.sid = sid
self.movie_title = movie_title
self.time = time
self.hall = hall
self.rows = rows
self.cols = cols
# 0 表示空座,1 表示已售
self.seats: List[List[int]] = [
[0for _ in range(cols)] for _ in range(rows)
]
defshow_seats(self):
print(f"\n场次 {self.sid} - 《{self.movie_title}》 {self.time}{self.hall}")
print("座位图(O=空位,X=已售):")
# 打印列号
print(" ", end="")
for c in range(1, self.cols + 1):
print(f"{c:2d}", end=" ")
print()
# 打印每一排
for r in range(self.rows):
print(f"排{r + 1:2d} ", end="")
for c in range(self.cols):
symbol = "O"if self.seats[r][c] == 0else"X"
print(f" {symbol} ", end="")
print()
defbook_seat(self, row: int, col: int) -> bool:
"""row、col 传进来用 1 开始的人类习惯,内部转成 0 开始"""
# 越界校验
ifnot (1 <= row <= self.rows and1 <= col <= self.cols):
print("座标不对,超出座位范围了")
returnFalse
r_idx = row - 1
c_idx = col - 1
if self.seats[r_idx][c_idx] == 1:
print("这个位置已经被订走了,换一个吧")
returnFalse
self.seats[r_idx][c_idx] = 1
print(f"订座成功:{row} 排 {col} 座")
returnTrue
这个类解决了三件事:
把一场电影的基础信息存起来 在内存里维护一张二维座位表 提供两个操作:展示座位、订座位
只要这个东西好用,外面的系统就是一层壳。
单个场次搞定了,还得有个“系统入口”,把多个场次挂在一起,再给用户提供命令行菜单。
比如来个 TicketSystem 类:
classTicketSystem:
def__init__(self):
self.screenings = {}
self._init_demo_data()def_init_demo_data(self):
# 这里先写死几场,实际可以从数据库、接口里面加载
s1 = Screening(1, "星际穿越", "2024-06-01 19:00", "1号厅", rows=6, cols=8)
s2 = Screening(2, "盗梦空间", "2024-06-01 20:30", "2号厅", rows=5, cols=6)
s3 = Screening(3, "流浪地球 2", "2024-06-01 21:00", "3号厅", rows=7, cols=10)
self.screenings[s1.sid] = s1
self.screenings[s2.sid] = s2
self.screenings[s3.sid] = s3
deflist_screenings(self):
print("\n当前可选场次:")
for sid, s in self.screenings.items():
print(f"{sid}. 《{s.movie_title}》 {s.time}{s.hall} "
f"({s.rows}排 x {s.cols}列)")
defchoose_screening(self) -> Screening | None:
self.list_screenings()
raw = input("请输入要查看的场次编号(q 返回):").strip()
if raw.lower() == "q":
returnNone
ifnot raw.isdigit():
print("输入的不是数字")
returnNone
sid = int(raw)
screening = self.screenings.get(sid)
ifnot screening:
print("没有这个场次编号")
returnNone
return screening
defrun(self):
whileTrue:
print("\n==== 简易电影订票系统 ====")
print("1. 查看场次列表")
print("2. 查看场次座位并订票")
print("3. 退出系统")
choice = input("请选择功能:").strip()
if choice == "1":
self.list_screenings()
elif choice == "2":
self.handle_booking()
elif choice == "3":
print("拜拜~")
break
else:
print("没这个选项,再来一次")
defhandle_booking(self):
screening = self.choose_screening()
ifnot screening:
return
# 展示座位
screening.show_seats()
# 选座
row_raw = input("请输入要订的排号(数字,q 取消):").strip()
if row_raw.lower() == "q":
return
col_raw = input("请输入要订的列号(数字,q 取消):").strip()
if col_raw.lower() == "q":
return
ifnot (row_raw.isdigit() and col_raw.isdigit()):
print("排号/列号必须是数字")
return
row = int(row_raw)
col = int(col_raw)
screening.book_seat(row, col)
# 订完再显示一次座位图
screening.show_seats()
最后别忘了加个脚本入口:
if __name__ == "__main__":
system = TicketSystem()
system.run()
丢到一个 ticket_system.py 里,在终端里:python ticket_system.py,就能跑起来了。
上面那个版本已经能用,不过选座体验还比较原始,比如:
只能一次选一个座位 没有“连坐”检测 没有限制一场订太多
这些都可以在 Screening 里继续扩展,比如一次支持订多个座位,可以用一个小函数解析类似 “3-5,3-6,4-7” 这种输入。
举个多选座的简单版本:
defparse_seat_list(seat_str: str):
"""
seat_str 形如:'3-5,3-6,4-7'
返回列表 [(3,5), (3,6), (4,7)]
"""
result = []
parts = seat_str.split(",")
for p in parts:
p = p.strip()
ifnot p:
continue
if"-"notin p:
print(f"格式不对:{p},应该是 '排-列'")
returnNone
r_str, c_str = p.split("-", 1)
ifnot (r_str.isdigit() and c_str.isdigit()):
print(f"格式不对:{p},排和列必须是数字")
returnNone
result.append((int(r_str), int(c_str)))
return result
然后在 handle_booking 里面改一下逻辑:
defhandle_booking(self):
screening = self.choose_screening()
ifnot screening:
return
screening.show_seats()
seat_str = input("请输入要订的座位,例如 3-5,3-6 (q 取消):").strip()
if seat_str.lower() == "q":
return
seats = parse_seat_list(seat_str)
if seats isNone:
return# 先整体校验一遍,避免一半成功一半失败
for row, col in seats:
ifnot (1 <= row <= screening.rows and1 <= col <= screening.cols):
print(f"{row}-{col} 超出范围,整单取消")
return
if screening.seats[row - 1][col - 1] == 1:
print(f"{row}-{col} 已被占用,整单取消")
return
# 校验通过再正式落位
for row, col in seats:
screening.book_seat(row, col)
screening.show_seats()
这样起码能做到“要么全部成功,要么全不订”,避免出现用户说“我刚刚选的那几张票怎么只扣了一部分”。
当前这套是纯内存版,你一退出程序,所有订票记录就没了。如果只是为了写作业、练手,问题不大。如果想稍微认真一点,可以弄个最简单的落地方式,比如:
用 JSON 文件存每个场次的座位矩阵 程序启动时从 JSON 读进来,退出时再写回去
给一个非常简化的示例,大概这样:
import json
from pathlib import PathclassTicketSystem:
def__init__(self, data_file: str = "data.json"):
self.data_file = Path(data_file)
self.screenings = {}
if self.data_file.exists():
self._load_from_file()
else:
self._init_demo_data()
self._save_to_file()
def_save_to_file(self):
data = {}
for sid, s in self.screenings.items():
data[sid] = {
"movie_title": s.movie_title,
"time": s.time,
"hall": s.hall,
"rows": s.rows,
"cols": s.cols,
"seats": s.seats,
}
self.data_file.write_text(json.dumps(data, ensure_ascii=False, indent=2))
def_load_from_file(self):
raw = self.data_file.read_text(encoding="utf-8")
data = json.loads(raw)
for sid_str, info in data.items():
sid = int(sid_str)
s = Screening(
sid,
info["movie_title"],
info["time"],
info["hall"],
info["rows"],
info["cols"],
)
s.seats = info["seats"]
self.screenings[sid] = s
# 记得在订票成功后调用 _save_to_file
然后在 book_seat 或者 handle_booking 那里,只要订票成功就调用一次 _save_to_file(),就能把新状态写回磁盘。虽然很粗糙,但足够支撑“下次打开还能看到之前的座位情况”。
等这个命令行版本跑顺了,再往上加花活就容易了,比如:
用 Flask / FastAPI 做成 Web 接口,前端页面用 Ajax 拿座位图 引入数据库,把场次和订单分开建表 加上用户系统,谁订的票、一个账号限购几张 加一个“锁座超时自动释放”的小任务
这些本质上都是在现在这套“核心模型”的基础上往外扩,真正要保证不乱的,还是 Screening 里那张二维座位表和几条订票规则。
你可以先把上面的代码扔进一个文件里跑一跑,习惯一下这个数据流,后面要接 Web、接数据库的时候,就不会觉得逻辑特别散。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB