※以下のプログラムはPython 2.7/3.9およびRuby 2.7でそのまま貼り付けて動作することを確認しています。
背景
ライブラリの挙動により、このようなことをしたいことがあると思います。
process(config=json.load(open(fname)),data=get_data(config))
process(config: JSON.parse(File.read(fname)), data: get_data(config))
しかし、前側のパラメータに与えられる情報を後ろのパラメータで使うことはできません。
解決策
※以下、サンプルなのでconfig/dataという名前になっていますが、実際にはこの辺りはちゃんとした名前をつけてください
一時変数
こういう場合、通常はkwargs変数を用意して処理するのだと思います。
Python
def process(**kwargs): # actually defined in another module
print(kwargs)
def get_data(config):
return 1
kwargs = {
'config': {}, # json.load(open('aaa.json'))
}
kwargs['data'] = get_data(kwargs['config'])
process(**kwargs)
# {'config': {}, 'data': 1}
Ruby
class C # actually defined in another module
def self.process(**kwargs)
print(kwargs)
end
end
def get_data(config)
return 1
end
kwargs = {
:config => {}, # JSON.parse File.read('aaa.json')
}
kwargs[:data] = get_data(kwargs[:config])
C.process(**kwargs)
# {:config=>{}, :data=>1}
似非関数型プログラミング
しかし、このkwargsという変数をどうしても入れたくない場合もあると思います。このような場合、PythonのlocalsやRubyのlocal_variablesを用いて関数型にすることができてしまいます!!!
Python
def process(**kwargs): # actually defined in another module
print(kwargs)
def get_data(config):
return 1
def build_args(fname):
config = {} # json.load(open(fname))
data = get_data(config)
del fname
return locals()
process(**build_args('aaa.json'))
# {'config': {}, 'data': 1}
Ruby
- delに相当するものがRubyにはないので、Hashの要素を削除する必要があります。一時変数…と思いきや、Rubyには_1があるので安心です。
class C # actually defined in another module
def self.process(**kwargs)
print(kwargs)
end
end
def get_data(config)
return 1
end
def build_args(fname)
config = {} # JSON.parse File.read(fname)
data = get_data(config)
return local_variables.zip(local_variables.map(&binding.method(:local_variable_get))).to_h.tap{_1.delete(:fname)}
end
C.process(**build_args('aaa.json'))
# {:config=>{}, :data=>1}
最後に
通常、data = get_data(conf)
に相当する操作は、data=None(やnil)を渡した上で、ライブラリ側でdata is None(なりnilなり)のときに実行することだと思います。この記事で紹介した方法はライブラリ側を変更できない場合のみにしてください(というか必要ないならやらないでください)。
蛇足
このPythonサンプルをRubyに移植したのが私がRubyで初めてkwargsをまともに使ったプログラムになってしまいました^^;