システムプロンプトを簡単に切り替えたい:Classを活用すればいいのか〜 Python入門覚え書き
Pythonをマイペースで学習中ですが、ようやくClassが理解できつつある気がします。
それでタイトルに有るように、Classを使えば、IFの連続での手続きでないやり方で、切り替えができるということが分かってきました。
以下、GPT-4の解説です。
クラスを使って `if-else` ロジックを簡潔に書き換えることは可能です。クラスを使うと、各ケースに対応する動作をメソッドとして定義し、インスタンス化によって異なる動作を持たせることができます。
以下は、Pythonでクラスを使って指定された数字に応じて異なるプロンプトを返す例です:
class SystemPrompt:
def __init__(self, number):
self.number = number
self.prompts = {
1: "AAA",
2: "BBB",
3: "CCC"
}
def get_prompt(self):
return self.prompts.get(self.number, "Invalid number") # デフォルト値として"Invalid number"を返す
# 使用例
number = 2 # 例えば、ユーザーからの入力など
prompt_instance = SystemPrompt(number)
system_prompt = prompt_instance.get_prompt()
print(system_prompt) # "BBB" を出力
このコードでは、`SystemPrompt` クラスの `init` メソッドで数字とプロンプトのマッピングを定義しています。`get_prompt` メソッドは、初期化時に指定された数字に基づいて適切なプロンプトを返します。存在しない数字が指定された場合は、`"Invalid number"` を返します。
クラスを使用すると、コードの再利用性を高め、複雑な `if-else` 構造をより管理しやすい形に整理することができます。また、将来的にプロンプトの種類を拡張する場合にも、`prompts` ディクショナリに項目を追加するだけで対応できるため、柔軟性が向上します。
Pythonでクラスのインスタンスを他の関数内で使用する場合、そのインスタンスを関数に渡すか、関数内でインスタンスを生成することができます。グローバル変数を使用する必要はありません。以下に例を示します。
まず、先ほどの`SystemPrompt`クラスを使ってインスタンスを生成し、プロンプトを取得する関数を定義します:
class SystemPrompt:
def __init__(self, number):
self.number = number
self.prompts = {
1: "AAA",
2: "BBB",
3: "CCC"
}
def get_prompt(self):
return self.prompts.get(self.number, "Invalid number")
# 関数定義
def display_prompt(number):
prompt_instance = SystemPrompt(number)
system_prompt = prompt_instance.get_prompt()
print(system_prompt) # ここでプロンプトを出力
# 関数を呼び出す
display_prompt(2) # "BBB" を出力
この場合、`display_prompt`関数は引数`number`を受け取り、その数値に基づいて`SystemPrompt`のインスタンスを生成しています。その後、`get_prompt`メソッドを呼び出してプロンプトを取得し、出力します。関数の外部にグローバル変数を設定する必要はありません。
関数外でインスタンスを生成し、それを関数に渡すこともできます:
# 関数定義
def display_prompt(prompt_instance):
system_prompt = prompt_instance.get_prompt()
print(system_prompt) # ここでプロンプトを出力
# インスタンスを生成して関数に渡す
prompt_instance = SystemPrompt(2)
display_prompt(prompt_instance) # "BBB" を出力
この場合、`display_prompt`関数は`SystemPrompt`のインスタンスを引数として受け取り、その`get_prompt`メソッドを呼び出してプロンプトを表示します。
どちらの方法でも、グローバル変数は不要で、関数の引数を通じて必要な情報を渡すことができます。これにより、コードの再利用性が高く、他の部分との依存関係が少なくなるため、望ましいプラクティスです。
別ファイルに記載しても大丈夫という話
`SystemPrompt` クラスを別のファイルに作成し、同じディレクトリ内に置くことで、他のPythonファイルからそのクラスをインポートして使用することができます。ファイル名は、そのファイルが何を含んでいるかを表すものであるべきです。クラス名やそのクラスの機能を反映した名前を選ぶことが一般的です。
例えば、`SystemPrompt` クラスはプロンプトを生成するという役割を持っているため、ファイル名を `system_prompt.py` とすることが適切かもしれません。以下はその例です:
`system_prompt.py`:
class SystemPrompt:
def __init__(self, number):
self.number = number
self.prompts = {
1: "AAA",
2: "BBB",
3: "CCC"
}
def get_prompt(self):
return self.prompts.get(self.number, "Invalid number")
そして、このクラスを別のファイルからインポートして使用するには、例えば `app.py` というファイルを作成し、以下のように記述します:
`app.py`:
from system_prompt import SystemPrompt
def display_prompt(number):
prompt_instance = SystemPrompt(number)
system_prompt = prompt_instance.get_prompt()
print(system_prompt)
# 関数を呼び出す
display_prompt(1) # "AAA" を出力
display_prompt(2) # "BBB" を出力
display_prompt(3) # "CCC" を出力
このように、`system_prompt.py` を作成し、それを `app.py` からインポートすることで、`SystemPrompt` クラスが定義されていることを `app.py` に知らせ、クラスを使用することができます。同じディレクトリ内にあるため、パスの指定など追加の設定は必要ありません。
デフォールト設定
Pythonでは関数やメソッドの引数にデフォルト値を設定することができます。`init` メソッドの引数`number`にデフォルト値を設定することで、何も指定されなかった場合にデフォルトで 1 を使用するようにできます。以下はその例です:
class SystemPrompt:
def __init__(self, number=1): # デフォルト値として 1 を設定
self.number = number
self.prompts = {
1: "AAA",
2: "BBB",
3: "CCC"
}
def get_prompt(self):
return self.prompts.get(self.number, "Invalid number")
この変更により、`SystemPrompt` クラスのインスタンスを生成する際に引数を何も渡さない場合、自動的に `number` が 1 に設定され、"AAA" がデフォルトのプロンプトとして使用されます。
使用例:
prompt_instance = SystemPrompt() # 引数を渡さない場合、デフォルトで 1 が使用される
print(prompt_instance.get_prompt()) # "AAA" を出力
引数を指定してインスタンスを生成した場合は、その指定された値が使用されます。
prompt_instance = SystemPrompt(2)
print(prompt_instance.get_prompt()) # "BBB" を出力
実行ファイルが作られる話
`pycache` ディレクトリと `.pyc` ファイルは、Pythonが自動的に生成するもので、Pythonのバイトコードキャッシュを含んでいます。
Pythonでは、ソースコード(`.py`ファイル)が最初に実行されると、Pythonインタプリタはソースコードをコンパイルしてバイトコードに変換します。このバイトコードは、Pythonの仮想マシンが理解できる中間形式で、Pythonプログラムを実行する際にはこのバイトコードが使用されます。
このバイトコードを毎回ソースコードから生成するのは時間がかかるため、Pythonはバイトコードをキャッシュして再利用することで実行速度を向上させます。キャッシュされたバイトコードは `.pyc` ファイルとして保存され、`pycache` ディレクトリに配置されます。
`system_prompt.cpython-311.pyc` というファイルについては、`system_prompt.py` というPythonソースコードから生成されたバイトコードであり、`cpython-311` はこのバイトコードがCPythonバージョン3.11を使って生成されたことを意味しています(CPythonはPythonの標準実装です)。
このバイトコードファイルは、ソースファイルが変更されない限り、次回プログラムを実行する際に再びソースコードからバイトコードを生成する必要がないため、プログラムの起動時間が速くなります。
通常、これらのファイルやディレクトリは無視しても構いません。ソースコードを編集して再実行すると、キャッシュされたバイトコードが古くなっていれば自動的に更新されます。また、これらのファイルはバージョン管理(Gitなど)に含めるべきではなく、通常 `.gitignore` ファイルに記載して無視されます。
SubDirectory構造での指定の話
Pythonではディレクトリ構造を反映してモジュールやパッケージを指定する際にドット(`.`)を使用します。ドットは現在のディレクトリ構造における階層を表し、サブディレクトリにあるモジュールやパッケージにアクセスするために使います。
以下は、さらに詳しい例です。
もし以下のようなディレクトリ構造があった場合:
my_project/
│
├── main.py
└── my_package/
├── __init__.py
└── my_module.py
そして `my_module.py` には以下のようなクラスが定義されているとします:
# my_module.py
class MyClass:
def greet(self):
return "Hello, world!"
このクラスを `main.py` からインポートするには、以下のように書きます:
# main.py
from my_package.my_module import MyClass
my_instance = MyClass()
print(my_instance.greet())
ここで `my_package.my_module` がドット表記で指定されており、これにより `my_package` ディレクトリの中の `my_module.py` ファイルから `MyClass` をインポートしています。
Pythonのインポートシステムにおいては、`init.py` ファイルはパッケージの初期化を行うために存在しますが、Python 3.3 以降は名前空間パッケージという概念が導入されたため、通常のパッケージでは `init.py` がなくてもパッケージとして認識されるようになりました。ただし、古いバージョンのPythonや互換性のために `init.py` を置くことが一般的です。
以上、PoeでのGPT-4でいろいろ整理して教えてもらった覚え書きです。
この記事を最後までご覧いただき、ありがとうございます!もしも私の活動を応援していただけるなら、大変嬉しく思います。