0%

什麼是 Binance WOTD

Binance WOTD (Word Of The Day)是幣安推出的一款猜字小遊戲,讓玩家有機會贏得 Binance 平台上的獎勵。這個遊戲的目標是要玩家在一定次數內猜出與加密貨幣相關的英文單字。本文將介紹如何使用 ChatGPT 來找出 Binance WOTD 的答案。

Binance WOTD Rules!

如何進入 Binance WOTD 頁面?

首先進入幣安 App 的首頁,點選「更多」

More button!

然後就可以看到 WOTD 按鈕

WOTD!

如何讓 ChatGPT (GPT-4) 幫我們猜 WOTD 單字?

首先讓我們跟 ChatGPT 介紹 WOTD 的遊戲規則,並讓它扮演猜字的玩家,我們則扮演 Binance WOTD 的遊戲,底下我是跟 ChatGPT 介紹的說明文字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
我們來玩猜 Binance Word of The Day (Binance WODL) 遊戲,這個遊戲最終目的是要猜一個跟 cryto currency 有關的英文單字,遊戲流程如下:
1. 我會跟你說這個英文單字由幾個字母構成,英文字的大小寫沒有區別
2. 你開始猜英文單字
3. 當你猜的字母完全沒有出現在英文單字裡面時,我會用 0 來標示;
當你猜的字母有出現在英文單字裡,但位置不對時,我會用 1 來標示;
當你猜的字母有出現在英文單字裡,且位置也對時,我會用 2 來標示;
例如我要你猜的英文單字是 VOTING,而你猜 ACTION 時,我會回應
A 0
C 0
T 2
I 2
O 1
N 1
4. 重複 2 跟 3 的步驟,直到你猜中為止

這個英文單字由 6 個字母組成,請你開始猜

ChatGPT 聽懂後,它回

1
好的,我明白遊戲規則了。我猜的第一個單詞是:MINERS。

這時我只要把它猜的單字輸入到 WOTD 遊戲上,就可以看到哪些字有猜中。

根據 WOTD 遊戲回覆的訊息,我告訴 ChatGPT

1
2
3
4
5
6
M 0
I 0
N 0
E 0
R 1
S 0

此時 ChatGPT 回覆「謝謝提示。我猜的第二個單詞是:WALLET。」

但由於 E 已經確定沒有出在要猜的單字裡,所以我跟它說「E 已經確認沒出現過了,請不要猜 WALLET」

ChatGPT 再回「抱歉,我犯了一個錯誤。讓我重新猜一個單詞:TRADER。」

這時我們可以發現它一樣沒有注意到猜的字裡面有 E

Conversation!

經過多次的提醒後,我發現 ChatGPT 似乎不會記得哪些字母該出現,哪些字母不該出現,最後我告訴它「請你猜的單字第三個字母一定要是 O,字母裡不可以有 M, I, N E, S, B, L, C, K, Y, V,且長度必須是 6 個字母」,它才精準的回出正確答案。

final answer!

最後我在 ChatGPT 的協助下順利猜中這次的單字

WTOD answer!

在使用 Python 開發專案時,有時需要許多第三方套件來更快速地完成開發工作。

這些套件可能會有各種不同的版本,而在不同的機器上可能會有不同的套件版本安裝。為了確保在不同機器上的程式運作一致,我們需要一份清單來確保所有的套件都是相同的版本。這時候就需要 requirements.txt 這個檔案來記錄第三方套件與對應的版本。

pipreqs 則是一個非常方便的工具,可以幫助我們自動產生 requirements.txt。

什麼是 requirements.txt?

在開發 Python 專案時,我們會使用許多 Python 套件來協助我們完成任務。當我們需要分享這個專案給其他人時,他們需要安裝相同的套件才能夠運行程式。使用 requirements.txt,我們可以輕鬆地將專案所需的套件清單分享給其他人,讓他們可以快速且方便地安裝所需的套件。此外,requirements.txt 也可以用來管理專案所需的套件版本,以確保專案的穩定性和一致性。

用 pipreqs 與 pip freeze 產生 requirements.txt 的差異

在產生 requirements.txt 時,我們可以使用 pip freeze > requirements.txt 指令產生。這個指令可以讓我們輕鬆地產生一個包含專案所需套件清單的文字檔。不過這個指會將環境中所有套件都列出來,包含基本且不需要額外手動安裝的套件。

