当前位置:首页>python>用Python实现一个电影订票系统!

用Python实现一个电影订票系统!

  • 2026-10-21 13:29:04
用Python实现一个电影订票系统!

那天晚上快十一点,我在客厅沙发上瘫着刷手机,想订一张第二天早上第一场的电影票,结果某个App卡了半天一直转圈。人都困得不行了,订单还在“处理中”。我当时就想:要不自己用 Python 写个小电影订票小系统玩玩算了,起码逻辑和问题都在我掌控里,对吧。

就当是给自己练下基本功,你也可以跟着一起写一个,很简单的那种,命令行就能跑起来,不整花里胡哨的页面。

先想一想,电影订票到底要干嘛嘛,别一上来就敲代码。正常用户的操作流程,大概就是:

  • 看到有哪些电影、什么场次
  • 选一个场次
  • 选座位
  • 支付(咱这里就不连支付了,当作“模拟付款成功”)
  • 看看自己的订单

所以最小闭环其实就是:场次 + 座位 + 订单。你把这三块搞清楚,一个小系统就成型了。

我一般习惯用几个类把东西抽一下,不然全写在一个脚本里,变量到处飞,很快就乱了。

我们这次会有这么几个概念:

  • 场次:哪部电影、几点、单价多少、座位分布
  • 座位:第几排第几列,有没有卖掉
  • 订单:谁买的、买了哪个场次、买了什么座位

先从最核心的“场次 + 座位”开始写起。

那天在公司楼下抽烟的时候,我就在纸上随手画了个座位图,5 排 8 列那种迷你厅就够演示了。代码里就是一个二维数组就搞定了。

我们用 dataclasses 来省点事儿,定义好 Seat 和 Show 两个类:

from dataclasses import dataclass, field
from typing import List


@dataclass
classSeat:
"""一个座位"""
    row: int      # 第几排,从 0 开始存
    col: int      # 第几列,从 0 开始存
    is_booked: bool = False# 是否已售


@dataclass
classShow:
"""一场电影场次"""
    id: int
    movie_title: str
    time: str          # 简单用字符串,不搞 datetime 了
    price: float
    rows: int = 5# 座位行数
    cols: int = 8# 座位列数
    seats: List[List[Seat]] = field(init=False)

def__post_init__(self):
# 初始化座位二维数组
        self.seats = [
            [Seat(r, c) for c in range(self.cols)]
for r in range(self.rows)
        ]

defavailable_seat_count(self) -> int:
"""计算还剩多少空座"""
return sum(
1
for row in self.seats
for seat in row
ifnot seat.is_booked
        )

defshow_seat_map(self) -> None:
"""在命令行打印座位图,O=空位 X=已售"""
        print(f"\n场次 {self.id} - 《{self.movie_title}》 {self.time}")
        print("座位图:O=可选,X=已售")
# 打印列号(从 1 开始给用户看)
        header = "    " + " ".join(f"{c+1:2d}"for c in range(self.cols))
        print(header)
for r, row in enumerate(self.seats):
            line = f"第{r+1:2d}排 "
for seat in row:
                line += " X"if seat.is_booked else" O"
            print(line)
        print()

defbook_seat(self, user_row: int, user_col: int) -> Seat:
"""
        订座位,用户输入是从 1 开始的行列,
        内部存储是从 0 开始,所以要减 1
        """

        r = user_row - 1
        c = user_col - 1

ifnot (0 <= r < self.rows and0 <= c < self.cols):
raise ValueError("座位不存在,检查下排号和列号是不是超范围了")

        seat = self.seats[r][c]
if seat.is_booked:
raise ValueError("这个座位刚刚被别人抢先买了,换一个吧")
        seat.is_booked = True
return seat

这里几个点说一下,别光看过去就完了:

  • __post_init__ 是 dataclass 在初始化后自动调用的,我们在这里把 seats 二维数组建出来
  • 用户输入的行列我们让他从 1 开始(更符合人类直觉),内部都是从 0 开始存,记得减 1
  • show_seat_map 这个打印函数,很粗糙,但够用,看着也清楚

有了场次,还差一个东西:订单。

你总得知道“谁买了哪个场次的哪几个座位”,对不对。为了简单,我们不搞用户登录,就用一个随便输入的“昵称”当作用户标识。

订单结构大概就:订单号 + 用户名 + 场次 + 座位列表 + 总价。

from dataclasses import dataclass
from typing import List, Tuple


@dataclass
classOrder:
"""简单的订单结构"""
    order_id: int
    user_name: str
    show: Show
    seats: List[Seat]

    @property
deftotal_price(self) -> float:
return round(len(self.seats) * self.show.price, 2)

defsummary(self) -> str:
        seat_str = ", ".join(
f"{s.row + 1}排{s.col + 1}座"for s in self.seats
        )
return (
f"订单号:{self.order_id}\n"
f"用户:{self.user_name}\n"
f"电影:{self.show.movie_title}\n"
f"场次时间:{self.show.time}\n"
f"座位:{seat_str}\n"
f"总价:{self.total_price} 元"
        )

这里我懒得搞单独的价格字段了,反正总价就是:数量 × 单价。


上面这些都只是“模型”,真正跟用户打交道的是那个“主程序循环”。就是那种:

  1. 打印菜单
  2. 让你选 “查看场次 / 订票 / 查看订单 / 退出”
  3. 根据选择调用不同函数

我当时写的时候心里就一个念头:千万别把逻辑写得太复杂,不然你半夜调 bug 真的会骂自己。

来,我们写一个 TicketSystem 把东西包起来:

