質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

6511閲覧

python 特殊メソッド__call__の利点

nudesudesu

総合スコア8

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

2クリップ

投稿2021/07/20 14:01

編集2021/07/20 14:02

python初心者です。
下記の質問をみて、特殊メソッド__call__と、その機能がインタンス作成後に関数的に引数を持たせられると知りました。
https://teratail.com/questions/259292

素朴にインスタンス作成時にコンストラクタに引数を持たせるほうがシンプルかなと思うのですが、__call__を使う利点などありますでしょうか?
コンストラクタ: init では対応できないことなどありますでしょうか?
(クラスを継承させたときに、新しいコンストラクタのプロパティー?(selfの値)を更新できるから__call__が利点なのですか?)

Python

1 2class ConstractTest: 3 def __init__(self, value): 4 self.value = value 5 print(self.value) 6 pass 7 pass 8 9 10class CallTest: 11 def __call__(self, value): 12 print(value) 13 pass 14 pass 15 16test_constracta = ConstractTest('constract hoge') 17 18test_call = CallTest() 19test_call('call hoge') 20 21>> constract hoge 22>> call hoge

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

  • 素朴にインスタンス作成時にコンストラクタに引数を持たせるほうがシンプルかなと思うのですが、callを使う利点などありますでしょうか?

使い道が違うので比べるものではありません。

  • コンストラクタ: init では対応できないことなどありますでしょうか?

下の例を見て考えてみてください。

python

1>>> class CallTest: 2... def __init__(self, coefficient): 3... self.coefficient = coefficient 4... def __call__(self, variant): 5... return self.coefficient * variant 6... 7>>> test_call_5 = CallTest(5) 8>>> test_call_7 = CallTest(7) 9>>> 10>>> print(test_call_5(9)) 1145 12>>> print(test_call_7(9)) 1363

投稿2021/07/20 14:32

ppaul

総合スコア24670

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nudesudesu

2021/07/21 22:57 編集

上記quickquipさんの回答を受け、機能的な違いはなく、文法的な違いだと思っていますが、認識齟齬ありますでしょうか? 例えば私の例だと print文のconstractとcallが異なる以外なにか出力に違いがあるのでしょうか?
ppaul

2021/07/21 23:41 編集

quickquipさんのa.exec(...)がa(...)と書けるだけというのは完全に正しいです。 しかし、nudesudesuさんの質問が、__init__と__call__の機能の違いを聞いているなら、それは質問に対する回答ではないと私は考えています。 nudesudesuさんが納得しているのなら、どうでもよいことかもしれませんが、私は以下のようなことを理解してほしかったのです。 コンストラクタが呼び出す__init__はクラスからインスタンスを生成する場合に、インスタンスの初期状態を設定するためのものである。 呼び出し可能メソッド__call__は、インスタンスをcallableにして、他の関数インスタンス(組み込み関数とかユーザ定義関数)と同じように()で呼び出せるようにするものである。 したがって、実行する階層(クラスかインスタンスか)とか目的が全く違うものを比べるのは意味がない。
nudesudesu

2021/07/21 23:53

大変わかりやすい補足ありがとうございます! まだ勉強不足なところがあるので 、 無知なので教えてほしいのですが、実行階層がクラスかインスタンスだとのように目的分けするのでしょうか? (このあたりが自分の根本的理解不足の原因だと想っています。) とんちんかんな事を言っていたら、申し訳ないですが、コンストラクタは実行対象がクラスのため、クラス内でサイド定義可能に対して、 __call__は実行対象がインスタンスであるので、クラス内では実行不可能(引数として、インスタンス化したものにあてがって渡すしかない)という認識であっていますしょう か?(上記補足コード参照) とんちんかんなことを言ってたら恐縮ですが、他クラスで問題なく呼び出せるのがクラスで、
ppaul

2021/07/22 00:34

集合論はわかりますか? クラスとインスタンスを考えるときのイメージは集合と要素です。 つまり、犬の集まりはクラスであり、ポチやJohnは犬クラスのインスタンスです。 犬や猫や猿のクラスは哺乳類のサブクラスですが、Pythonでは派生クラスとよびます。 ポチの鳴き声は「ポチ.鳴き声」という属性で表すこともできますが、ポチが鳴くという動作だと思えば「ポチ.鳴く()」というメソッドで表すこともあります。 ポチは日本の犬なので「ポチ..鳴き声」は「ワン」なのでしょうが、Johnが米国の犬だとすると「John.鳴き声」は「Bow-wow」かもしれません。こういうのをインスタンス変数とよびます。 というように、クラスとかインスタンスは呼び出し型の問題ではありません。
nudesudesu

2021/07/22 01:00 編集

クラスとインスタンスの定義の違いについて大変わかりやすい解説でよくわかりました。 以前、クラスは鯛焼の型、インスタンスは鯛焼きと聞いたことがあったのですが 型に対して処理するコンストラクタに対して、型から作られた鯛焼きに対して処理するのが__call__である。 つまり、集合自体の定義を変える、要素の定義を変えるは、 必要に応じて処理のタイミングが違うので使い分けが必要、いう理解でよりいでしょうか?
ppaul

2021/07/22 01:20