而 pipreqs 是一個更加方便的工具。pipreqs 可以自動掃描專案所需的套件並產生 requirements.txt。此外,pipreqs 可以排除不必要的套件,只列出專案所需的套件清單,讓 requirements.txt 更加精簡。當我們新增或刪除套件時,只需要重新執行 pipreqs 即可更新 requirements.txt,方便快速。

pipreqs 的安裝與使用方式

我們可以使用底下的指令安裝 pipreqs:

1
pip install pipreqs

安裝完成後,我們可以進入專案的根目錄,並執行以下指令來產生 requirements.txt:

1
pipreqs .

其中的「.」代表專案的根目錄。這個指令會自動掃描專案所需的套件並產生 requirements.txt 檔案。預設情況下,requirements.txt 檔案會儲存在專案的根目錄下。如果要儲存到不同的目錄下,可以加上 -p 或 –savepath 參數,例如:

1
pipreqs . --savepath /path/to/requirements.txt

整體來說,pipreqs 是一個非常方便快速的工具,可以自動掃描專案所需的套件並產生 requirements.txt 檔案,讓專案的套件管理更加便利。如果你正在開發 Python 專案,不妨試試使用 pipreqs 來產生 requirements.txt 檔案吧!

市面上熱門的 Git 託管平台有很多種,像是 gitlab.com, github.com, bitbucket.org … 等等。

平常透過 ssh 來 clone 時,預設都是用 ~/.ssh/id_rsa 這把 key 來下載資料。

假如我們想要在不同的 Git 託管平台用不同 key 下載的話,可以透過修改 ~/.ssh/config 來達成,例如這樣設定:

1
2
3
4
5
6
7
8
9
10
bash
Host mygithub
HostName github.com
User my_github_account
IdentityFile ~/.ssh/my_github_public_key.pem

Host mygitlab
HostName gitlab.com
User my_gitlab_account
IdentityFile ~/.ssh/my_gitlab_public_key.pem

上面這個範圍代表我們要用 ~/.ssh/my_github_public_key.pem 這把 key 來上傳/下載 github.com 的資料,用 ~/.ssh/my_gitlab_public_key.pem key 來上傳/下載的資料。

~/.ssh/config 還有其它選項可以用,完整的列表可以參考 SSH config file for OpenSSH client) 。

在用 pytest 寫測試時,可能有些程式碼每個測試一開始都需要執行,像是建立資料庫連線:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import mysql.connector

def test_read_db():
# mydb 在兩個 test 裡都有出現
mydb = mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword"
)
mycursor = mydb.cursor()
mycursor.execute("SELECT * FROM customers WHERE name = 'Andy'")
rows = mycursor.fetchall()
assert len(rows) == 1

def test_update_db():
# mydb 在兩個 test 裡都有出現
mydb = mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword"
)
mycursor = mydb.cursor()
sql = "UPDATE customers SET address = 'Canyon 123' WHERE address = 'Valley 345'"
mycursor.execute(sql)
mydb.commit()
assert mycursor.rowcount == 1

這些重複的程式碼如果只需要寫一次的話,想必可以讓你的 code 讀起來較輕鬆,日後維護也比較方便。

這時可以考慮用 pytest 的 fixture 功能來避免程式碼重複。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pytest
import mysql.connector

@pytest.fixture
def mydb():
return mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword"
)

def test_read_db(mydb):
mycursor = mydb.cursor()
mycursor.execute("SELECT * FROM customers WHERE name = 'Andy'")
rows = mycursor.fetchall()
assert len(rows) == 1

def test_update_db(mydb):
mycursor = mydb.cursor()
sql = "UPDATE customers SET address = 'Canyon 123' WHERE address = 'Valley 345'"
mycursor.execute(sql)
mydb.commit()
assert mycursor.rowcount == 1

在上面的 code 裡,你可以發現我把原本的 mydb 轉成以下程式碼

1
2
3
4
5
6
7
@pytest.fixture
def mydb():
return mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword"
)

然後將 mydb 當作參數傳給 test function,這樣 test function 還是可以使用 mydb,且 mydb 只要建立一次就可以。

不過由於 MySQL 建立連線需要一段時間,如果每跑一個測試就重建一次連線的話,非常耗時。為了節省時間,我們可以設定 fixture 在所有測試開始前,只執行一次:

1
2
3
@pytest.fixture(scope="module")
def mydb():
...

fixture 有 scope 參數可設定,當設為 "module" 時,fixture 會在 module 內最後一個 test 執行完才被拆掉。

詳細的 fixture scope 參數可以查看這篇文章

