こんにちは、igaです。
先日の連休で、あるコンテンツの聖地巡礼をして英気を養ってきました!
英気を養ったところで、「Node.jsからPythonにソースコードを移植する」ということが必要になりました。
元のNode.jsのコードでPythonには存在しない書き方をしていて、そのままPythonに書き直すのが難しいため、ChatGPTに助けてもらって移植を行ってみよう、と考えました。
今回のポイント
変換にあたって、Node.jsで変数の値をインクリメントする「index++」という記述が、Pythonには存在しません。
同じように変数の値をインクリメントする場合、Pythonでは「index += 1」という記述にする必要があります。
それで今回のソースコードですが、関数の引数を指定するところでインクリメントの、しかもやや複雑な記述が存在していました。
num = this.#translateInt16(this.#hexArr[index++], this.#hexArr[index++]);
上記のような実装で、「translateInt16」メソッドの引数に、「hexArr[index++]」という配列の変数が指定されています。
さらに、1行で2つも「index++」というインクリメントの指定があって、実際にどのような値が渡されるのか、分かるでしょうか?
これは、人間がぱっと考えても難しいですよね?
#そもそも、このような難解な実装をするな、と思われる人も多いと思いますが、私もそう思いました(笑)
移植するNode.jsのソースコード
冒頭のコードを含むNode.jsの移植ソースコードは、以下のような内容です。
(解説するために内容は単純化しています。そのため、変換処理でのエラーチェックなども省略しています。)
const MESSAGE_HEADER = 0x80; const MESSAGE_STATUS = 0x01 const MESSAGE_VALUE = 0x02; class MessageData { // Message Status status = 0; // Message Value value = 0; }; class MessageDataParser { // input data is hex string #payload = ''; #arrLength = 0; // translated hex arry from input string #hexArr = []; // Constructor constructor(payload) { this.#payload = payload; } // translate lettle-endian #translateInt16(a, b) { return a + (b << 8); } #parseMessage(index, messageData) { switch(this.#hexArr[index++]){ case MESSAGE_STATUS: messageData.status = this.#translateInt16(this.#hexArr[index++], this.#hexArr[index++]); break; case MESSAGE_VALUE: messageData.value = this.#translateInt16(this.#hexArr[index++], this.#hexArr[index++]); break; default: // ignore unknown message. index += 2; break; } return index; } parse() { let msg = new MessageData(); let arrayIndex = 0; // for debug // console.info('Parse payload.: payload=' + this.#payload); const payloadLength = this.#payload.length; // parse hex string to array this.#arrLength = payloadLength / 2; for (let i = 0; i < this.#arrLength; i++) { this.#hexArr.push(parseInt(this.#payload.substring(i * 2, i * 2 + 2), 16)); //parse hex } if (this.#hexArr[arrayIndex++] == MESSAGE_HEADER) { do { arrayIndex = this.#parseMessage(arrayIndex, msg); }while(arrayIndex < this.#arrLength) } return msg; } } module.exports = MessageDataParser
変換元のソースコードは、メッセージのデシリアライズを行うものになります。
今回デシリアライズする、メッセージの仕様は以下の通りです。

ChatGPTにNode.jsからPythonに変換してもらう
以下のプロンプトで、ChatGPT(今回は、GPT-4を使用しました)にNode.jsからPythonに変換してもらいます。
ChatGPTが変換してくれた結果が、以下のコードになります。
class MessageData: def __init__(self): self.status = 0 self.value = 0 class MessageDataParser: MESSAGE_HEADER = 0x80 MESSAGE_STATUS = 0x01 MESSAGE_VALUE = 0x02 def __init__(self, payload): self._payload = payload self._hex_arr = [] self._arr_length = 0 def _translate_int16(self, a, b): return a + (b << 8) def _parse_message(self, index, message_data): code = self._hex_arr[index] index += 1 if code == self.MESSAGE_STATUS: message_data.status = self._translate_int16(self._hex_arr[index], self._hex_arr[index + 1]) index += 2 elif code == self.MESSAGE_VALUE: message_data.value = self._translate_int16(self._hex_arr[index], self._hex_arr[index + 1]) index += 2 else: # ignore unknown message. index += 2 return index def parse(self): msg = MessageData() array_index = 0 self._arr_length = len(self._payload) // 2 for i in range(self._arr_length): self._hex_arr.append(int(self._payload[i * 2:i * 2 + 2], 16)) # parse hex if self._hex_arr[array_index] == self.MESSAGE_HEADER: array_index += 1 while array_index < self._arr_length: array_index = self._parse_message(array_index, msg) return msg
変換結果
さて冒頭でも説明したインクリメントの記述部分はどうなったでしょうか?
Node.jsでの記述:
messageData.status = this.#translateInt16(this.#hexArr[index++], this.#hexArr[index++]);
- 1つ目の引数にはhexArr[index]の値を渡す
- 2つ目の引数にはhexArr[index+1]を渡す
が変換後も再現されていれば成功です。
また関数の呼び出しが行われた後は、indexの値が2増加しているはずです。
Pythonでの記述:
message_data.status = self._translate_int16(self._hex_arr[index], self._hex_arr[index + 1]) index += 2
Node.jsでの処理内容と同じことがPythonに変換した結果に反映されています。
インクリメントの記述を、単純に「index += 1」で置き換えるのではなく、Node.jsで記述された意味と同じになるようになっています。
- 1つ目の引数に_hex_arr[index]の値を渡す
- 2つ目の引数に_hex_arr[index+1]を渡す
そして、関数の呼び出しを行った後でindexの値を2増加させるため「index += 2」と変換しています。
完璧ですね…!
変換されたコードの動作確認
変換した処理の内容は正しそうですが、念のためPythonコードが正しく動くのか、pytestでテストコードを書いて確認します。
import pytest from src.dataparser import MessageDataParser def test_parse_status_only(): parser = MessageDataParser('80010203') data = parser.parse() assert data.status == 0x302 assert data.value == 0 def test_parse_value_only(): parser = MessageDataParser('80020203') data = parser.parse() assert data.status == 0 assert data.value == 0x302 def test_parse_unknown_code(): parser = MessageDataParser('80030203') data = parser.parse() assert data.status == 0 assert data.value == 0 def test_parse_status_and_value(): parser = MessageDataParser('80010203020204') data = parser.parse() assert data.status == 0x302 assert data.value == 0x402 def test_parse_no_header(): parser = MessageDataParser('010203') data = parser.parse() assert data.status == 0 assert data.value == 0 def test_parse_skip_unknown(): parser = MessageDataParser('80010203040706020204') data = parser.parse() assert data.status == 0x302 assert data.value == 0x402
テストを実行するとすべてPASSEDになり、正常に動作することが確認できました。

まとめ
今回、ChatGPTにプログラム言語の変換をやってもらいましたが、変換先の言語で書けない処理であっても適切に変換してくれました。
単純に変換元と変換先で同じ記述が使えない場合に、対応する記述に置き換えるのではなく、記述した意味を把握して変換を行っているように見えます。
これはかなり強力なので、プログラムの移植にChatGPTを活用するのは有用だと思います。