この記事は MicroAd Advent Calendar 2021 の 18 日目の記事です。
概要
Pythonでは辞書もしくは辞書ライクなオブジェクトで設定を管理することが多いですよね。
ただその中から関数やクラスに渡す引数をキーで指定して取り出すのは億劫です。
理想を言えば関数の引数名に応じて自動的に抽出されて欲しいところです。
そこでこの記事では以下のコードのような辞書と関数との引数の受け渡しをよりズボラに書く方法をご紹介していきます。
def hoge(a, b):
print(a, b)
def fuga(c):
print(c)
config = {'a':1, 'b':2, 'c': 3}
hoge(config['a'], config['b'])
fuga(config['c'])
可変長引数を利用する方法
まず1つ目はPythonの可変長引数という機能を利用した方法です。
可変長引数_
がいらない引数を詰め込むゴミ箱のような役割をしてくれるので、関数を呼ぶ辞書を雑に展開して渡すことができます。
def hoge(a, b, **_):
print(a, b)
def fuga(c, **_):
print(c)
# 引数を詰め込んだ辞書
config = {'a':1, 'b':2, 'c': 3}
#
hoge(**config)
fuga(**config)
この方法はシンプルではあるんですが、可変長引数をゴミ箱として利用しちゃっているので、本来の用途に使えないというのが残念なところです。
デコレータで渡す
2つ目はデコレータで設定辞書の内容を渡す方法です。
デコレータが設定辞書に含まれる値の中から関数の引数に含まれるもののみを抽出して渡しています。
先ほどは可変長引数で行なっていた要らない引数の回収をデコレータが代わってくれているので、可変長引数を通常通り利用できるようになっています。
config = {}
def load_config(conf):
config = conf
def deco(f):
def f_(*args, **kwargs):
# configから関数の引数に含まれるものだけを選択
fargs = f.__code__.co_varnames[:f.__code__.co_argcount]
config_kwargs = {k: v for k, v in config.items() if k in fargs}
# configから作った引数と元の引数をマージして渡す(元の引数優先)
f(*args, **(config_kwargs | kwargs))
return f_
import deco
@deco.deco
def hoge(a, b):
print(a, b)
@deco.deco
def fuga(c):
print(c)
config = {'a':1, 'b':2, 'c': 3}
deco.load_config(config)
jobs.hoge()
jobs.fuga()
コード自体は簡潔ですがデコレータ使ってるので処理の流れは追いづらい気がしますね。