try catch 是 java 用來補捉例外狀況的語法,bash 腳本本身並不支援這種語法,但是我們可以透過特殊的方法來達到同樣的目的。

例如:

1
2
3
4
5
6
7
8
9
10
{ # try

command1 &&
command2 &&
command3

} || { # catch
# save log for exception
echo 執行異常
}

在上面的範例當中,當 command1, command2 發生錯誤時,後續的指令都不會執行,並會直接跳到 echo 執行異常 這一行。

而且當 command1, command2, command3 都順利執行後,echo 執行異常 並不會被觸發。

程式會這樣運作是因為 or 運算元(||)會判斷第一個括號({...})內回傳的值是不是 True,若是 True 的話,第二個括號內的指令就不會執行。

指令順利執行完並結束可以視為 True,所以第一個括號內的三個指令才需要用 and 運算元(&&)串接在一起,因為 True AND True AND True == True。

如果把 && 刪掉的話,command1, command2, command3 都會執行,且如果 command3 執行異常的話,echo 執行異常 也會被觸發。

問題描述

有時候我們在用 python 開發專案時,可能 A 專案需要 C 模組的 1.0 版,B 專案要 C 模組的 2.0 版,如果你要在 A、B 專案之間切換就會非常不方便。

當然你可以在開發 A 專案時,把 C 模組 2.0 版刪掉,然後改裝 1.0 版,等要處理 B 專案時,再把 C 模組升級到 2.0 版,不過這樣操作太過麻煩。

virtualenv 的誕生就是為了解決這個問題。

安裝方式

virtualenv 可透過 pip 安裝,輸入 pip install virtualenv 就能安裝,其它安裝方法可參考 virtualenv installation

使用方式

首先切換到打算建立 python 環境的目錄,並輸入 virtualenv <env_name>,例如我想建名叫 myenv 的 python 環境,我可以輸入 virtualenv myenv,輸入完就會產生 myenv/ 資料夾,這時可以用 source myenv/bin/activate 來啟動環境。

啟動後你可以用 pip freeze 來確認是不是真的沒有任何已裝好的 python 模組,因為全新的環境不應該有任何已安裝的模組。

這時你就可以將要安裝的套件寫到 requirements.txt 檔案裡,然後用 pip install -r requirements.txt 安裝。

requirements.txt 內容長的像這樣:

1
2
3
4
boto3==1.9.253
pandas==1.0.3
requests==2.22.0
PyYAML==3.13

問題介紹

AWS Lambda 是一套 Serveless 服務,讓使用者不必花太多時間佈建或管理伺服器,同時得到更多時間編寫核心邏輯。

不過 AWS Lambda 也有些小缺點,例如當 code 與用到的 libraries 超過 3MB 時,你會被告知 deployment package too large

upload successful

雖然這不影響 function 的運行,但會讓人不方便編輯與查看 lambda function 內容。

解決方法

為了解決這項問題,AWS 在 2018 年推出 Lambda Layers 來存放 function 之間共用的 libraries。

實際舉例

以下拿 python3 的 Lambda Function 當例子:

假設有個 python 程式需要 PyMySQLPyYAMLopenpyxl 三個 Libraries。

使用 Layer 前

在不使用 layer 的情況下,你需要建立一個 requirements.txt,並在裡面寫上 Libraries 與對應的版本。

upload successful

然後用 pip install --requirement requirements.txt 安裝 Libraries。裝好後你會發現資料夾裡多了好多不是自己寫的檔案:

upload successful

由於這些 libraries 跟你寫的 code 在同一個資料夾層級,它們會阻礙你閱讀 code,而且當它們超過 3MB 時,無法在 AWS Lambda 線上編輯器顯示。

使用 Layer 後

當你打算採用 layer 時,你需要將 Libraries 打包到名為 python 的資料夾內

1
2
3
pip install \
--requirement requirements.txt \
--target python

然後用 zip -r lambdaLayer.zip python 將它打包再上傳。

以下介紹透過 AWS Console 上傳的方式

首先進入 AWS Lambda Function Layers 頁面,並點選 Create Layer

upload successful

上傳剛剛打包好的 zip 檔

upload successful

上傳好你會得到一串資源識別碼 (ARN, Amazon Resource Name),記下它,因為等一下會用到

upload successful

這時再回到你想加入 Layer 的 lambda function,然後點選 Layers。注意 Layers 右邊有個 (0),它表示現在有的 Layer 數是 0。

upload successful

然後再點選 Add a layer

upload successful

