【質問①】なぜ多くの教本等ではインスタンスを生成する際には'init'を使うのでしょうか。必ず呼ばれる'new()'を用いれば良いのではと思ってしまいます。
そもそも、__new__が必ず呼ばれる、という事が間違っています。
参考にされているサイトでは、「super().__init__を呼ばなければ」という条件を出していますが、そうであればsuper().__new__を呼び出さない事もできます。
python
1class Hoge:
2 def __new__(cls, *args, **kwargs):
3 print('Hoge.new')
4 return super().__new__(cls, *args, **kwargs)
5
6class Piyo(Hoge):
7 def __new__(cls, *args, **kwargs):
8 print('Piyo.new')
9 return object.__new__(cls, *args, **kwargs)
「__new__」は、インスタンスを生成するメソッド。「__init__」は、インスタンスを初期化するメソッドです。
ですので、初期化を行うコードは「__init__」に書くのが自然ですし、皆そのように従っているだけかと思います。
【質問②】上記Qiitaのページにあるメタクラスとは何者なのでしょうか。
メタクラスに関して、自分も最近真面目に調べた程度の浅い知識なのですが。
その時に参考にしたのが、以下のサイトです。
一言で言えば、「クラスのクラス」です。と言ってもさっぱりわかりませんが。
Pythonにおいて、モジュールもメソッドも全てobjectです。それはクラスに関しても例外ではありません。
そして、全てのobjectは型オブジェクトを持ちます。
普通にクラスを定義した場合、型オブジェクトはtypeになります。
>>> class Hoge:
... pass
...
>>> type(Hoge)
<class 'type'>
>>>
この型オブジェクトを、自分で定義した物がメタクラスです。
>>> class Metaclass(type):
... pass
...
>>> class Hoge(metaclass=Metaclass):
... pass
...
>>> type(Hoge)
<class '__main__.Metaclass'>
>>>
では、どのように使うかと言えば、クラス定義をした際にそのクラスインスタンスに対して処理する用途などに使います。
そういった意味ではデコレータと似ていると思いますが、デコレータと違う点は。
- 「
@decorator」というような記述をしなくてもよい。
- クラス、もしくはその派生クラスにしか適用できない。
- デコレータは生成されたクラスインスタンスに対して処理するのに対し、メタクラスはクラスインスタンスを生成する前にも処理を行うことができる。
です。
例えば、PyQtの場合、自分のクラスにシグナルを追加する場合、クラス属性にシグナルを追加します。
python
1from PyQt5 import QtCore
2class MyObject(QtCore.QObject):
3 mysignal = QtCore.pyqtSignal()
または、SQLAlchemyのテーブル定義では、クラス属性にテーブル名なりカラム定義なりを追加します。
python
1from sqlalchemy import Column, String
2from sqlalchemy.ext.declarative import declarative_base
3Base = declarative_base()
4class MyTable(Base):
5 __tablename__ = 'mytable'
6 name = Column(String, primary_key)
7Base.metadata.create_all(engine)
このように、クラス属性を定義して振る舞いを指定する事は何らかのモジュールを使っている上で多々ある事ですが、大抵の場合属性を定義するだけではなく、何らかの処理が必要になるはずです。
python
1class Parent:
2 subclasses = {}
3
4 @staticmethod
5 def init_class(cls):
6 Parent.subclass[cls.__clsname__] = cls
7
8class Hoge(Parent):
9 __clsname__ = 'hoge'
10Parent.init_cls(Hoge)
上記のようにParent.init_classを呼び出せば処理ができますが、実際にはそのような処理は記述しません。
そこで、メタクラスを使います。
python
1class Metaclass(type):
2 subclasses = {}
3 def __init__(cls, name, bases, attrs):
4 super().__init__(name, bases, attrs)
5 Metaclass.subclasses[cls.__clsname__] = cls
6
7class Parent(metaclass=Metaclass):
8 __clsname__ = 'parent'
9 pass
10
11class Hoge(Parent):
12 __clsname__ = 'hoge'
13
14print(Metaclass.subclasses)
このようにしておけば、Parentを継承するクラスは__clsname__を定義しておくだけでOKというようになります。
そもそもメタクラスのようなメタプログラミング技法は、ライブラリ作成者がディベロッパに対して余計な手間を省くために使用されることが多いですから、一般的にはあまり使用しないと思います。
退会済みユーザー
2020/01/26 13:37