Documentation Python

Pythonのcallable()関数は、オブジェクトが関数やメソッドのように呼び出せるかどうかを確認するための組み込み関数です。動的な関数呼び出しやコールバックの検証など、柔軟なプログラム設計に欠かせない機能です。

呼び出し可能なオブジェクトの種類

オブジェクトcallable()説明
関数(def)True通常の関数定義
ラムダ式True無名関数
クラスTrueインスタンス生成のため
メソッドTrueインスタンス/クラスメソッド
__call__を持つインスタンスTrueカスタム呼び出し可能オブジェクト
組み込み関数Trueprint, lenなど
通常のインスタンスFalse__call__なし
文字列・数値Falseプリミティブ型

基本的な使い方

構文

callable(object)
パラメータ説明
object呼び出し可能かどうか確認したいオブジェクト
戻り値True(呼び出し可能)または False

各オブジェクトの判定

# 関数
def greet(name):
    return f"Hello, {name}!"

print(callable(greet))  # True

# ラムダ式
add = lambda x, y: x + y
print(callable(add))    # True

# クラス
class Person:
    pass

print(callable(Person))       # True(インスタンス生成可能)
print(callable(Person()))     # False(通常のインスタンス)

# 組み込み関数
print(callable(print))   # True
print(callable(len))     # True
print(callable(int))     # True(型変換関数)

# プリミティブ型
print(callable(42))       # False
print(callable("hello"))  # False
print(callable([1, 2, 3]))  # False

__call__メソッドの実装

基本的な__call__

class Greeter:
    """挨拶を生成する呼び出し可能オブジェクト"""

    def __init__(self, greeting: str = "Hello"):
        self.greeting = greeting

    def __call__(self, name: str) -> str:
        """インスタンスを関数のように呼び出す"""
        return f"{self.greeting}, {name}!"

# 使用例
greeter = Greeter()
print(callable(greeter))     # True
print(greeter("Alice"))      # Hello, Alice!

japanese_greeter = Greeter("こんにちは")
print(japanese_greeter("太郎"))  # こんにちは, 太郎!

状態を持つ呼び出し可能オブジェクト

class Counter:
    """呼び出すたびにカウントアップする"""

    def __init__(self, start: int = 0):
        self.count = start

    def __call__(self) -> int:
        self.count += 1
        return self.count

counter = Counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

# 別のインスタンスは独立したカウント
counter2 = Counter(100)
print(counter2())  # 101

関数デコレータとしての活用

