Python inspect.signature()とは?関数の引数を調べる方法
inspect.signature()は、関数がどんな引数を受け取るかを調べる関数です。
引数の名前・既定値・種類をプログラムから扱える形で取得できます。
signature()の基本
import inspect
def greet(name, greeting="こんにちは", *args, times=1, **kwargs):
return f"{greeting}、{name}さん"
sig = inspect.signature(greet)
print(sig)
print(type(sig).__name__)
実行結果:
(name, greeting='こんにちは', *args, times=1, **kwargs)
Signature
定義そのままの文字列になります。ヘルプ表示やエラーメッセージにそのまま使えます。
引数を1つずつ調べる
for name, p in sig.parameters.items():
print(f"{name:10} kind={p.kind.name:22} default={p.default!r}")
実行結果:
name kind=POSITIONAL_OR_KEYWORD default=<class 'inspect._empty'>
greeting kind=POSITIONAL_OR_KEYWORD default='こんにちは'
args kind=VAR_POSITIONAL default=<class 'inspect._empty'>
times kind=KEYWORD_ONLY default=1
kwargs kind=VAR_KEYWORD default=<class 'inspect._empty'>
parametersは順序を保った辞書です。定義順に並びます。
既定値がないときはinspect._empty
ここが分かりにくい点です。既定値がない引数のdefaultはNoneではありません。
p = sig.parameters["name"]
print(p.default is None)
print(p.default is inspect.Parameter.empty)
実行結果:
False
True
Noneを既定値にした引数と区別するためです。
def f(a, b=None): pass
for name, p in inspect.signature(f).parameters.items():
has = p.default is not inspect.Parameter.empty
print(name, "既定値あり:", has, "値:", p.default)
実行結果:
a 既定値あり: False 値: <class 'inspect._empty'>
b 既定値あり: True 値: None
if p.default:と書いてはいけません。Noneも0も偽になってしまいます。
5種類のkind
| kind | 書き方 |
|---|---|
POSITIONAL_ONLY |
def f(a, /) |
POSITIONAL_OR_KEYWORD |
普通の引数 |
VAR_POSITIONAL |
*args |
KEYWORD_ONLY |
*より後ろ |
VAR_KEYWORD |
**kwargs |
KEYWORD_ONLYはキーワードでしか渡せない引数です。上の例のtimesがこれにあたります。*argsより後ろに書いたためです。
型ヒントも取れる
def add(a: int, b: int = 0) -> int:
return a + b
sig = inspect.signature(add)
print(sig)
print("戻り値:", sig.return_annotation)
print("aの型:", sig.parameters["a"].annotation)
実行結果:
(a: int, b: int = 0) -> int
戻り値: <class 'int'>
aの型: <class 'int'>
型ヒントがそのまま取得できます。
FastAPIなどのフレームワークは、この情報を使ってリクエストの値を自動で変換・検証しています。
型ヒントについては「Python typingモジュールとは?型ヒントの書き方を初心者向けに解説」を参照してください。
bindで引数を検証する
inspect.signature(greet).bind()
実行結果:
TypeError: missing a required argument: 'name'
関数を実行せずに、引数が正しいか確認できます。
b = inspect.signature(greet).bind("田中")
b.apply_defaults()
print(b.arguments)
実行結果:
{'name': '田中', 'greeting': 'こんにちは', 'args': (), 'times': 1, 'kwargs': {}}
apply_defaults()を呼ぶと既定値が埋まります。
実際に渡される値の全体像が分かるため、ログやキャッシュのキー生成に使えます。
デコレータでの注意点
import functools
import inspect
def deco(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@deco
def greet(name, greeting="こんにちは"):
return f"{greeting}、{name}さん"
print(inspect.signature(greet))
実行結果:
(*args, **kwargs)
元の引数の情報が消えています。包んだwrapperの定義が見えているためです。
functools.wrapsを付けると解決します。
def deco(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
実行結果:
(name, greeting='こんにちは')
デコレータには必ず@functools.wrapsを付けてください。「Python functools.wraps()とは?デコレータで関数情報を保つ方法」で解説しています。
組み込み関数では失敗することがある
print(inspect.signature(len))
inspect.signature(print)
実行結果(Python 3.9):
(obj, /)
ValueError: no signature found for builtin <built-in function print>
len()は取得できるのにprint()はできません。
C言語で実装された関数のうち、引数の情報が埋め込まれていないものは取得できません。どちらになるかは関数ごとに違います。
try:
sig = inspect.signature(func)
except ValueError:
sig = None
print("シグネチャを取得できません")
外部から受け取ったオブジェクトを扱うなら、必ずtryで囲んでください。
signature()のまとめ
- 引数の名前・既定値・種類・型ヒントを取得できる
- 既定値なしは
NoneではなくParameter.empty kindで*argsやKEYWORD_ONLYを判別できるbind()で実行せずに引数を検証- デコレータは
@functools.wrapsが必須 - 組み込み関数では
ValueErrorになることがある




