2020年5月15日金曜日

pythonのリスト化したインスタンスそれぞれに対してインスタンス関数を使う方法

pythonでインスタンスのリストを作り、インスタンス関数に対して同一変数を与える方法等。
結論は、mapとlambdaを使うと便利ということ。


例として次のような、クラスを定義する。

class myclass:
    def __init__(self,val):
        self.val=val

    def func(self,val):
        return self.val==self.val

次のように動作する。

o=myclass(0)
o.func(0) → True
o.func(1) → False

これを、インスタンスのリストとする。

ol=[myclass(1),myclass(2),myclass(3)]

olのインスタンス全てに対してfunc(x)を呼ぶ時、forを使って次のように書ける。

for o in ol:
    o.func(x)

上のままでは全く意味のない動作である。

(1)全ての結果をリストとして得たい場合、例えば次のように書ける。

rl=[]
for o in ol:
    rl.append(o.func(x))


(2)一つでもTrueを返す場合何某かの処理をするという場合、例えば次のように書ける。

for o in ol:
    if o.func(x):
        <何某かの処理>
        break

(breakが無ければ、Trueが複数返る場合はその回数だけ処理を行う。)

(3)全てがTrueの場合に処理する場合、例えば次のように書ける。

flag=True
for o in ol:
    if not(o.func(x)):
        flag=False
        break
if flag:
    <何某かの処理>


ここからが本題。
mapとlambdaを使用すると、forを使わなくても全てのインスタンスについてfuncを呼び出せる。

map(lambda o: o.func(x), ol)


以下は、それぞれ上記の例と同じ動作をするものである。

(1)
rl=list(map(lambda o: o.func(x), ol))

(2)
if any(map(lambda o: o.func(x), ol)):
    <何某かの処理>

(3)
if all(map(lambda o: o.func(x), ol)):
    <何某かの処理>


anyやallとmapを組み合わせる場合、
anyでは最初にTrueが返った段階で、以降のインスタンスのfunc(x)は実行されない。
allでは最初にFalseが返った段階で、以降の(略)。
これは、mapが生成される段階では、まだmapされた関数は実行されないということである。

最初の例のようにmapをリスト化する場合、その段階で全てに対してfunc(x)が実行される。


(追記)
mapとlambda以外に、リスト内包表記を使うこともできる。

(1)については、リスト内包表記を使うと、次のように書ける。
rl=[o.func(x) for o in ol]

同様に(2)は、
if any([o.func(x) for o in ol]):
    <何某かの処理>

(3)は、
if all([o.func(x) for o in ol]):
    <何某かの処理>

ただし、(2)と(3)は全てのインスタンスのfunc(x)が実行されることになる。

(1)についてはリスト内包表記の方が処理が速いようだ。
したがって、単純にリストを生成する場合、リスト内包表記の方が適しているだろう。
(2)と(3)のようにallやanyと組み合わせる場合、func(x)の処理が重くなると、mapとlambdaが有利になる・・・こともあると思う。
(anyの中でTrueが出る可能性が高い場合や、allでFalseの可能性が高い場合)