この記事を読むのに約5分かかります。
Pythonには多くの黒い魔法があります。あなたの心を分割しないために、今日はメタクラスについてのみ話します。メタクラスのこの機能には、2つの極端な見方があります。
•この機能はすごすぎます。これは全能のアラジンの魔法のランプです。Pythonの強みを示すためにそれを使用する機会を見つける必要があります。 •この機能は危険すぎて、人々がそれを悪用するのを混乱させます。一度開くと、デーモンが解放され、コードの保守が困難になります。
今日は、メタクラスがアラジンの魔法のランプなのか、パンドラの箱なのかを見ていきます。
多くの本はメタクラスに翻訳されますが、これは文字通りにしか理解されません。メタは確かにメタであり、ソースであり、翻訳は問題ありません。ただし、理解するときは、メタをデータを説明するデータを超越するものとして理解する必要があります。実際、メタクラスのメタはギリシャ語のメタに由来し、次の2つの意味があります。
•技術用語メタデータなどの「超えて」とは、データを説明するデータを超えていることを意味します。 •専門用語のメタモルフォーゼなどの「変化」とは、変化の形を意味します。
したがって、メタクラスはクラスを記述するスーパークラスであると同時に、サブクラスの形式を変更できることが理解できます。これはメタデータの定義に似ていますか?プログラミングでこの機能をどのように使用しますか?
非常に便利。メタクラスがない場合、サブクラスは親クラスを継承し、親クラスはサブクラスに対して操作を実行できませんが、メタクラスを使用すると、動的にカスタマイズおよび変更できるデコレータのように、サブクラスを操作できます。クラス、メタクラスは、そのサブクラスを継承するように動的にカスタマイズまたは変更できます。
メタクラスをデコレータのようにカスタマイズおよび変更して、そのサブクラスを継承できることはすでにご存知でしょう。これが、メタクラスが解決できる実際的な問題です。たとえば、大規模なインテリジェント音声アシスタントプロジェクトでは、10,000の音声対話シナリオがあり、各シナリオは異なるチームによって開発されています。インテリジェントボイスアシスタントのコアチームメンバーとして、各サブシナリオの実装の詳細を理解することは不可能です。
さまざまなシーンを動的に実験する場合、今日のシーンAとBの構成を実験する必要があることが多く、明日の実験BとCの構成では、構成ファイルだけで数万行のオーダーであり、作業負荷は小さくありません。この動的構成の概念を使用して、テキスト構成ファイルに従って、エンジンに必要なPythonクラスを動的にロードさせることができます。
よくわからない場合は、YAMLを知っておく必要があります。YAMLは、データを簡単にシリアル化および逆シリアル化できる有名なPythonツールです。YAMLObjectを使用すると、サブクラスのいずれかでシリアル化と逆シリアル化(シリアル化と逆シリアル化)をサポートできます。デシリアライズ)。シリアル化と逆シリアル化について明確にする必要があります。
•シリアル化:プログラムの実行中、すべての変数またはオブジェクトがメモリに保存されます。プログラムが呼び出されると、これらの変数またはオブジェクトが占有していたメモリが再利用されます。変数とオブジェクトをディスクに永続的に保存したり、ネットワークを介して送信したりするには、変数またはオブジェクトをバイナリストリームに変換する必要があります。それをバイナリストリームに変換するプロセスはシリアル化です。 •逆シリアル化:逆シリアル化とは、プログラムの実行中にプログラムをディスクから読み取ることができないことを意味します。シリアル化されたオブジェクトまたは変数をディスクからメモリに転送する必要があり、バイナリストリームが元のデータに変換されます。フォーマット。このプロセスを逆シリアル化と呼びます。
これで、さまざまな形式の10,000個のYAML構成ファイルができました。元々、これらの構成ファイルをロードするには、10,000個のクラスを作成する必要があります。メタクラスでは、メタクラススーパークラスを実装してから、このメタクラスを継承するサブクラスを実装するだけで済みます。さまざまな構成ファイルに応じてさまざまなクラスを自動的にプルできるため、効率が大幅に向上します。
クラスの作成とインスタンス化のステップを完全に理解できるように、ipythonで手動でコーディングし、各ステップで何が出力されるかを確認してください。
In[15]:classMymeta(type):...: def __init__(self, name, bases, dic):...:super().__init__(name, bases, dic)...:print('===>Mymeta.__init__')...:print(self.__name__)...:print(dic)...:print(self.yaml_tag)...:...: def __new__(cls,*args,**kwargs):...:print('===>Mymeta.__new__')...:print(cls.__name__)...:return type.__new__(cls,*args,**kwargs)...:...: def __call__(cls,*args,**kwargs):...:print('===>Mymeta.__call__')...: obj = cls.__new__(cls)...: cls.__init__(cls,*args,**kwargs)...:return obj
...:
In[16]:
In[16]:
In[16]:classFoo(metaclass=Mymeta):...: yaml_tag ='!Foo'...:...: def __init__(self, name):...:print('Foo.__init__')...: self.name = name
...:...: def __new__(cls,*args,**kwargs):...:print('Foo.__new__')...:return object.__new__(cls)...:===>Mymeta.__new__
Mymeta
===> Mymeta.__init__
Foo
{'__ module__':'__main__','__qualname__':'Foo','yaml_tag':'!Foo','__init__':<function Foo.__init__ at 0x0000000007EF3828>,'__new__':<function Foo.__new__ at 0x0000000007EF3558>}!Foo
In[17]: foo =Foo('foo')===>Mymeta.__call__
Foo.__new__
Foo.__init__
In[18]:
上記の実行結果から、クラスFoo()定義を定義するときに、MyMetaの __new__
メソッドと __init__
メソッドが順番に呼び出されてFooクラスが構築され、foo = Foo()が呼び出されてクラスのインスタンスが作成されることがわかります。 MyMetaの __call__
メソッドを呼び出して、Fooクラスの __new__
メソッドと __init__
メソッドを呼び出します。
上記の例を実行すると、多くのことを理解できます。通常の状況では、親クラスのサブクラスのプロパティを操作することはできませんが、メタクラスは操作できます。別の理解方法:メタクラス、デコレータ、およびクラスデコレータはすべてメタプログラミングとして分類できます。
メタクラスの基本的な原則を理解するには、Pythonタイプモデルを深く理解する必要があります。以下、3点で説明します。
驚かれるかもしれませんが、実際、クラス自体はtypeと呼ばれるクラスのインスタンスにすぎません。 Pythonタイプの世界では、タイプタイプは創造の神です。これは、コードで確認できます。
In [2]: #Python3はPython2に似ています
...: classMyClass:...: pass
...:...: instance =MyClass()...:in[3]:type(instance)...:
Out[2]: __main__.MyClass
In [4]:type(MyClass)...:
Out[4]: type
In [5]:
ご覧のとおり、インスタンスはMyClassのインスタンスであり、MyClassは単なる「神」タイプのインスタンスです。
__call__
演算子のオーバーロードに他なりませんステートメントの最後でクラスを定義すると、実際に発生するのは、Pythonが型の __call__
演算子を呼び出すことです。簡単に言えば、クラスを定義するときは、次のように記述します。
classMyClass:
data =1
Pythonが実際に実行するのは、次のコードです。
class=type(classname, superclasses, attributedict)
ここで、等号の右側にあるtype(classname、superclasses、attributedict)は、typeの __call__
演算子のオーバーロードであり、これはさらに以下を呼び出します。
type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)
もちろん、これはすべて、次のようなコードで確認できます。
In [5]:classMyClass:...: data =1...:...: instance =MyClass()...:
In [6]: MyClass, instance
...:
Out[6]:(__main__.MyClass,<__main__.MyClass at 0x4ef5188>)
In [7]: instance.data
...:
Out[7]:1
In [8]: MyClass =type('MyClass',(),{'data':1})...: instance =MyClass()...:
In [9]: MyClass, instance
...:
Out[9]:(__main__.MyClass,<__main__.MyClass at 0x4f40748>)
In [10]: instance.data
...:
Out[10]:1
In [11]:
MyClassの通常の定義は、type演算子を手動で呼び出した結果とまったく同じであることがわかります。
__call__
演算子オーバーロードメカニズムを置き換えることにより、通常のクラスは「変形を超えて」実際、上記の点を理解すると、メタクラスにその才能を発揮する機会を与えるのはPythonのクラス作成メカニズムであることがわかります。
タイプMyClassのメタクラスをMyMetaに設定すると、MyClassはネイティブタイプによって作成されなくなりますが、MyMetaの __call__
演算子オーバーロードが呼び出されます。
class=type(classname, superclasses, attributedict)
# になる
class=MyMeta(classname, superclasses, attributedict)
ただし、すべてに長所と短所があり、特にメタクラスが存在します。ご覧のとおり、メタクラスは通常のPythonタイプモデルを「歪め」ます。したがって、不注意に使用した場合、コードベース全体へのリスクは計り知れません。
言い換えると、メタクラスは、フレームワークレベルのPythonライブラリを開発するときに、Python開発者の小さなグループによってのみ使用されます。アプリケーション層では、メタクラスは多くの場合適切な選択ではありません。
この記事は、Pythonクラスを作成するプロセスからメタクラスの役割を理解するのに役立ちます。
メタクラスはブラックマジックです。適切に使用すれば天国を意味し、そうでなければ地獄を意味します。
(終了)