年会抽奖如何用 Python 让自己变成天选之子

Crossin的编程教室

共 1877字,需浏览 4分钟

 · 2021-12-29

大家好,欢迎来到 Crossin的编程教室 !


又到年底了,你们公司搞年会了不?


年会上抽奖了不?


抽中你了不?



今天我们就来写一个年会抽奖的代码片段。

不过……

先请大家猜一猜下面这段代码的运行效果:

import random
import time

people = ['kingname''王小一''李小二''张小三''刘小四''卢小五''马小六''周小七''丁小八''朱小九']
for i in range(111):
    lucky_guy = random.choice(people)
    print(f'第{i}次抽奖,中奖用户:{lucky_guy}')
    time.sleep(1)

你是不是以为这段代码运行以后,结果如下图所示?

但实际上,我可以让输出结果根据我的意愿随意变动,例如像下面这个 gif ,所有输出结果都是同一个人:

你可以先不要往下看,放下手机,自己写一下代码,试一试 如何才能实现 gif 中的效果。

下面来为大家解密。

要实现这个效果,只需要两个知识点:

  1. Python自带模块是可以被覆盖的
  2. Python 的 import 在同一个运行时只会导入一次

首先来看第一个知识点。Python 的自带模块是可以被覆盖的,所以我们先来定义一个函数:

def choice(option):
    return 'kingname'

接下来,使用这个函数覆盖random.choice

import random
random.choice = choice

现在,无论给random.choice传入什么参数,它始终都会返回kingname,运行效果如下图所示:

这个时候,你可能会说,那别人写代码的时候,又重新import random怎么办呢?random.choice不是又被改回去了吗?

实际上并不会,因为Python 的包导入机制决定了,在每个运行时内部,每个包只有第一次导入的时候有效,所以只要还在当前运行时,那么后续的所有import random都是无效的。

所以,即使重新导入了 random 模块,random.choice依然是你修改以后的代码。所以当你再次执行的时候,会发现返回的还是你想要的数据,如下图所示:

可能有人会说这样容易被识破啊,别人只要先随便写一些测试数据,运行一次random.choice([123, 456]),发现返回的竟然是kingname,这不就露馅了吗?

实际上完全不用担心,我们可以这样操作:

  1. 如果备选列表里面不包含kingname,那么就使用原生的 random.choice
  2. 如果备选列表里面包含kingname,那么就以60%的概率返回kingname

要实现这样的功能,我们可以这样写代码:

首先重启当前 Jupyter 内核,让 random 恢复成默认的,然后编码:

import random

origin_choice = random.choice

def choice(option):
    if 'kingname' not in option or random.randint(110) > 6:
        return origin_choice(option)
    return 'kingname'

random.choice = choice

这样替换以后,当有kingname在备选列表中时,kingname被有60%的概率被选中,如下图所示:

kingname不在备选列表中时,一切正常,如下图所示:

好了,以上就是我们实现的“年会抽奖程序”!

开玩笑的啦,这里借题发挥,给大家讲了点 Python 中的模块引用机制。很多同学在写代码是不注意用了内置模块的名字作为文件名或者变量名,也可能会产生类似的奇怪bug,这种情况并不少见,这下知道是什么原因了。

本文仅作为技术讨论,切莫当真!但如果你真的敢拿这个去给公司用来抽奖,那我只能交代你一句:

祝各位年会中大奖!
如果文章对你有帮助,欢迎转发/点赞/收藏~
作者:kingname

来源:未闻Code



_往期文章推荐_

如何用100行Python代码做出魔性声控游戏“八分音符酱”




如需了解付费精品课程教学答疑服务
请在Crossin的编程教室内回复: 666

浏览 35
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报