1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

__call__についてのメモ

Last updated at Posted at 2020-03-15

#はじめに
python、kerasともに初学者です。自分用の忘備録です。

#この書き方はなんだろう
Keras functional APIなるものを使うと、keras Sequential APIよりも複雑なニューラルネットワークを使うことができるとのことです。

そこで出てくるのが以下のような書き方です。

001.py
import keras
from keras import layers

inputs = layers.Input(shape=(784,))
dense_hoge = layers.Dense(64, activation='relu')
x = dense_hoge(inputs)# これ!

dense_hoge は keras.layers.core.Dense クラスのインスタンスなのですが、インスタンス名に続けてカッコがあり、中に変数名_inputs_が書いてあります。

しかも最後の2行は、

002.py
x = layers.Dense(64, activation='relu')(inputs)

とも書けるとのことです。変数名1(変数名2)という書き方は、これまで見たことがありません。

一体これは、何をどう処理しているのでしょうか?

#調べた結果
クラスの中で定義された__call__関数が処理しているとのことでした。

簡単なクラスを書いてみます。

003.py
class A:
    def __init__(self):
        print("ClassA __init__")
    
    def __call__(self):
        print ("ClassA __call__")

クラスAのオブジェクトを作ってaに格納。

003.py
a = A()
#ClassA __init__

インスタンスaに()をつけて呼んでみる。

003.py
a()
#ClassA __call__

というわけで、インスタンス名に()をつけると、__call__で定義した処理が実行されるということがわかりました。

__call__が定義されていないクラスではどうでしょうか?

004.py
class B:
    def __init__(self):
        print("ClassB __init__")

b = B()
#ClassB __init__
b()  #以下のエラーが表示される
#Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#TypeError: 'B' object is not callable

'B' objectはcallableではないと、表示されました。
callableかどうかはcallable()で調べられるとのことですので、実行してみました。

004.py
callable(a)
#True
callable(b)
#False

__call__を定義していないClass BではFalseになりました。

インスタンスではなくてクラスを指定するとClass AでもClass Bでもtrueになりました。

005.py
callable(A)
#True
callable(B)
#True

#連続して呼べるのか?
__call__はオブジェクトを返り値にできるということでした。それでは、__call__がcallableなオブジェクトを返す場合は次の__call__を連続して呼べるでしょうか?
以下のClass CはClass Aのインスタンスを返します。Class Aのインスタンスはcallableで、__call__を呼ぶとClassA __call__と返すはずです。

006.py
class C:
    def __init__(self):
        print("ClassC __init__")
    
    def __call__(self):
        print ("ClassC __call__")
        return A()

c = C()
c()()
#ClassC __call__   
#ClassA __init__    #class Cの__call__でClass Aをインスタンス化した時の出力
#ClassA __call__

....連続して呼べました。コードが見にくくなりそうなので、あまり使うべきではないように思いますが、連続して使えることがわかりました。

#変数名として既存の関数名を使うとどうなるのか?
変数名()という書き方ができることがわかりました。pythonでは、関数名として使われている文字列であっても、変数名として利用できるのでした。例えばprintという変数名です。print()とした場合、呼ばれるのは__call__でしょうか?それとも、通常のprint関数でしょうか?

007.py
print = A()
print('123') #以下のエラー表示
#Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#TypeError: __call__() takes 1 positional argument but 2 were given

__call__の方が呼ばれました。

#考察
変数名()という書き方ができることがわかりました。

クラスで__call__を定義して使うのと、関数hoge()を定義して使うのと、どう違うでしょうか?変数名(引数)と変数名.hoge(引数)との違いはなんでしょうか?

変数()の書き方のデメリットとして、

  1. これまで、カッコの前にあるのは関数名と決まっていたのが、必ずしもそうではないことになる。コードの可読性を低下させる。
  2. 知らずに既存の関数名と同じ変数名を使用した場合に、その関数が使えなくなる。

の2点が考えられます。__call__を使うメリットはあるんですかね?

__call__のようなメソッドは特殊メソッドと呼ばれているようです。色々便利に使えそうですが、解説ページがたくさんあるようなので、ここではここまでにしたいと思います。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?