pythonのメタクラスの理解

12092 ワード

1.クラスもオブジェクト
ほとんどのプログラミング言語では、クラスはオブジェクトを生成する方法を記述するコードセグメントのセットです.Pythonでは、この点は依然として成立しています.
>>> class ObjectCreator(object):
…       pass
…
>>> my_object = ObjectCreator()
>>> print my_object
<__main__.objectcreator object="" at=""/>

しかし、Pythonのクラスはそれだけではない.クラスもオブジェクトです.はい、そうです.対象です.キーワードclassを使用すると、Python解釈器は実行時にオブジェクトを作成します.
次のコード・セグメント:
>>> class ObjectCreator(object):
…       pass
…

ObjectCreatorという名前のオブジェクトがメモリに作成されます.このオブジェクト(クラスオブジェクトObjectCreator)には、オブジェクト(インスタンスオブジェクト)を作成する機能があります.しかし、その本質は依然としてオブジェクトであり、次のような操作を行うことができます.
  • 変数
  • に割り当てることができます
  • コピーできます
  • プロパティを追加できます
  • 関数パラメータとして渡すことができます
  • 次に例を示します.
    >>> print ObjectCreator     #         ,           
    
    >>> def echo(o):
    …       print o
    …
    >>> echo(ObjectCreator)                 #              
    
    >>> print hasattr(ObjectCreator, 'new_attribute')
    Fasle
    >>> ObjectCreator.new_attribute = 'foo' #           
    >>> print hasattr(ObjectCreator, 'new_attribute')
    True
    >>> print ObjectCreator.new_attribute
    foo
    >>> ObjectCreatorMirror = ObjectCreator #             
    >>> print ObjectCreatorMirror()
    <__main__.objectcreator object="" at=""/>

    2.動的にクラスを作成する
    クラスもオブジェクトなので、実行時に他のオブジェクトと同じように動的に作成できます.まず、関数にクラスを作成し、classキーを使用すればいいです.
    >>> def choose_class(name):
    …       if name == 'foo':
    …           class Foo(object):
    …               pass
    …           return Foo     #      ,      
    …       else:
    …           class Bar(object):
    …               pass
    …           return Bar
    …
    >>> MyClass = choose_class('foo')
    >>> print MyClass              #        ,      
    
    >>> print MyClass()            #              ,     
    <__main__.foo object="" at=""/>

    しかし、クラス全体のコードを自分で書く必要があるため、これはまだダイナミックではありません.クラスもオブジェクトなので、何かによって生成されなければなりません.classキーワードを使用すると、Pythonインタプリタは自動的にこのオブジェクトを作成します.しかし、Pythonの多くのことと同じように、Pythonは手動で処理する方法を提供しています.
    内蔵関数typeを覚えていますか?この古いが強力な関数は、オブジェクトのタイプが何なのかを知ることができます.
    >>> print type(1) #     
    
    >>> print type("1") #      
    
    >>> print type(ObjectCreator()) #       
    
    >>> print type(ObjectCreator) #    
    

    上の実行結果をよく見てみると、typeを使ってObjectCreatorを表示するタイプは、typeと答えているので、少し驚いているのではないでしょうか.の下を見る
    3.typeを使用したクラスの作成
    typeにはまったく異なる機能があり、クラスを動的に作成します.
    typeは、クラスの記述をパラメータとして受け入れ、クラスを返すことができます.(入力パラメータによって同じ関数が2つ全く異なる使い方を持つのは馬鹿なことですが、Pythonでは後方互換性を保つためです)
    typeはこのように動作します.
    type(クラス名、親クラス名からなるメタグループ(継承の場合は空)、属性を含む辞書(名前と値)
    たとえば、次のコードがあります.
    In [2]: class Test: #     Test 
       ...:     pass
       ...:
    In [3]: Test() #     Test      
    Out[3]: <__main__.test at=""/>

    このように手動で作成できます.
    Test2 = type("Test2",(),{}) #    Test2 
    In [5]: Test2() #     Test2      
    Out[5]: <__main__.test2 at=""/>

    クラス名として「Test 2」を使用し、クラスの参照として変数として使用することもできます.クラスと変数は異なり、ここで複雑にする理由はありません.すなわちtype関数の1番目の実パラメータは,クラスの名前を表す別の名前と呼ぶこともできる.
    In [23]: MyDogClass = type('MyDog', (), {})
    
    In [24]: print MyDogClass
    

    helpを使用してこの2つのクラスをテストします
    In [10]: help(Test) # help  Test 
    
    Help on class Test in module __main__:
    
    class Test(builtins.object)
     |  Data descriptors defined here:
     |
     |  __dict__
     |      dictionary for instance variables (if defined)
     |
     |  __weakref__
     |      list of weak references to the object (if defined)
    In [8]: help(Test2) # help  Test2 
    
    Help on class Test2 in module __main__:
    
    class Test2(builtins.object)
     |  Data descriptors defined here:
     |
     |  __dict__
     |      dictionary for instance variables (if defined)
     |
     |  __weakref__
     |      list of weak references to the object (if defined)

    4.typeを使用して属性付きクラスを作成する
    typeはクラスのプロパティを定義する辞書を受け入れます.
    >>> Foo = type('Foo', (), {'bar':True})

    次のように翻訳できます.
    >>> class Foo(object):
    …       bar = True

    Fooは通常のクラスとして使用できます.
    >>> print Foo
    
    >>> print Foo.bar
    True
    >>> f = Foo()
    >>> print f
    <__main__.foo object="" at="">
    >>> print f.bar
    True

    もちろん、このクラスに継承できます.したがって、次のコードがあります.
    >>> class FooChild(Foo):
    …       pass

    次のように書くことができます.
    >>> FooChild = type('FooChild', (Foo,),{})
    >>> print FooChild
    
    >>> print FooChild.bar   # bar    Foo    
    True

    注意:
    typeの2番目のパラメータは、文字列ではなく親の名前で、追加された属性はクラス属性であり、インスタンス属性ではありません.
    5.typeを使用してメソッド付きクラスを作成する
    最終的には、あなたのクラスの増加方法を望んでいます.適切な署名を持つ関数を定義し、属性として割り当てるだけです.
    インスタンスメソッドの追加
    In [46]: def echo_bar(self): #          
        ...:     print(self.bar)
        ...:
    
    In [47]: FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) # FooChild   echo_bar  ,          
    
    In [48]: hasattr(Foo, 'echo_bar') #  Foo  ,   echo_bar    
    Out[48]: False
    
    In [49]:
    
    In [49]: hasattr(FooChild, 'echo_bar') #  FooChild  ,   echo_bar    
    Out[49]: True
    
    In [50]: my_foo = FooChild()
    
    In [51]: my_foo.echo_bar()
    True

    静的メソッドの追加
    In [36]: @staticmethod
        ...: def testStatic():
        ...:     print("static method ....")
        ...:
    
    In [37]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar, "testStatic":
        ...: testStatic})
    
    In [38]: fooclid = Foochild()
    
    In [39]: fooclid.testStatic
    Out[39]: 
    
    In [40]: fooclid.testStatic()
    static method ....
    
    In [41]: fooclid.echo_bar()
    True

    クラスメソッドの追加
    In [42]: @classmethod
        ...: def testClass(cls):
        ...:     print(cls.bar)
        ...:
    
    In [43]:
    
    In [43]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar, "testStatic":
        ...: testStatic, "testClass":testClass})
    
    In [44]:
    
    In [44]: fooclid = Foochild()
    
    In [45]: fooclid.testClass()
    True

    Pythonでは、クラスもオブジェクトであり、動的にクラスを作成することができます.これがキーワードclassを使用するときにPythonが背後でやったことであり、これはメタクラスによって実現されます.
    6.いったい何が元類なのか(やっとテーマになった)
    メタクラスとは、クラスを作成するための「もの」です.クラスを作成するのは、クラスのインスタンスオブジェクトを作成するためではないでしょうか.しかし、Pythonのクラスも対象であることを学びました.
    メタクラスは、これらのクラス(オブジェクト)を作成するために使用され、メタクラスはクラスのクラスです.
    MyClass = MetaClass() #           ,      “ ”
    MyObject = MyClass() #  “ ”        

    typeがこのようにするのを見ました
    MyClass = type('MyClass', (), {})

    これは、関数typeが実際にメタクラスであるためです.typeはPythonが背後ですべてのクラスを作成するために使用するメタクラスです.なぜタイプではなくタイプがすべて小文字になっているのか知りたいですか?
    これはstrと一貫性を保つためであり、strは文字列オブジェクトを作成するためのクラスであり、intは整数オブジェクトを作成するためのクラスであると思います.typeはクラスオブジェクトを作成するクラスです.検査に合格できます.class__を選択します.Pythonの中のすべてのもの、注意して、私はすべてのものを指します--すべて対象です.これには、整数、文字列、関数、クラスが含まれます.これらはすべてオブジェクトであり、クラスから作成されます.このクラスはtypeです.
    >>> age = 35
    >>> age.__class__
    
    >>> name = 'bob'
    >>> name.__class__
    
    >>> def foo(): pass
    >>>foo.__class__
    
    >>> class Bar(object): pass
    >>> b = Bar()
    >>> b.__class__
    

    今、どちらに対しても_class__の_class__属性は何ですか?
    >>> a.__class__.__class__
    
    >>> age.__class__.__class__
    
    >>> foo.__class__.__class__
    
    >>> b.__class__.__class__
    

    したがって、メタクラスはクラスというオブジェクトを作成するものです.typeはPythonの内蔵メタクラスです.もちろん、自分のメタクラスを作成することもできます.
    **7. __metaclass__属性**
    クラスを定義するときに__を追加できます.metaclass__で行ないます.
    class Foo(object):
        __metaclass__ = something…
        ...  ...

    そうすればPythonはメタクラスでクラスFooを作成します.気をつけて、この中にはいくつかのテクニックがあります.まずclass Foo(object)と書きますが、クラスFooはメモリに作成されていません.Pythonはクラスの定義で__を探しますmetaclass__プロパティは、見つかったらPythonがクラスFooを作成し、見つからなかったら内蔵typeで作成します.次の言葉を何度も繰り返し読みます.次のコードを書くと、
    class Foo(Bar):
        pass

    Pythonは次の操作を行いました.
    Fooには_がありますmetaclass__この属性ですか?もしそうなら、Pythonが通過します.metaclass__Fooという名前のクラス(オブジェクト)を作成Pythonが見つからない場合_metaclass__,Bar(親)で検索を続行します.metaclass__プロパティを選択し、前と同じ操作を試みます.
    Pythonがどの親にも見つからない場合_metaclass__,モジュール階層で検索されます.metaclass__,同じ操作を試みます
    それでも見つからなかったら_metaclass__,Pythonは内蔵typeでこのクラスオブジェクトを作成します.
    今の問題は、あなたが__にいることです.metaclass__にはどんなコードが置かれていますか?答えは、クラスを作成できるものです.では、クラスを作成するには何が使えますか?type、またはtypeまたはサブクラス化typeに使用される任意のものでもよい.
    8.カスタムメタクラス
    メタクラスの主な目的は、クラスを作成するときに自動的にクラスを変更することです.通常、APIでは、現在のコンテキストに合致するクラスを作成することを望んでいます.
    バカな例を想定して、あなたのモジュールのすべてのクラスの属性は大文字であるべきだと決定します.いくつかの方法がありますが、1つはモジュールレベルで__を設定することです.metaclass__.この方法では、このモジュールのすべてのクラスがこのメタクラスによって作成され、メタクラスにすべての属性を大文字に変更するように伝えるだけで万事順調です.
    幸いなことにmetaclass__実際には任意に呼び出され、正式なクラスである必要はありません.そこで、ここではまず簡単な関数を例に始めましょう.
    python 2で
    #-*- coding:utf-8 -*-
    def upper_attr(future_class_name, future_class_parents, future_class_attr):
    
        #      ,   __           
        newAttr = {}
        for name,value in future_class_attr.items():
            if not name.startswith("__"):
                newAttr[name.upper()] = value
    
        #  type      
        return type(future_class_name, future_class_parents, newAttr)
    
    class Foo(object):
        __metaclass__ = upper_attr #  Foo     upper_attr
        bar = 'bip'
    
    print(hasattr(Foo, 'bar'))
    print(hasattr(Foo, 'BAR'))
    
    f = Foo()
    print(f.BAR)

    python 3で
    #-*- coding:utf-8 -*-
    def upper_attr(future_class_name, future_class_parents, future_class_attr):
    
        #      ,   __           
        newAttr = {}
        for name,value in future_class_attr.items():
            if not name.startswith("__"):
                newAttr[name.upper()] = value
    
        #  type      
        return type(future_class_name, future_class_parents, newAttr)
    
    class Foo(object, metaclass=upper_attr):
        bar = 'bip'
    
    print(hasattr(Foo, 'bar'))
    print(hasattr(Foo, 'BAR'))
    
    f = Foo()
    print(f.BAR)

    もう一度やりましょう.今度は本当のclassをメタクラスとして使います.
    #coding=utf-8
    
    class UpperAttrMetaClass(type):
        # __new__   __init__          
        # __new__              
        #  __init__                
        #      __new__,              
        #   ,       ,          ,        __new__
        #        ,     __init__     
        #                __call__    ,        
        def __new__(cls, future_class_name, future_class_parents, future_class_attr):
            #      ,   __           
            newAttr = {}
            for name,value in future_class_attr.items():
                if not name.startswith("__"):
                    newAttr[name.upper()] = value
    
            #   1:  'type'        
            # return type(future_class_name, future_class_parents, newAttr)
    
            #   2:  type.__new__  
            #       OOP  ,     
            # return type.__new__(cls, future_class_name, future_class_parents, newAttr)
    
            #   3:  super  
            return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr)
    
    #python2   
    class Foo(object):
        __metaclass__ = UpperAttrMetaClass
        bar = 'bip'
    
    # python3   
    # class Foo(object, metaclass = UpperAttrMetaClass):
    #     bar = 'bip'
    
    print(hasattr(Foo, 'bar'))
    #   : False
    print(hasattr(Foo, 'BAR'))
    #   :True
    
    f = Foo()
    print(f.BAR)
    #   :'bip'

    このようにして、それ以外に、元類について本当に言うことはありません.しかし、メタクラス自体は簡単です.
  • ブロッククラスの作成
  • 修正クラス
  • は、変更後のクラス
  • を返す.
  • いったいなぜメタクラスを使用するのですか?

  • 今、私たちの大きなテーマに戻って、いったいどうしてあなたはこのような間違いやすい難解な特性を使うのですか?一般的には使えません
    「元は深い魔法です.99%のユーザーは心配する必要はありません.元を使う必要があるかどうかを明らかにしたい場合は、それを必要としません.実際に元を使う人は、何をする必要があるのかよく知っています.なぜ元を使うのか説明する必要はありません」.Python界のリーダーTim Peters