クラスは鯛焼の型、インスタンスは鯛焼き、という説明で理解したければ以下のようになります。 鯛焼は食べる対象であって、鯛焼が何かをすることは期待していませんね。 一方、ねじ回しというのは道具であって、それを何かに使うものです。 Pythonでは、道具を作るときには普通は関数定義文(def)を使って関数を定義します。 しかし、少しづつ違う動きをする関数をたくさん作りたい場合、は関数定義文では手間がかかります。 そういう場合に使われるのが高階関数と__call__を持つクラスです。 こうかい関数について知りたければ以下を読んでください。 https://docs.python.org/ja/3/library/functools.html?highlight=functool __call__を持つクラスは、いろいろな大きさのねじ回しを作るためのねじ回しの型ということもできます。鯛焼は全部同じ大きさの鯛焼ができるのに、いろいろな大きさのねじ回しができるのはおかしい、と思うかもしれませんが、それは鯛焼の型を使う説明が良くないためです。いろいろな大きさのねじ回しを作る製造装置だとでも思ってください。 私が例で示したコードでは、test_call_5というCallTestクラスのインスタンスは、引数を5倍するという道具(比喩でいうとねじ回し)、test_call_7は引数を5倍するという道具です。
nudesudesu

2021/07/22 12:53

大変勉強になりました!ありがとうございます!!
nudesudesu

2021/07/22 12:54

わかりやすかったです!
guest

0

ベストアンサー

文法だけを単純に考えるなら、

普通のメソッドと違い呼び出し時のメソッド名が省略できる

ことにしか違いがありません。

pyhton

1class Hoge: 2 def __init__(self, *args, **kwargs): 3 ... 4 5 def exec(self, *args, **kwargs): 6 ... 7 8 def __call__(self, *args, **kwargs): 9 ... 10 11a = Hoge(a=3, b=4) 12 13a.exec(c=1, d=4) # execメソッドの呼び出し 14a(c=1, d=4) # __call__メソッドの呼び出し

a.exec(...)a(...)短く書けるだけです。

したがって機能的なメリットがあって採用するようなものではありません。
どちらかというと「メソッドの名前をどう付けるといいのか?」「このクラスの役割は何か?」というような問題意識の文脈で語られるべきものかと思います。
そのクラスを使ってコードを書いてみた時に、メソッド名が付いていることが冗長な場合(例えば上のようにexecみたいなふんわりした名前にせざるをえないとか)に使えばいいものです。


素朴にインスタンス作成時にコンストラクタに引数を持たせるほうがシンプルかなと思うのですが、__call__を使う利点などありますでしょうか?
コンストラクタ: __init__ では対応できないことなどありますでしょうか?

下の文をまに受けるなら、メソッドの利点はある? コンストラクタで対応できないことがある? と聞いているのと同じなのですが、その自覚はあったでしょうか?


(特に関係ないこと)
__init__ のことをPyhton界隈ではコンストラクタとはあまり呼ばないです。
なぜ質問のコードには"何もしない文"であるpassが登場するのでしょうか。本当はここにコードがあるけれど引用では省略するという意味でしたら、Ellipsis リテラル...を使うのも(慣習として)ありです。

投稿2021/07/20 23:33

編集2021/07/20 23:57
quickquip

総合スコア11235

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nudesudesu

2021/07/21 23:41 編集

まず回答ありがとうございます。 文法的な書き方にしか違いがなく、普通のメソッドと違い呼び出し時のメソッド名が省略できることにしか違いがないということで理解しました。大変助かりました、 2点不明があり確認させてください。(理解不足だったら大変恐縮です、、) 1._「_init__ のことをPyhton界隈ではコンストラクタとはあまり呼ばないです。」 様々な記事でそういう記載があったのですが、それら全部が間違いなのでしょうか (外部サイトで恐縮ですが、参考:https://techacademy.jp/magazine/18842) 2 「なぜ質問のコードには"何もしない文"であるpassが登場するのでしょうか」 returnしないものに関しては、処理の末尾でインデントしやすいのでコーディングの観点で記載しましたが問題ないでしょうか?(いずれにせよそんな慣習はないという答えになる?)
quickquip

2021/07/22 03:27 編集

正確にはPython公式界隈と書いた方がよかったかもしれません。 https://docs.python.org/ja/3/reference/datamodel.html#object.__init__ > 引数はクラスのコンストラクタ式に渡したものです。 とありますが、__init__≠コンストラクタ でないとこの文の意味が通りません。 Python公式だと class Hoge: pass に対して Hoge() という呼び出しのことをコンストラクタ式、Hogeのことをコンストラクタと呼んでいます。 https://docs.python.org/ja/3/library/datetime.html#datetime.date.today のちょっと上 > 他のコンストラクタ、および全てのクラスメソッドを以下に示します とあります。つまりPython公式は date.today() や date.fromtimestamp(timestamp) や date.fromordinal(ordinal) のような、インスタンスを生成する呼び出しのことをコンストラクタと呼んでいます。 https://docs.python.org/ja/3/library/bz2.html#bz2.open > BZ2File のコンストラクタと同様に、引数 filename には(略) bz2.openの引数が https://docs.python.org/ja/3/library/bz2.html#bz2.BZ2File と同じだと言っています。ここでも、bz2.BZ2File のことをコンストラクタと呼んでいます。 公式が用語をどう使っているかに頓着しない限り、__init__のことをコンストラクタと呼びたくなるのも、他の言語から来た人は違和感を持つのもよく分かります。 > returnしないものに関しては、処理の末尾でインデントしやすいのでコーディングの観点で記載しましたが問題ないでしょうか? そのような慣習はないので、他の人が見たらギョッとします。 よくない言葉で言えば"気持ちが悪い"です そんな書き方をするコードを読むことはないでしょうから、そういう癖は付けない方がよいのでは? と個人的には思います。
nudesudesu

2021/07/22 12:52

ありがとうございます!大変勉強になりました!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問