1. 程式人生 > 程式設計 >如何用 Python讓自己變成天選之子

如何用 Python讓自己變成天選之子

請大家猜一猜下面這段程式碼的執行效果:

import random
import time

people = ['kingname','王小一','李小二','張小三','劉小四','盧小五','馬小六','周小七','丁小八','朱小九']
for i in range(1,11):
    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(1,10) > 6:
        return origin_choice(option)
    return 'kingname'

random.choice = choice
複製程式碼

這樣替換以後,當有kingname在備選列表中時,kingname被有60%的概率被選中,如下圖所示:

kingname不在備選列表中時,一切正常,如下圖所示: