Pythonでは、関数名を文字列として保持し、その文字列を使って動的に関数を呼び出したい場面があります。例えば、設定ファイルから関数名を読み込んで実行したり、ユーザー入力に応じて異なる関数を呼び出したりする場合です。
この記事では、getattr() を中心に、文字列から関数を呼び出す方法を詳しく解説します。
リフレクションとは
リフレクションとは、プログラムが実行時に自分自身の構造(クラス、メソッド、属性など)を調べたり操作したりする機能のことです。Pythonでは getattr()、setattr()、hasattr() などの組み込み関数を使ってリフレクションを実現できます。
文字列から関数を呼び出す処理は、このリフレクションの代表的な活用例です。
getattr()の基本的な使い方
getattr() は、オブジェクトから指定した名前の属性を取得する組み込み関数です。
構文
getattr(object, name[, default])
| 引数 | 説明 |
|---|---|
object | 属性を取得する対象のオブジェクト |
name | 取得したい属性の名前(文字列) |
default | 属性が存在しない場合に返すデフォルト値(省略可) |
モジュールの関数を呼び出す例
例えば、math モジュールの sqrt 関数を文字列で呼び出す場合は次のように記述します。
import math
# 文字列で関数を取得
func_name = "sqrt"
sqrt_func = getattr(math, func_name)
# 関数を実行
result = sqrt_func(16)
print(result) # 出力: 4.0
自作モジュールでの使用例
自作モジュール calculator.py があるとします。
# calculator.py
def add(a, b):
"""2つの数を足す"""
return a + b
def subtract(a, b):
"""2つの数を引く"""
return a - b
def multiply(a, b):
"""2つの数を掛ける"""
return a * b
このモジュールの関数を文字列で呼び出すには次のようにします。
import calculator
# 実行したい関数名を文字列で指定
operation = "add"
# getattr()で関数を取得して実行
func = getattr(calculator, operation)
result = func(10, 5)
print(f"{operation}(10, 5) = {result}") # 出力: add(10, 5) = 15
デフォルト値を使った安全な呼び出し
存在しない関数名を指定すると AttributeError が発生します。これを防ぐために、デフォルト値を設定できます。
import calculator
# 存在しない関数名
operation = "divide"
# デフォルト値として None を設定
func = getattr(calculator, operation, None)
if func is not None:
result = func(10, 5)
print(result)
else:
print(f"関数 '{operation}' は存在しません")
# 出力: 関数 'divide' は存在しません
デフォルト関数を設定する方法
デフォルト値として関数を設定することもできます。
def default_operation(a, b):
"""デフォルトの処理"""
print(f"未定義の操作が呼び出されました: {a}, {b}")
return None
import calculator
operation = "unknown"
func = getattr(calculator, operation, default_operation)
result = func(10, 5)
# 出力: 未定義の操作が呼び出されました: 10, 5
クラスのメソッドを文字列で呼び出す
getattr() はモジュールだけでなく、クラスのインスタンスに対しても使用できます。
class TextProcessor:
def uppercase(self, text):
"""テキストを大文字に変換"""
return text.upper()
def lowercase(self, text):
"""テキストを小文字に変換"""
return text.lower()
def reverse(self, text):
"""テキストを反転"""
return text[::-1]
# インスタンスを作成
processor = TextProcessor()
# 文字列でメソッドを指定して呼び出し
method_name = "uppercase"
method = getattr(processor, method_name)
result = method("hello world")
print(result) # 出力: HELLO WORLD
# 複数のメソッドを動的に呼び出す
methods = ["uppercase", "lowercase", "reverse"]
text = "Python"
for method_name in methods:
method = getattr(processor, method_name)
print(f"{method_name}: {method(text)}")
# 出力:
# uppercase: PYTHON
# lowercase: python
# reverse: nohtyP
globals()とlocals()を使った方法
モジュールではなく、現在のスコープにある関数を呼び出す場合は globals() や locals() を使用できます。
globals()を使った例
def greet_english():
return "Hello!"
def greet_japanese():
return "こんにちは!"
def greet_spanish():
return "Hola!"
# グローバル名前空間から関数を取得
language = "japanese"
func_name = f"greet_{language}"
if func_name in globals():
func = globals()[func_name]
print(func()) # 出力: こんにちは!
else:
print(f"関数 '{func_name}' は存在しません")
locals()を使った例
locals() は現在のローカルスコープの変数を辞書として返します。
def process_data(data, operation_name):
def double(x):
return x * 2
def triple(x):
return x * 3
def square(x):
return x ** 2
# ローカル名前空間から関数を取得
operations = locals()
if operation_name in operations:
return operations[operation_name](data)
else:
raise ValueError(f"未知の操作: {operation_name}")
print(process_data(5, "double")) # 出力: 10
print(process_data(5, "square")) # 出力: 25
辞書を使った関数マッピング
より明示的で安全な方法として、関数を辞書にマッピングする方法があります。
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
if b == 0:
raise ValueError("0で割ることはできません")
return a / b
# 関数を辞書にマッピング
operations = {
"add": add,
"subtract": subtract,
"multiply": multiply,
"divide": divide,
}
def calculate(operation_name, a, b):
"""
指定された操作を実行する
Args:
operation_name: 操作名(文字列)
a: 第1引数
b: 第2引数
Returns:
計算結果
"""
if operation_name not in operations:
raise ValueError(f"未知の操作: {operation_name}")
return operations[operation_name](a, b)
# 使用例
print(calculate("add", 10, 5)) # 出力: 15
print(calculate("multiply", 10, 5)) # 出力: 50
この方法は以下の利点があります。
- 許可された関数のみを呼び出せる(セキュリティ向上)
- コードが明示的で読みやすい
- IDEの補完が効きやすい
実践的な使用例: プラグインシステム
getattr() を活用したプラグインシステムの例を紹介します。
# plugins/image_processor.py
class ImageProcessor:
def resize(self, image, width, height):
print(f"画像をリサイズ: {width}x{height}")
return image
def rotate(self, image, angle):
print(f"画像を{angle}度回転")
return image
def flip(self, image, direction):
print(f"画像を{direction}方向に反転")
return image
# main.py
def execute_plugin_action(plugin, action_name, *args, **kwargs):
"""
プラグインのアクションを実行する
Args:
plugin: プラグインオブジェクト
action_name: 実行するアクション名
*args, **kwargs: アクションに渡す引数
Returns:
アクションの実行結果
"""
# アクションが存在するか確認
if not hasattr(plugin, action_name):
raise AttributeError(f"プラグインに '{action_name}' アクションが存在しません")
# アクションが呼び出し可能か確認
action = getattr(plugin, action_name)
if not callable(action):
raise TypeError(f"'{action_name}' は呼び出し可能ではありません")
return action(*args, **kwargs)
# 使用例
processor = ImageProcessor()
image = "sample.jpg"
# 設定ファイルやユーザー入力から読み込んだアクション名
actions = [
("resize", {"width": 800, "height": 600}),
("rotate", {"angle": 90}),
("flip", {"direction": "horizontal"}),
]
for action_name, params in actions:
execute_plugin_action(processor, action_name, image, **params)
各方法の比較
| 方法 | 用途 | 安全性 | 推奨度 |
|---|---|---|---|
getattr() | モジュール・クラスの属性取得 | 高(デフォルト値設定可) | 高 |
globals() | グローバル関数の取得 | 中 | 中 |
locals() | ローカル関数の取得 | 中 | 中 |
| 辞書マッピング | 限定された関数の呼び出し | 最高 | 最高 |
まとめ
Pythonで文字列から関数を呼び出す方法を紹介しました。
getattr(): モジュールやクラスの関数を動的に呼び出す最も一般的な方法- デフォルト値: 存在しない関数を安全に処理するために活用
- 辞書マッピング: セキュリティを重視する場合に最適
状況に応じて適切な方法を選択してください。設定ファイルからの関数呼び出しやプラグインシステムなど、動的な処理が必要な場面で活用できます。