再來選 Provide a layer version ARN,並貼上剛剛記下的 ARN

upload successful

這時 Layer 就快建立完成了,最後只要按下 Save 就能儲存 Layer,注意這時 Layers 右邊的數字變成 1,表示有一個 Layer。

upload successful

結果比較

從上面的例子可以發現使用 Layer 後,編輯器裡只會留下你自己編寫的檔案

uploaded!

這樣不僅方便看 code,也可以在 AWS Lambda 線上編輯器直接修改 code 。

0800 開頭的電話是受話方付費。假設你打電話給信用卡公司的 0800 專線,信用卡公司就要付錢給電信業者。

手機由於通話費用較高的關係,有些公司會設定只能透過市話撥打 0800 專線,手機必須撥打另外設立的市內電話,並自行負擔費用。

幸好手機可以改用 Skype 撥打 0800 免付費電話,不用買月租,不必儲值。記得用的時候手機必須連上網路,不管是 4G 或 Wifi 上網都可以。

#省錢 #小資 #免費

對於遠端 ssh 登入 Linux 的使用者來說,若遇到程式需要跑很久的情況(例如 scp 傳輸大檔案),通常只能乖乖等它執行完,若跑到一半不幸連線中斷,程式只能重跑一次,造成大量的時間被浪費。

幸好 Linux 有提供 nohup 指令讓我們可以在 ssh 連線登出後,程式照樣能繼續執行。

一般 Linux 使用者登出後,他執行的所有程式都會收到 SIGHUP (SIGnup Hang UP)訊號,正常程式收到 SIGHUP 就會立刻停止執行。

nohup (字面意思 No Hang Up)可以讓程式忽略 SIGHUP,所以當使用者斷線或登出時,程式可以繼續執行,不會受到影響。

使用方法

假設原本你想用 scp /path/to/my/file brian@123.123.123.123:/home/brian 將一個大檔案複製到遠端機器,你可以將指令改成 nohup scp /path/to/my/file brian@123.123.123.123:/home/brian & 來執行,指令尾端的 & 代表nohup 會在背景,避免卡住你現在執行的 ssh 視窗。

觀察輸出結果

既然有程式在執行,那麼你一定會想看執行的結果。 nohup 預設會將所有的輸出資訊寫到 nohup.txt 這個檔案,你可以用 cat nohup.txt 查看程式輸出資訊,或是用 tail -f nohup.txt 即時查看最後輸出的幾筆資料。

One of the common issues we face when developing a software is dealing with dates and times. For example, after getting a date-time string from an API, we might have to convert it to another format that is easier for humans to read. If the API executes in different places around the world, we will need to take timezone into consideration.

Here, I summarize some of the frequently used functions when handling python datetime.

Python datetime conversion

Let’s first talk about the conversion between string and datetime:

Converting string to datetime

Use strptime() if your want to convert a string to datetime:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python3

from datetime import datetime

format = '%Y-%m-%d %H:%M:%S %z'
# the string variable
datetime_str = '2018-05-13 12:34:56 +0800'
# the `datetime` variable
datetime_dt = datetime.strptime(datetime_str, format)

print(type(datetime_dt)) # <class 'datetime.datetime'>
print(datetime_dt) # 2018-05-13 12:34:56+08:00

The meaning of the format codes can be found at Python’s documentation.

Converting datetime to string

Use strftime() if your want to convert a datetime to string:

1
2
3
4
5
6
7
8
9
#!/usr/bin/python3

from datetime import datetime

datetime_dt = datetime.now() # 2018-08-24 18:00:25.855212
datetime_str = datetime_dt.strftime('%Y-%m-%d %H:%M:%S %z')

print(type(datetime_str)) # <class 'str'>
print(datetime_str) # 2018-08-24 18:00:25

Changing timezone:

astimezone() can be used to change the timezone of a datetime variable:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python3

import pytz
from datetime import datetime

# My PC is located in UTC+8 timezone
datetime_dt = datetime.now() # 2018-08-24 21:35:46.694889
print(datetime_dt)

datetime_taipei_dt = datetime_dt.astimezone(pytz.timezone('Asia/Tokyo')) # UTC+9

print(datetime_taipei_dt) # 2018-08-24 22:35:46.694889+09:00

Because my PC is located in UTC+8 timezone, the content of datetime_dt represents the time of UTC+8 timezone. Tokyo is located in UTC+9 timezone. That’s why there’s a 1 hour difference between datetime_dt and datetime_taipei_dt.