class Retry:
    """失敗時にリトライするデコレータ"""

    def __init__(self, max_retries: int = 3):
        self.max_retries = max_retries

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(self.max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e
                    print(f"Attempt {attempt + 1} failed: {e}")
            raise last_exception
        return wrapper

@Retry(max_retries=3)
def unstable_api_call():
    import random
    if random.random() < 0.7:
        raise ConnectionError("API connection failed")
    return "Success!"

# 実行例
# result = unstable_api_call()

実践的な使用パターン

動的な関数呼び出し

def safe_call(func, *args, **kwargs):
    """安全に関数を呼び出す"""
    if not callable(func):
        raise TypeError(f"{type(func).__name__} is not callable")
    return func(*args, **kwargs)

# 使用例
def add(a, b):
    return a + b

result = safe_call(add, 1, 2)
print(result)  # 3

# エラーケース
try:
    safe_call("not a function", 1, 2)
except TypeError as e:
    print(e)  # str is not callable

プラグインシステム

class PluginManager:
    """プラグインを管理・実行するマネージャー"""

    def __init__(self):
        self.plugins = {}

    def register(self, name: str, plugin):
        """プラグインを登録(呼び出し可能なオブジェクトのみ)"""
        if not callable(plugin):
            raise TypeError(f"Plugin must be callable, got {type(plugin).__name__}")
        self.plugins[name] = plugin

    def execute(self, name: str, *args, **kwargs):
        """プラグインを実行"""
        if name not in self.plugins:
            raise KeyError(f"Plugin '{name}' not found")
        return self.plugins[name](*args, **kwargs)

    def list_plugins(self) -> list:
        """登録済みプラグインの一覧"""
        return list(self.plugins.keys())

# プラグインの定義
def uppercase_plugin(text: str) -> str:
    return text.upper()

class ReversePlugin:
    def __call__(self, text: str) -> str:
        return text[::-1]

# 使用例
manager = PluginManager()
manager.register("uppercase", uppercase_plugin)
manager.register("reverse", ReversePlugin())

print(manager.execute("uppercase", "hello"))  # HELLO
print(manager.execute("reverse", "hello"))    # olleh
print(manager.list_plugins())  # ['uppercase', 'reverse']

イベントハンドラシステム

from typing import Callable, Dict, List, Any

class EventEmitter:
    """イベント駆動のハンドラ管理"""

    def __init__(self):
        self._handlers: Dict[str, List[Callable]] = {}

    def on(self, event: str, handler: Callable):
        """イベントハンドラを登録"""
        if not callable(handler):
            raise TypeError("Handler must be callable")

        if event not in self._handlers:
            self._handlers[event] = []
        self._handlers[event].append(handler)

    def emit(self, event: str, *args, **kwargs) -> List[Any]:
        """イベントを発火"""
        results = []
        if event in self._handlers:
            for handler in self._handlers[event]:
                results.append(handler(*args, **kwargs))
        return results

    def off(self, event: str, handler: Callable = None):
        """ハンドラを解除"""
        if event in self._handlers:
            if handler is None:
                del self._handlers[event]
            else:
                self._handlers[event] = [
                    h for h in self._handlers[event] if h != handler
                ]

# 使用例
emitter = EventEmitter()

def on_user_login(user_id: int):
    print(f"User {user_id} logged in")

def send_welcome_email(user_id: int):
    print(f"Sending welcome email to user {user_id}")

emitter.on("login", on_user_login)
emitter.on("login", send_welcome_email)

emitter.emit("login", 123)
# 出力:
# User 123 logged in
# Sending welcome email to user 123

コマンドパターン

from abc import ABC, abstractmethod
from typing import Optional

class Command(ABC):
    """コマンドの基底クラス"""

    @abstractmethod
    def __call__(self):
        pass

class PrintCommand(Command):
    def __init__(self, message: str):
        self.message = message

    def __call__(self):
        print(self.message)

class Calculator:
    def __init__(self):
        self.result = 0

    def add(self, value: int):
        self.result += value
        return self.result

class CalculatorCommand(Command):
    def __init__(self, calculator: Calculator, operation: str, value: int):
        self.calculator = calculator
        self.operation = operation
        self.value = value

    def __call__(self):
        if self.operation == "add":
            return self.calculator.add(self.value)

# コマンドキュー
class CommandQueue:
    def __init__(self):
        self.commands = []

    def add(self, command):
        if not callable(command):
            raise TypeError("Command must be callable")
        self.commands.append(command)

    def execute_all(self):
        results = []
        for command in self.commands:
            results.append(command())
        self.commands.clear()
        return results

# 使用例
calc = Calculator()
queue = CommandQueue()
queue.add(CalculatorCommand(calc, "add", 10))
queue.add(CalculatorCommand(calc, "add", 20))
queue.add(PrintCommand("計算完了"))

queue.execute_all()
print(f"Result: {calc.result}")  # Result: 30

hasattr vs callable

class MyClass:
    def method(self):
        pass

    value = 42

obj = MyClass()

# hasattrは属性の存在確認
print(hasattr(obj, "method"))  # True
print(hasattr(obj, "value"))   # True
print(hasattr(obj, "nothing")) # False

# callableは呼び出し可能性の確認
print(callable(obj.method))    # True
print(callable(obj.value))     # False

# 組み合わせて使用
def get_callable_attr(obj, name):
    """呼び出し可能な属性を取得"""
    if hasattr(obj, name):
        attr = getattr(obj, name)
        if callable(attr):
            return attr
    return None

method = get_callable_attr(obj, "method")
if method:
    method()  # 安全に呼び出し

型ヒントとcallable

from typing import Callable, TypeVar, ParamSpec

P = ParamSpec('P')
T = TypeVar('T')

def apply_twice(func: Callable[[int], int], value: int) -> int:
    """関数を2回適用する"""
    return func(func(value))

def double(x: int) -> int:
    return x * 2

result = apply_twice(double, 5)
print(result)  # 20 (5 -> 10 -> 20)

# Callableの型ヒント例
def create_multiplier(factor: int) -> Callable[[int], int]:
    """乗算関数を生成"""
    def multiplier(x: int) -> int:
        return x * factor
    return multiplier

triple = create_multiplier(3)
print(triple(10))  # 30

まとめ

用途方法
呼び出し可能性の確認callable(obj)
カスタム呼び出し可能オブジェクト__call__メソッドを実装
安全な関数呼び出しif callable(func): func()
コールバック検証callableでチェック後に登録
型ヒントCallable[[Args], Return]

callable()関数は、動的なプログラミングにおいてオブジェクトの呼び出し可能性を確認するための重要なツールです。__call__メソッドと組み合わせることで、関数のように振る舞うオブジェクトを作成でき、より柔軟で再利用可能なコード設計が可能になります。

参考文献

円