メタクラスは typeを明記して継承しなければいけないそうですががなぜですか。objectを明記して継承させる場合や、何も明記しない場合とどこが違うのでしょうか。
*明記... class A(type): の(type)のこと
よくある普通のクラスはtype
のインスタンスです。type
を継承するとメタクラスが作れます。class B(metaclass=A):
のような記述は、B
クラスそのものをA
のインスタンスとして作りますよ、ということを表します。
python
1>>> class Hoge:
2... pass
3...
4>>> class Fuga(type):
5... pass
6...
7>>> class Piyo(metaclass=Fuga):
8... pass
9...
10>>> type(Hoge)
11<class 'type'>
12>>> type(Fuga)
13<class 'type'>
14>>> type(Piyo)
15<class '__main__.Fuga'>
Piyo
だけ型が違いますね。これがメタクラスの機能です。
type型とobject型の違い(どちらがどちらのスーパークラスか)もよくわかりません。全ての型がtype型のサブクラスだということは知っています。
object
がpython3の世界の最上位クラスです。type
もobject
のサブクラスです。
python
1>>> object.__bases__
2()
3>>> type.__bases__
4(<class 'object'>,)
でも、これは約束事だし、知っておいてもそんなにメリットはないかもしれません。
b.bの値をインスタンス作成時の任意値にしたいのですがどうすればいいですか。
インスタンス作成、b = B(任意値)とした時b.b = 任意値としたいです。以下のコードだとエラーとなってしまいます。
まずメタクラスはあくまでもクラスの拡張ですから、
python
1class A(type):
2 def __new__(cls, *args):
3 print("Called A's __new__!")
4 _s = type.__new__(cls, *args)
5 _s.a = "This is A's attr"
6 return _s
7 def __init__(self, *args):
8 print("Called A's __init__!")
9 self.b = args[1]
10
11print("def B")
12class B(metaclass=A):
13 pass
14print("end of def B")
15
16b = B()
17print(b.a,b.b, sep="\n")
18""" =>
19def B
20Called A's __new__!
21Called A's __init__!
22end of def B
23This is A's attr
24()
25"""
という動作になることを理解しておかないといけないのです。A
の__init__
で行っている操作というのは、たとえば
python
1class A(type):
2 def __new__(cls, *args):
3 print("Called A's __new__!")
4 _s = type.__new__(cls, *args)
5 _s.a = "This is A's attr"
6 return _s
7 def __init__(self, *args):
8 print("Called A's __init__!")
9 self.b = args[1]
10
11class B(int, metaclass=A):
12 pass
13
14b = B()
15print(b.a,b.b, sep="\n")
16""" =>
17Called A's __new__!
18Called A's __init__!
19This is A's attr
20(<class 'int'>,)
21"""
こういう操作を可能にする(ここではB
の定義時に引数を受け取っている)、ということなのです。どの段階で何を受け取るのかを注意してください。
質問文のやりたいことはインスタンスをいじりたいということなので、たぶん普通のクラスの継承でやった方がいいのですが。どうしてもやるなら、
python
1class A(type):
2 def __init__(self, *args):
3 def init(self, arg):
4 self.b = arg
5 self.__init__ = init
6
7class B(metaclass=A):
8 pass
9
10b = B("hoge")
11print(b.b) # => hoge
という感じでしょうか。A
をメタクラスにするクラスの__init__
はすべてデフォルトで任意値
を一つ引数に取り、それをb
という属性に束縛するという動作になります。