classTicketSystem:
def__init__(self):
# 模拟几场电影
        self.shows = {
1: Show(1, "星际穿越", "2024-01-07 10:00", 39.9),
2: Show(2, "盗梦空间", "2024-01-07 14:30", 35.0),
3: Show(3, "三体:黑暗森林", "2024-01-07 20:00", 49.9),
        }
        self.orders: List[Order] = []
        self._order_seq = 1# 自增订单号

defrun(self):
        print("欢迎来到命令行电影订票系统 🎬")
whileTrue:
            print("\n========== 主菜单 ==========")
            print("1. 查看所有场次")
            print("2. 订票(选场次 + 选座)")
            print("3. 查看我的订单")
            print("4. 退出系统")
            choice = input("请输入操作序号:").strip()

if choice == "1":
                self.list_shows()
elif choice == "2":
                self.handle_booking()
elif choice == "3":
                self.list_orders()
elif choice == "4":
                print("好的,祝你观影愉快~")
break
else:
                print("输入不太对,再来一次吧")

deflist_shows(self):
        print("\n当前可选场次:")
for show in self.shows.values():
            print(
f"场次ID: {show.id} | 《{show.movie_title}》 "
f"| 时间: {show.time} | 单价: {show.price} 元 "
f"| 剩余座位: {show.available_seat_count()}"
            )

defhandle_booking(self):
ifnot self.shows:
            print("现在还没有上架的电影场次。")
return

        self.list_shows()
try:
            show_id = int(input("\n请输入想要订票的场次ID:").strip())
except ValueError:
            print("场次ID必须是数字。")
return

if show_id notin self.shows:
            print("没有这个场次ID,检查一下是不是输错了。")
return

        show = self.shows[show_id]
        show.show_seat_map()

try:
            num = int(input("你想一次买几个座位?:").strip())
if num <= 0:
                print("买的数量得大于 0 吧。")
return
except ValueError:
            print("数量必须是数字。")
return

        seats: List[Seat] = []
for i in range(num):
            print(f"\n正在选择第 {i+1} 个座位:")
try:
                row = int(input("请输入排号(从1开始):").strip())
                col = int(input("请输入列号(从1开始):").strip())
                seat = show.book_seat(row, col)
                seats.append(seat)
                print(f"已选:第{row}排{col}座")
except ValueError as e:
                print(f"选座失败:{e}")
# 简单处理:如果失败,这个座位就不算
# 也可以选择让用户重选,这里先不复杂化
continue

ifnot seats:
            print("一个座位都没选上,这次订票就算了。")
return

        user_name = input("\n请输入你的昵称(用于记录订单):").strip() or"匿名用户"

        order = Order(
            order_id=self._order_seq,
            user_name=user_name,
            show=show,
            seats=seats
        )
        self.orders.append(order)
        self._order_seq += 1

        print("\n订票成功!订单信息如下:")
        print("--------------------------------")
        print(order.summary())
        print("--------------------------------")

deflist_orders(self):
ifnot self.orders:
            print("\n你还没有任何订单。")
return

        print("\n你的全部订单:\n")
for order in self.orders:
            print(order.summary())
            print("--------------------------------")

最后在文件末尾加上一个入口:

if __name__ == "__main__":
    system = TicketSystem()
    system.run()

这么一整套下来,一个可以跑的“命令行电影订票系统”就算完工了。你在终端里 python movie_ticket.py 跑起来,实际体验一下流程,会比只看代码印象深很多。

这里顺手说几个这种小玩具项目里,比较容易踩的点,免得你自己写的时候又重新掉坑里:

一个是输入校验。比如行列号越界、场次 ID 不存在、数量不是数字,这些东西在自己测试的时候不一定会去故意输错,但一旦别人来用,就一定有人各种乱输。上面我都是用 try/except ValueError 做了一层兜底,虽然简陋,但基本不会直接把程序整崩了。

还有一个是状态更新要统一地方维护。我们现在是:

  • Show 负责改 Seat.is_booked
  • TicketSystem 负责把 Order 存起来

如果你哪天想加“退票”功能,只要记住两件事: 把订单里的座位 is_booked 改回 False,然后把订单从 self.orders 里删掉就完事。逻辑都在这两块,不会散得到处都是。

再往后要扩展,其实有很多方向: 比如你想加:

  • 支持不同影厅(可以在 Show 里多加一个 hall_name)
  • 把数据保存到文件 / 数据库,下次重启还能看到之前的订单
  • 用 FastAPI 或 Flask 把这套逻辑包成 HTTP 接口,再随便搞个前端页面

这些都可以在现在这份代码的基础上慢慢加,你会发现:当最早的数据模型设计得还算清晰的时候,后面要长肉就舒服多了。

我写这种小系统,主要是有两个用处:

一个是练手,刷语法、刷类的设计、刷异常处理这些东西; 另一个就是,你之后面试的时候,如果有人问你“用面向对象设计一个订票系统”这种题,你脑子里是有画面的,不会愣在那里现编。

你可以先把上面的代码直接搞成一个 movie_ticket.py 跑一遍,能跑通之后,再根据自己想法魔改一版: 比如加个“同一行必须连坐”的限制、加个“学生票半价”的逻辑之类的。改着改着,这个就变成你自己的电影订票系统了。

行,我这边就先说到这,我去给自己真买张电影票了,不然写了半天系统结果一个电影都没看,太亏了。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥私藏精品🔥
虎哥作为一名老码农,整理了全网最全《Python高级资料合集》。总量高达650GB

最新文章

随机文章