LoginSignup
4
6

More than 1 year has passed since last update.

実践ChatGPT:本番環境で利用できるコード生成の基本

Last updated at Posted at 2023-05-07

本番環境で利用できるコードをChatGPTで利用する場合の例です。
本番環境で利用するコードはただ動くだけではなく、①再利用できて、②エラー処理もあり、③コメントもされていて、④ユニットテストもされて、⑤ドキュメントされていることが最低条件になります。ChatGPTを上手に使えば、この条件を満たすコードを生成させ、コーディング作業の効率を上げることが可能になります。

ChatGPTが生成できるコードの長さには制限がある。長いコードを生成する場合は関数に分割するか、回答を分けるように指示する。推奨は短い関数に分割することである。その方がユニットテストも生成して、テストの実行もできる。

ChatGPTでコードを生成する場合は次を行う。

  1. 機能を分割して、短いコードを生成するようにする
  2. 質問をテンプレート化して、テンプレートを改善する
  3. 生成するコードの言語、フレームワーク、アルゴリズムを具体的に指定する
  4. 言語毎及びフレームワーク毎にテンプレートを作成する
  5. モジュールを利用する場合は、具体的に利用するモジュールや関数を制限する
  6. 入力を明示的に指定する。例えば引数の日付配列、日付形式、日付配列のタイムゾーン、ソート順
  7. 例を提供する。例えば「['2023-05-07','2022-05-07'.'2024-05-07'」をソートする。
  8. 条件を明示する。例えば「引数で指定された日付形式のPython配列を降順にソートする関数を生成してください。」
  9. エラー処理も指定する。引数が正しくない場合はNoneを返す
  10. 出力に含むものを指定する。例えば、コードにコメントを含める
  11. 回答を実際に実行して正しいか確認する。時々、エラーになる場合もある
  12. 生成されたコードを「エレガント」(優雅)にする
  13. ユニットテストを生成する
  14. ドキュメントを生成する

注意点:生成されたコードが数回間違えがある場合は最初からやり直す。継続して質問すると同じ回答をループする傾向がある。

コード生成の生成

テンプレートを作成する例です。

1. 単純に機能を説明する。本例ではpythonでソートする関数を作成する

プロンプト:python配列をソートする関数を生成してください。

def sort_array(arr):
    sorted_arr = sorted(arr)
    return sorted_arr

正しいコードですが、実際のプログラムではソートするオブジェクト型によりソートに仕方が異なるために詳細条件を指定するようにします。

2. ソートする内容を具体的に指定する。本例では日付をソートする。

プロンプト:yyyy-mm-dd hh:MM:ss形式の日付型python配列を降順にソートする関数を生成してください。

from datetime import datetime

def sort_dates_descending(dates):
    # convert dates to datetime objects
    datetime_dates = [datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S') for date_str in dates]
    # sort datetime objects in descending order
    sorted_dates = sorted(datetime_dates, reverse=True)
    # convert datetime objects back to date strings
    sorted_date_strings = [datetime.strftime(date_obj, '%Y-%m-%d %H:%M:%S') for date_obj in sorted_dates]
    return sorted_date_strings

上記のコードも正しいコードですが、異なる形式の日付もソートできるように日付形式も引数で渡すようにします。

3. 入力を具体的に指定する。本例では日付配列と日付形式を指定する。

プロンプト:引数で指定された日付形式のpython配列を降順にソートする関数を生成してください。

from datetime import datetime

def sort_dates(dates, date_format):
    sorted_dates = sorted(dates, key=lambda x: datetime.strptime(x, date_format), reverse=True)
    return sorted_dates

正常系処理としては正しいのですが、実際のプログラムではエラー処理も含める必要があるため、エラー処理も指定します。

4. エラー処理を指定する。

プロンプト:引数で指定された日付形式のpython配列をソートする関数を生成してください。引数は日付配列、日付形式、引数日付のタイムゾーン、ソート順です。引数が正しいかのチェックも含めてください。エラーの場合はNoneを返します。正しい場合はローカルタイムゾーンに変換した日付配列を返します。

from datetime import datetime
import pytz

def sort_dates(dates, date_format, timezone, sort_order):
    # 引数が正しいかどうかをチェック
    if not dates or not date_format or not timezone or not sort_order:
        return None

    # タイムゾーンを設定
    try:
        tz = pytz.timezone(timezone)
    except pytz.exceptions.UnknownTimeZoneError:
        return None

    # 日付をパースしてローカルタイムゾーンに変換
    local_dates = []
    for date in dates:
        try:
            parsed_date = datetime.strptime(date, date_format)
            local_date = tz.localize(parsed_date)
            local_dates.append(local_date)
        except ValueError:
            return None

    # ソート
    if sort_order.lower() == "ascending":
        sorted_dates = sorted(local_dates)
    elif sort_order.lower() == "descending":
        sorted_dates = sorted(local_dates, reverse=True)
    else:
        return None

    return sorted_dates

comprehensionを利用してpython風に書き換えます。また関数、引数、コード内のコメントも記述するようにします。

5. 利用する機能を明示的に指定する。また出力されたい内容を明示的に指定する。

本例ではPython comprehensionを利用するようにし、出力にコメントを含むようにする。
プロンプト:引数で指定された日時形式のpython配列をソートする関数を生成してください。引数は日付配列、日付形式、引数日付のタイムゾーン、ソート順です。ループする場合はpythonのcomprehensionを利用してください。引数が正しいかのチェックも含めてください。エラーの場合はNoneを返します。正しい場合はローカルタイムゾーンに変換した日付配列を返します。関数と引数のコメントとコードにコメントを含めてください。

from datetime import datetime
from typing import List, Union
import pytz


def sort_dates(dates: List[str], date_format: str, timezone: str, sort_order: str) -> Union[None, List[datetime]]:
    """
    指定された日時形式のPython配列をソートする関数

    :param dates: 日付配列 (list[str])
    :param date_format: 日付形式 (str)
    :param timezone: 引数日付のタイムゾーン (str)
    :param sort_order: ソート順 (str)
    :return: ローカルタイムゾーンに変換した日付配列 (list[datetime]) もしくは None (引数が正しくない場合)
    """

    # 引数チェック
    if not isinstance(dates, list) or not all(isinstance(date, str) for date in dates):
        return None

    try:
        tz = pytz.timezone(timezone)
        parsed_dates = [datetime.strptime(date, date_format) for date in dates]
        timezone_aware_dates = [tz.localize(date) for date in parsed_dates]
    except (ValueError, pytz.UnknownTimeZoneError):
        return None

    # ソート順に応じたソート
    if sort_order == "asc":
        sorted_dates = sorted(timezone_aware_dates)
    elif sort_order == "desc":
        sorted_dates = sorted(timezone_aware_dates, reverse=True)
    else:
        return None

    # ローカルタイムゾーンに変換して返す
    return [date.astimezone(pytz.timezone('local')) for date in sorted_dates]

pytzはpython2で使われているモジュールです。python3ではdatetimeモジュールが推奨されているためpython3で使われるモジュールに制限します。

6. 利用するモジュールを制限する。本例ではPython3標準モジュールを使うようにする。

プロンプト:引数で指定された日時形式のpython配列をソートするpython3関数を生成してください。引数は日付配列、日付形式、引数日付のタイムゾーン、ソート順です。ループする場合はpythonのcomprehensionを利用してください。なるべくpython3標準モジュールを利用してください。引数が正しいかのチェックも含めてください。エラーの場合はNoneを返します。正しい場合はローカルタイムゾーンに変換した日付配列を返します。関数と引数のコメントとコードにコメントを含めてください。

import datetime

def sort_dates(dates, date_format, timezone, sort_order):
    """
    引数で指定された日時形式のPython配列をソートするPython3関数。
    
    :param dates: ソートする日付の配列
    :type dates: list[str]
    :param date_format: 日付のフォーマット
    :type date_format: str
    :param timezone: 引数日付のタイムゾーン
    :type timezone: str
    :param sort_order: ソート順。'asc' (昇順)または'desc' (降順)
    :type sort_order: str
    :return: ソートされた日付の配列をローカルタイムゾーンに変換したもの
    :rtype: list[datetime.datetime] or None
    """
    # 引数のチェック
    if not isinstance(dates, list) or not all(isinstance(date, str) for date in dates):
        return None
    if sort_order not in ['asc', 'desc']:
        return None
    try:
        tz = datetime.timezone(datetime.timedelta(hours=int(timezone)))
    except:
        return None
    
    # 日付をdatetimeオブジェクトに変換して、タイムゾーンを設定する
    dates = [datetime.datetime.strptime(date, date_format).replace(tzinfo=tz) for date in dates]
    
    # 日付をソートする
    dates = sorted(dates, reverse=(sort_order=='desc'))
    
    # 日付をローカルタイムゾーンに変換する
    local_tz = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
    dates = [date.astimezone(local_tz) for date in dates]
    
    return dates

上記コードでも正しく動作しましが、綺麗に短くするように指示します。

7. コードをもっと短くする

プロンプト:コードをもっとエレガントにしてください。

import datetime

def sort_dates(dates, date_format, timezone, sort_order):
    """
    引数で指定された日時形式のPython配列をソートするPython3関数。

    :param dates: ソートする日付の配列
    :type dates: list[str]
    :param date_format: 日付のフォーマット
    :type date_format: str
    :param timezone: 引数日付のタイムゾーン
    :type timezone: str
    :param sort_order: ソート順。'asc' (昇順)または'desc' (降順)
    :type sort_order: str
    :return: ソートされた日付の配列をローカルタイムゾーンに変換したもの
    :rtype: list[datetime.datetime] or None
    """
    try:
        tz = datetime.timezone(datetime.timedelta(hours=int(timezone)))
    except ValueError:
        return None
    except TypeError:
        return None
    
    try:
        sorted_dates = sorted([datetime.datetime.strptime(date, date_format).replace(tzinfo=tz) for date in dates],
                              reverse=(sort_order == 'desc'))
    except ValueError:
        return None
    
    return [date.astimezone() for date in sorted_dates]

実際に生成されたコードが動くかの確認するために関数を呼び出す例も生成します。

8. 関数ろ呼び出す例を作成する

プロンプト:sort_dates関数を呼び出す例を生成してください。

# 例: sort_dates()関数を呼び出す
dates = ['2022-04-30 14:30:00', '2022-04-29 10:45:00', '2022-05-01 08:15:00']
date_format = '%Y-%m-%d %H:%M:%S'
timezone = '+9'
sort_order = 'asc'

sorted_dates = sort_dates(dates, date_format, timezone, sort_order)

if sorted_dates is not None:
    for date in sorted_dates:
        print(date.strftime(date_format))
else:
    print("Invalid arguments.")

項目6と項目7をPythonを抽象化して基本テンプレートの一つとします。類似した機能の開発する場合はこのテンプレートを利用します。flaskやデータベース処理用にも類似した手順でテンプレートを作成します。テンプレートを作成することがChatGPTを学び始めた人でも本番環境で利用できるコードを生成できるようにします。

テストケースを作成する

エラー処理も含めて正しく動作するか確認するためにテストケースも生成します。

  1. プロンプト:unittestを作成してください。
import unittest
from datetime import datetime
from typing import List
from sort_dates import sort_dates


class TestSortDates(unittest.TestCase):

    def test_valid_arguments(self):
        # テストパラメータ
        dates = ['2022-04-30 14:30:00', '2022-04-29 10:45:00', '2022-05-01 08:15:00']
        date_format = '%Y-%m-%d %H:%M:%S'
        timezone = '+9'
        sort_order = 'asc'

        # 期待される結果
        expected_result = [
            datetime.strptime('2022-04-29 10:45:00', date_format),
            datetime.strptime('2022-04-30 14:30:00', date_format),
            datetime.strptime('2022-05-01 08:15:00', date_format)
        ]

        # テスト実行
        result = sort_dates(dates, date_format, timezone, sort_order)

        # 結果を検証する
        self.assertEqual(result, expected_result)

    def test_invalid_date_format(self):
        # テストパラメータ
        dates = ['2022-04-30 14:30:00', '2022-04-29 10:45:00', '2022-05-01 08:15:00']
        date_format = '%Y-%m-%d %H:%M:%'
        timezone = '+9'
        sort_order = 'asc'

        # 期待される結果
        expected_result = None

        # テスト実行
        result = sort_dates(dates, date_format, timezone, sort_order)

        # 結果を検証する
        self.assertEqual(result, expected_result)

    def test_invalid_timezone(self):
        # テストパラメータ
        dates = ['2022-04-30 14:30:00', '2022-04-29 10:45:00', '2022-05-01 08:15:00']
        date_format = '%Y-%m-%d %H:%M:%S'
        timezone = '+30'
        sort_order = 'asc'

        # 期待される結果
        expected_result = None

        # テスト実行
        result = sort_dates(dates, date_format, timezone, sort_order)

        # 結果を検証する
        self.assertEqual(result, expected_result)

    def test_invalid_sort_order(self):
        # テストパラメータ
        dates = ['2022-04-30 14:30:00', '2022-04-29 10:45:00', '2022-05-01 08:15:00']
        date_format = '%Y-%m-%d %H:%M:%S'
        timezone = '+9'
        sort_order = 'invalid'

        # 期待される結果
        expected_result = None

        # テスト実行
        result = sort_dates(dates, date_format, timezone, sort_order)

        # 結果を検証する
        self.assertEqual(result, expected_result)

    def test_empty_dates(self):

途中に終了しているために続きを表示するように問いかける
プロンプト:続きを表示してください。

def test_empty_dates(self):
    # テストパラメータ
    dates = []
    date_format = '%Y-%m-%d %H:%M:%S'
    timezone = '+9'
    sort_order = 'asc'

    # 期待される結果
    expected_result = []

    # テスト実行
    result = sort_dates(dates, date_format, timezone, sort_order)

    # 結果を検証する
    self.assertEqual(result, expected_result)

def test_none_argument(self):
    # テストパラメータ
    dates = None
    date_format = '%Y-%m-%d %H:%M:%S'
    timezone = '+9'
    sort_order = 'asc'

    # 期待される結果
    expected_result = None

    # テスト実行
    result = sort_dates(dates, date_format, timezone, sort_order)

    # 結果を検証する
    self.assertEqual(result, expected_result)

def test_invalid_date(self):
    # テストパラメータ
    dates = ['2022-04-30 14:30:00', 'invalid date', '2022-05-01 08:15:00']
    date_format = '%Y-%m-%d %H:%M:%S'
    timezone = '+9'
    sort_order = 'asc'

    # 期待される結果
    expected_result = None

    # テスト実行
    result = sort_dates(dates, date_format, timezone, sort_order)

    # 結果を検証する
    self.assertEqual(result, expected_result)

ドキュメントを生成する

最後に、ドキュメントも生成します。
プロンプト:ドキュメントを作成してください。
image.png

最後に

PowerShell, Java, bashスクリプトでも同様に本番環境で利用できるコードを生成できます。関数の分け方(モデリング)、アルゴリズムの選択、ソフトウエアアーキテクチャ、システムアーキテクチャについてどこまでChatGPTを利用することが可能なのかは調査中です。
ChatGPTにコーディングさせることにより、IT技術者にはより高度な技術力が求められるようになると思います。

以上

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6