Pythonで動的に関数(メソッド)を定義する


ハマってしまったので、どなたかの参考になれば幸いです。

**やろうとしていること**: 動的に3つのことなる`path`を表示する関数を定義する


`def func`の中が実際に関数が呼ばれたときに評価されるため、`t`の値が`for`を処理した最後の値になってしまっている。

```python
>>> path_freq = [{"path": "/pickup/6162449", "freq": 1, "name": "a"}, {"path": "/pickup/6162466", "freq": 1, "name": "b"}, {"path": "/pickup/6162461", "freq":1, "name": "c"}]
>>> task_list = {}
>>> for t in path_freq:
...   def func():
...     print(t)
...   task_list.update({func: t['path']})
...
>>> task_list
{<function func at 0x101184848>: '/pickup/6162461', <function func at 0x101184938>: '/pickup/6162466', <function func at 0x1011848c0>: '/pickup/6162449'}
>>> for k,v in task_list.items():
...   k()
...
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
```


Rubyで似たようなことを試す

```Ruby
[2] pry(main)> path_freq = [{"path" => "/pickup/6162449", "freq" => 1, "name" => "a"}, {"path" => "/pickup/6162466", "freq" => 1, "name" => "b"}, {"path" => "/pickup/6162461", "freq" => 1, "name" => "c"}]
=> [{"path"=>"/pickup/6162449", "freq"=>1, "name"=>"a"},
 {"path"=>"/pickup/6162466", "freq"=>1, "name"=>"b"},
 {"path"=>"/pickup/6162461", "freq"=>1, "name"=>"c"}]
[3] pry(main)> path_freq.each do |t|
[3] pry(main)*   define_method :hoge do
[3] pry(main)*     puts t
[3] pry(main)*   end
[3] pry(main)*   task_list.merge!({hoge => t})
[3] pry(main)* end
{"path"=>"/pickup/6162449", "freq"=>1, "name"=>"a"}
{"path"=>"/pickup/6162466", "freq"=>1, "name"=>"b"}
{"path"=>"/pickup/6162461", "freq"=>1, "name"=>"c"}
=> [{"path"=>"/pickup/6162449", "freq"=>1, "name"=>"a"},
 {"path"=>"/pickup/6162466", "freq"=>1, "name"=>"b"},
 {"path"=>"/pickup/6162461", "freq"=>1, "name"=>"c"}]
[4] pry(main)> task_list
=> {nil=>{"path"=>"/pickup/6162461", "freq"=>1, "name"=>"c"}}
[5] pry(main)> hoge
{"path"=>"/pickup/6162461", "freq"=>1, "name"=>"c"}
=> nil
```

PythonとRubyは言語仕様が似てると思ってたけど、一概にそうとも言えないとこの辺りで思いしる。
PythonではメソッドはObjectとして扱われるけど、Rubyでは違う。Rubyの場合は、define_methodで`hoge`メソッドを定義した段階で、この例では`main Object`のメソッドとして定義されてしまう。`hoge`メソッドをObjectとして配列に入れたりできない。ちなみに、task_listのkeyに`nil`が入っているのは、Rubyではputsの戻り値が`nil`だから。

ならば、別の方法でメソッドを定義してみる。
Pythonでsetaddrでclassにメソッドを追加してみるも、同じ挙動。

```python
>>> class Test(): 
...   pass
>>> path_freq = [{"path": "/pickup/6162449", "freq": 1, "name": "a"}, {"path": "/pickup/6162466", "freq": 1, "name": "b"}, {"path": "/pickup/6162461", "freq":
1, "name": "c"}]
>>> task_list = []
>>> for t in path_freq:
...   def func():
...     print(t)
...   setattr(Test, t['name'], func)
...   task_list.append(func)
...
>>> for f in task_list:
...   f()
...
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
>>> for t in path_freq:
...   def func(self):
...     print(t)
...   setattr(Test, t['name'], func)
...
>>> Test().a()
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
>>> Test().b()
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
>>> Test().c()
{'path': '/pickup/6162461', 'freq': 1, 'name': 'c'}
```

関数のSignatureは遅延評価されないのではと、引数のデフォルト値でListの値を渡すようにしてみた。

```python
>>> path_freq = [{"path": "/pickup/6162449", "freq": 1, "name": "a"}, {"path": "/pickup/6162466", "freq": 1, "name": "b"}, {"path": "/pickup/6162461", "freq":1, "name": "c"}]
>>> task_list = {}
>>> for t in path_freq:
...   def func(path=t['path']):
...     print(path)
...   task_list.update({func: t['path']})
...
>>> for k,v in task_list.items():
...   k()
...
/pickup/6162461
/pickup/6162466
/pickup/6162449
```

:+1: