#はじめに
python、kerasともに初学者です。自分用の忘備録です。
#この書き方はなんだろう
Keras functional APIなるものを使うと、keras Sequential APIよりも複雑なニューラルネットワークを使うことができるとのことです。
そこで出てくるのが以下のような書き方です。
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行は、
x = layers.Dense(64, activation='relu')(inputs)
とも書けるとのことです。変数名1(変数名2)という書き方は、これまで見たことがありません。
一体これは、何をどう処理しているのでしょうか?
#調べた結果
クラスの中で定義された__call__関数が処理しているとのことでした。
簡単なクラスを書いてみます。
class A:
def __init__(self):
print("ClassA __init__")
def __call__(self):
print ("ClassA __call__")
クラスAのオブジェクトを作ってaに格納。
a = A()
#ClassA __init__
インスタンスaに()をつけて呼んでみる。
a()
#ClassA __call__
というわけで、インスタンス名に()をつけると、__call__で定義した処理が実行されるということがわかりました。
__call__が定義されていないクラスではどうでしょうか?
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()で調べられるとのことですので、実行してみました。
callable(a)
#True
callable(b)
#False
__call__を定義していないClass BではFalseになりました。
インスタンスではなくてクラスを指定するとClass AでもClass Bでもtrueになりました。
callable(A)
#True
callable(B)
#True
#連続して呼べるのか?
__call__はオブジェクトを返り値にできるということでした。それでは、__call__がcallableなオブジェクトを返す場合は次の__call__を連続して呼べるでしょうか?
以下のClass CはClass Aのインスタンスを返します。Class Aのインスタンスはcallableで、__call__を呼ぶとClassA __call__と返すはずです。
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関数でしょうか?
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(引数)との違いはなんでしょうか?
変数()の書き方のデメリットとして、
- これまで、カッコの前にあるのは関数名と決まっていたのが、必ずしもそうではないことになる。コードの可読性を低下させる。
- 知らずに既存の関数名と同じ変数名を使用した場合に、その関数が使えなくなる。
の2点が考えられます。__call__を使うメリットはあるんですかね?
__call__のようなメソッドは特殊メソッドと呼ばれているようです。色々便利に使えそうですが、解説ページがたくさんあるようなので、ここではここまでにしたいと思います。