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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

Q&A

解決済

2回答

901閲覧

Python typeオブジェクトに__call__メソッドがないのにcallableなのは何故か?また仕様はどうなっているのか?

_Victorique__

総合スコア1392

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

1グッド

3クリップ

投稿2019/03/20 06:06

題名の通りです。__call__メソッドがないのにも関わらず呼び出しが出来てしまいます。
また、その挙動がよく分かりません。実行してみた感じだとそのオブジェクトの初期値?的なものが出力されているように感じます。
引数を与えるとその値がそのまま出力されます。ここら辺の仕様が確認できるところはありますでしょうか?

Python

1import pprint 2 3n = 1 4n_type = type(n) # <class 'int'> 5n_type() # 0 6n_type.__call__() # 0 7 8pprint.pprint(dir(n), compact=True) 9""" 10['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', 11 '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', 12 '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', 13 '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', 14 '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', 15 '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', 16 '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', 17 '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', 18 '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', 19 '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', 20 '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 21 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 22 'real', 'to_bytes'] 23""" 24pprint.pprint(dir(type(n)), compact=True) 25""" 26['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', 27 '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', 28 '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', 29 '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', 30 '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', 31 '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', 32 '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', 33 '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', 34 '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', 35 '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', 36 '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 37 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 38 'real', 'to_bytes'] 39"""
LouiS0616👍を押しています

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

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

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

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

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

otn

2019/03/20 07:00

n_type.__call__ ⇒ <method-wrapper '__call__' of type object at ~~> ですね。「method-wrapper」ググったけどよくわからず。
_Victorique__

2019/03/20 07:05

> otn様 こういう見えない部分があると凄く気になってしまいます。 何か別のものが呼ばれて最終的にcallが呼ばれているってことなんでしょうかね? よくわからないです。
guest

回答2

0

ベストアンサー

自分も type() の返す type オブジェクトに対して、呼び出しメソッドを使ったことがないので、知らなかったのですが、流れを追ってみたら以下のようです。

callable かどうかの判定

Python に置ける callable かどうかは __call__ が定義されているかどうかではなく、厳密にはオブジェクトの tp_call ポインタが NULL かどうかで見ている。
なので、__call__ を定義していなくても、tp_call が NULL でなかったら、callable である。

c

1int PyCallable_Check(PyObject *x) 2{ 3 if (x == NULL) 4 return 0; 5 return x->ob_type->tp_call != NULL; 6}

cpython/object.c at master · python/cpython

An optional pointer to a function that implements calling the object. This should be NULL if the object is not callable. The signature is the same as for PyObject_Call()

Type Objects — Python 3.7.3rc1 documentation

type オブジェクトに対して呼び出すと、なにが呼ばれるか

type() が返す type オブジェクトは組み込み型なので、tp_call がどうなっているのかと見てみたところ、NULL (0) ではなく、type_call() という関数が設定されていた。

line3662

1(ternaryfunc)type_call, /* tp_call */

cpython/typeobject.c at 3.6 · python/cpython

この関数の中身を見てみると、

line6198

1static PyObject * 2type_call(PyTypeObject *type, PyObject *args, PyObject *kwds) 3{ 4 PyObject *obj; 5 6 if (type->tp_new == NULL) { 7 PyErr_Format(PyExc_TypeError, 8 "cannot create '%.100s' instances", 9 type->tp_name); 10 return NULL; 11 } 12 13 obj = type->tp_new(type, args, kwds); 14 1516 17 return obj; 18}

cpython/typeobject.c at 3.6 · python/cpython

tp_new() で type (ここでの type 変数は type() 引数に渡したオブジェクトのクラスのこと) のインスタンスを作成して返しているようでした。
これが type(Class) が返す type オブジェクトを呼び出すと、元の Class のインスタンスが作成されるという挙動につながっているのではないでしょうか。

実行してみた感じだとそのオブジェクトの初期値?的なものが出力されているように感じます。

class Hoge: pass hoge1 = Hoge() hoge_type = type(hoge1) hoge2 = hoge_type() # 同じクラス print(type(hoge1) is type(hoge2)) # True

ちなみにint オブジェクトは引数を与えずコンストラクタを作成すると、値は0になります。

a = int() print(a) # 0

投稿2019/03/20 07:36

編集2019/03/20 07:42
tiitoi

総合スコア21956

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

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

_Victorique__

2019/03/20 07:46

詳しい回答有難う御座います! なんか不思議な仕様ですね。挙動は概ね予想通りでしたが何故こんな仕様なのでしょうか? 何が嬉しいのかが理解できません。tp_callのポインタがNULLじゃないのはやっぱりそれだけの理由があるということなんでしょうね。。。
tiitoi

2019/03/20 07:56 編集

どのライブラリにもドキュメントに明文化されていない仕様 (例えば、ドキュメントに乗っていない関数であったり、挙動であったり) というのがあります。 それらは開発者が内部的な実装の都合等でそうしたものですが、ドキュメント化されていないということは、外部の利用する側の人にその仕様を利用してもらうことは想定していないということです。 上記の挙動も、CPython の開発者でないのでわかりませんが、内部的になんらかの都合があり、そのようにしているのだと思います。しかし、ドキュメントに載っていないということは、Python を使う側が利用することは想定していないと思うので、その挙動 (隠し機能のようなもの) はコードを書く際に使わないほうがよいと思います。(将来的に言及なしに急に変わったりする可能性もあるため) 普通に Python を使っていれば、type() は型を確認するのに使うぐらいで、その返り値の type オブジェクトを呼び出したいというケースはないと思います。
_Victorique__

2019/03/20 08:02

確かにそうですね。深追いはしないことにします。 偶発的に呼び出してしまって疑問に思ったため質問させていただきました。 有難う御座いました!
guest

0

Pythonの組み込み関数のドキュメントが手掛かりでしょうね。

type

引数が1つだけの場合、object の型を返します。返り値は型オブジェクトで、一般に object.class によって返されるのと同じオブジェクトです。

とあるので、最初にtypeを使うとintクラスが返されます。

callable

object 引数が呼び出し可能オブジェクトであれば True を、そうでなければ False を返します。この関数が真を返しても、呼び出しは失敗する可能性がありますが、偽であれば、 object の呼び出しは決して成功しません。なお、クラスは呼び出し可能 (クラスを呼び出すと新しいインスタンスを返します) です。また、インスタンスはクラスが call() メソッドを持つなら呼び出し可能です。

クラスなので呼び出し可能です。この場合に呼び出されてるのは__call__ではなく__init__ですね。

python

1In [36]: class A: 2 ...: def __init__(self): 3 ...: print("__init__ of class A") 4 ...: def __call__(self): 5 ...: print("__call__ of class A") 6 ...: 7 ...: 8 9In [37]: a = A() 10__init__ of class A 11 12In [38]: b = type(a) 13 14In [39]: b() 15__init__ of class A 16Out[39]: <__main__.A at 0x10aed1710> 17 18In [40]: b()() 19__init__ of class A 20__call__ of class A

投稿2019/03/20 08:08

tachikoma

総合スコア3601

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問