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

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

ただいまの
回答率

87.48%

メタクラスにおける__new__()の返り値はtype型、object型のどちらですか。

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 497

score 15

はじめに

pythonを勉強中のものです。
メタクラスの動作がよくわかりません。
不明点を以下にまとめました。
ご教示いただけますと嬉しいです。

不明点
  1. メタクラスは type明記して継承しなければいけないそうですががなぜですか。objectを明記して継承させる場合や、何も明記しない場合とどこが違うのでしょうか。
    *明記... class A(type): の(type)のこと

  2. type型とobject型の違い(どちらがどちらのスーパークラスか)もよくわかりません。全ての型がtype型のサブクラスだということは知っています。

  3. b.bの値をインスタンス作成時の任意値にしたいのですがどうすればいいですか。
    インスタンス作成、b = B(任意値)とした時b.b = 任意値としたいです。以下のコードだとエラーとなってしまいます。

TypeError: B() takes no arguments


メタクラスの勉強をしているのでclass Bのメタクラスは必ずclass Aでお願いします。

前提のコード
class A(type):
    def __new__(cls, *args):
        print("Called A's __new__!")
        _s = type.__new__(cls, *args)
        _s.a = "This is A's attr"
        return _s
    def __init__(self, *args):
        print("Called A's __init__!")
        self.b = args[1]

class B(metaclass=A):
    pass


b = B()
print(b.a,b.b, sep="\n")
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • quickquip

    2019/06/21 22:25

    初心者マークは外した方がいいんじゃないでしょうか? (Pythonの中でもとりわけ難しい話題でしょう)

    キャンセル

回答 1

checkベストアンサー

+3

メタクラスは typeを明記して継承しなければいけないそうですががなぜですか。objectを明記して継承させる場合や、何も明記しない場合とどこが違うのでしょうか。
*明記... class A(type): の(type)のこと

よくある普通のクラスはtypeのインスタンスです。typeを継承するとメタクラスが作れます。class B(metaclass=A):のような記述は、BクラスそのものをAのインスタンスとして作りますよ、ということを表します。

>>> class Hoge:
...     pass
...
>>> class Fuga(type):
...     pass
...
>>> class Piyo(metaclass=Fuga):
...     pass
...
>>> type(Hoge)
<class 'type'>
>>> type(Fuga)
<class 'type'>
>>> type(Piyo)
<class '__main__.Fuga'>


Piyoだけ型が違いますね。これがメタクラスの機能です。

type型とobject型の違い(どちらがどちらのスーパークラスか)もよくわかりません。全ての型がtype型のサブクラスだということは知っています。

objectがpython3の世界の最上位クラスです。typeobjectのサブクラスです。

>>> object.__bases__
()
>>> type.__bases__
(<class 'object'>,)

でも、これは約束事だし、知っておいてもそんなにメリットはないかもしれません。

b.bの値をインスタンス作成時の任意値にしたいのですがどうすればいいですか。
インスタンス作成、b = B(任意値)とした時b.b = 任意値としたいです。以下のコードだとエラーとなってしまいます。

まずメタクラスはあくまでもクラスの拡張ですから、

class A(type):
    def __new__(cls, *args):
        print("Called A's __new__!")
        _s = type.__new__(cls, *args)
        _s.a = "This is A's attr"
        return _s
    def __init__(self, *args):
        print("Called A's __init__!")
        self.b = args[1]

print("def B")
class B(metaclass=A):
    pass
print("end of def B")

b = B()
print(b.a,b.b, sep="\n")
""" =>
def B
Called A's __new__!
Called A's __init__!
end of def B
This is A's attr
()
"""

という動作になることを理解しておかないといけないのです。A__init__で行っている操作というのは、たとえば

class A(type):
    def __new__(cls, *args):
        print("Called A's __new__!")
        _s = type.__new__(cls, *args)
        _s.a = "This is A's attr"
        return _s
    def __init__(self, *args):
        print("Called A's __init__!")
        self.b = args[1]

class B(int, metaclass=A):
    pass

b = B()
print(b.a,b.b, sep="\n")
""" =>
Called A's __new__!
Called A's __init__!
This is A's attr
(<class 'int'>,)
"""

こういう操作を可能にする(ここではB定義時に引数を受け取っている)、ということなのです。どの段階で何を受け取るのかを注意してください。

質問文のやりたいことはインスタンスをいじりたいということなので、たぶん普通のクラスの継承でやった方がいいのですが。どうしてもやるなら、

class A(type):
    def __init__(self, *args):
        def init(self, arg):
            self.b = arg
        self.__init__ = init

class B(metaclass=A):
    pass

b = B("hoge")
print(b.b) # => hoge

という感じでしょうか。Aをメタクラスにするクラスの__init__はすべてデフォルトで任意値を一つ引数に取り、それをbという属性に束縛するという動作になります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/24 00:16

    @hayataka2049さん
    ご回答ありがとうございます。疑問が解けました。

    キャンセル

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

  • ただいまの回答率 87.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る