场景描述
2022年12月7日注定会是一个里程碑的日子。因为从这一天开始,我们真正走出了新冠疫情的阴霾。经济开始逐渐复苏,民生有了烟火气,城市商业中心和购物广场开始回归和再现疫情之前的繁华热闹景象。瞧,寰银购物广场正在开展2023年新春促销活动。在青春美少女组合表演了精彩劲爆的暖场舞蹈之后,帅气的活动主持人开始粉墨登场:“大奖在招手,你要HOLD住”。当主持人宣布本次活动的特等奖品是价值20万的新能源轿车时,瞬间引爆了现场一片片的尖叫声。主持人卖力而讨好地调动现场的气氛,抽奖现场聚集了无数的消费者和围观的市民,人气越来越旺。根据“大奖在招手”活动规定:凡是在寰银购物广场购物消费合计金额满1000元者,均可以参与现场抽奖,保证抽奖必中奖。这样久违的现场促销活动还是吸引了众多市民的眼球,激发了消费热情,拉动了消费的增长。 下面就我们使用Python开发一个商场购物现场抽奖程序,来模拟和再现购物抽奖的火爆场景。
编程思路
抽奖程序需要明确和解决以下问题:
2.1 抽奖资格认定
根据购物小票金额加总合计数,由现场人工进行抽奖资格的认定,不用程序实现。
2.2 奖品池表示
我们可以使用Python的数据结构列表来存放奖品。列表的每一个元素,就代表一项奖品。
2.3 抽奖
可以使用random随机处理模块,实现从奖品池随机选取奖品。也就是从奖品池列表中随机选择一个元素。
2.4 补充奖品
根据实际需要,及时向奖品池中添加奖品,实际上就是实现对奖品池列表添加元素。
2.5 查询奖品
列出奖品池中前10个奖品,显示奖品池列表中的前10个元素。
代码实现
整个抽奖程序由两个模块组成:一个是prizes.py,主要是实现抽奖的一些基础性的公共函数;另一个模块big_prize.py则是抽奖程序的主程序。 模块prizes.py源代码程序如下所示:
"""
prizes.py : 大奖在招手公共模块
"""
import random
def make_pool(prz_list):
"""创建奖品池"""
prz_pool = []
for prize, amount in prz_list: # ①
for i in range(amount):
prz_pool.append(prize)
random.shuffle(prz_pool) # ②
return prz_pool
def lottery(prizes):
""" 开始抽奖 """
if prizes:
prize = random.choice(prizes) # ③
print('恭喜中奖:'+prize)
prizes.remove(prize) # ④
else:
print('没有奖品啦!')
return prizes
def append_prize(prizes):
""" 追加奖品 """
while True:
try:
prize_name = input('名称:')
amount = int(input('数量:'))
break
except ValueError:
print('数量:数据类型错!')
prizes += [prize_name] * amount # ⑤
random.shuffle(prizes)
return prizes
def print_prize(prizes):
"""打印前10奖品"""
if prizes:
for num, prize in enumerate(prizes[:10]):
print(str(num + 1) + "\t" + prize)
else:
print('没有奖品啦!')
主要函数功能介绍如下: 函数make_pool(prz_list):根据奖品清单prz_list,创建一个奖品池,并作为函数的返回值。 函数lottery(prizes):实现随机抽奖,并且删除已抽取的奖品,更新奖品池。 函数append_prize(prizes):向奖品池添加奖品,并对奖品池进行随机排列。 函数print_prize(prizes):实现奖品池中前10项奖品的打印输出。 重要语句如下: 语句①通过双重循环实现奖品池的创建。 语句②随机排列奖品。 语句③实现随机抽取奖品。 语句④把已抽奖的奖品移除奖品池,实现奖品池的更新。 语句⑤添加到奖品池中。
模块big_prize.py程序源代码如下:
"""
big_prize.py : 大奖在招手
"""
from common.prizes import * # ①
prize_list = [('3D 眼镜', 20), ('AI 音箱', 30), ('数字录音笔', 20), ('迷你投影仪', 20), ('手机自拍杆', 10)] # ②
def main():
prizes = make_pool(prize_list) # ③
while True:
cmd = input("L - 开始抽奖, A - 补充奖品, P - 查询奖品, Q - 退出程序: ")
if cmd.lower() == 'q':
break
elif cmd.lower() == 'l':
prizes = lottery(prizes) # ④
elif cmd.lower() == 'a':
prizes = append_prize(prizes) # ⑤
elif cmd.lower() == 'p':
print_prize(prizes) # ⑥
if __name__ == '__main__':
main()
重要语句说明如下: 语句①导入抽奖程序的基础模块prizes.py的所有函数,主要这个模块是存放common包中的。 语句②定义一个奖品清单的列表,包括每种奖品名称和数量。我们后续将使用它创建初始的奖品池。 语句③根据奖品清单,创建奖品池。 语句④开始抽奖。 语句⑤追加奖品。 语句⑥打印前个10奖品
执行效果
4.1 程序目录结构
D:\cases\大奖在招手>dir
2022/12/09 13:11 784 big_prize.py
2022/12/09 13:12 <DIR> common
D:\cases\大奖在招手>
D:\cases\大奖在招手\common> cd common
D:\cases\大奖在招手\common>
D:\cases\大奖在招手\common>dir
2022/12/09 13:12 1,196 prizes.py
2022/04/01 07:34 106 __init__.py
D:\cases\大奖在招手\common>
因为在子目录common中定义了一个__init__.py文件,所以common是Python的一个包。
4.2 程序执行效果
D:\cases\大奖在招手>python big_prize.py
L - 开始抽奖, A - 补充奖品, P - 查询奖品, Q - 退出程序: l
恭喜中奖:数字录音笔
L - 开始抽奖, A - 补充奖品, P - 查询奖品, Q - 退出程序: l
恭喜中奖:3D 眼镜
L - 开始抽奖, A - 补充奖品, P - 查询奖品, Q - 退出程序: p
1 AI 音箱
2 迷你投影仪
3 迷你投影仪
4 3D 眼镜
5 手机自拍杆
6 AI 音箱
7 3D 眼镜
8 3D 眼镜
9 AI 音箱
10 迷你投影仪
L - 开始抽奖, A - 补充奖品, P - 查询奖品, Q - 退出程序: q
D:\cases\大奖在招手>
程序优化
截至目前,商场抽奖程序实现了设计目标,但程序执行不太方便,程序处理逻辑有待优化: 首先,程序每次启动,均要重新创建奖品池,与实际场景不符。 其次,程序正常结束或退出时,奖品池的内容丢失; 再其次,将初始的奖品清单以文本文件形式存放,相对于现在的列表常量prize_list存放来说,实现了程序和数据的分离,增强了程序的灵活性。我们可以使用诸如Windows记事本这样的编辑程序,就可以修改奖品及数量,而不用修改程序big_prize.py,就能够实现初始化奖品池的内容。 最后,目前这一版程序是基于字符界面,程序界面不够美观。 第一个问题解决办法很简单,就是把big_prize.py中的语句③移入到while循环中,作为一个单独的功能选项,这样就可以根据需要,选择性地创建奖品池。 第二个问题的解决方法是在退出程序之前,把奖品池列表的内容保存到文件中,而在程序启动时读入文件保存的内容,将数据恢复到奖品池列表中。这里可以有多种方案可供选择,除了你可以使用文本文件保存奖品信息外,还有以下三个方案: 方案一是使用Python 的shelve模块。 使用Python的shelve模块,它可以将Python支持的变量,特别是各种对象变量例如列表、字典等写入到磁盘上的一个二进制文件中,以及提供逆向过程的功能,你的程序也可以从磁盘文件中快速恢复变量的数据。 方案二是使用Python 的json模块。 通过使用Python的json模块,可以非常简单地对程序中的变量进行读和写操作;它是使用JSON格式的文件存放奖品池的数据,JSON格式的文件特别适合于在不同的编程语言之间进行数据交换处理,例如前端程序和后台程序之间交换数据。 方案三是使用sqlite3数据库。 你完全可以选择使用数据库的存储方。Python自带一个轻量级的数据库sqlite3,使用方便快捷,应用范围广泛。我们常常能在嵌入式编程和浏览器中看到sqlite3的应用场景,我个人认为在本案例中使用数据库管理系统,有点大材小用,“杀鸡焉用牛刀”。 综上所述,我们建议使用Python的标准模块shelve,它可以简单优雅地将Python的数据结构存入文件,并且在你需要时候快捷地从文件中加载这些数据。 第三个问题的解决方案已经很明确了,我们需要新增一个函数,实现从文本文件或者shelve文件中读入奖品及数量,以此动态地创建奖品列表prize_list。需要注意的是每一个列表元素就是一个元组,包含奖品名称和奖品数量。 至于最后一个问题,Python提供了多种图形界面程序开发的解决方案。这些方案包括: Tkinter,wxPython和PyQt。其中Tkinter 是Python官方提供,轻量级的GUI开发工具。PyQt,wxPython 功能很强大。它们为快速开发桌面图形界面应用程序提供了强大的工具支持。结合本案例的具体情况,我们建议是使用Python原生图形开发库tkinter,对模块big_prize.py进行修改,实现图形应用程序界面的开发,有兴趣的读者不妨一试,你一定会有全新的收获。
场景扩展
在案例中实现的商场抽奖程序还适用于:企业/公司春晚演唱现场抽奖、各种团建活动的抽奖应用场景。