【注意】高度なパイソンメタクラスメタクラスは、ORMを実装します

【注意】高度なパイソンメタクラスメタクラスは、ORMを実装します

ディレクトリ

1. 元类

1.1。クラスもオブジェクトであります

1.2。動的なクラスの作成

1.3。使用タイプは、クラスを作成します

1.4複雑なクラスを作成するタイプの使用

1.4.1。タイプが継承してクラスを作成します

1.4.2。方法の例が追加されます

1.4.3。静的メソッドを追加します

1.4.4。クラスのメソッドを追加します

1.4.5。型クラスのより完全な使用を作成します

1.5メタクラス定義

1.6。__Metaclass__プロパティ

1.7。メタクラス

。1.8アップデート:__call__メソッドメタクラス

注:新しいメタクラスと、init、コール

1.9。メタクラスの利用シナリオ 

2. ORMを実装するメタクラス(オブジェクトリレーショナルマッピング)

何2.1。ORMです

メタクラスによる関数の挿入の2.2。ORMの簡単な実装


 

1. 元类

元のクラスは「もの」のクラスを作成するために使用され、元のクラスは、クラスを作成し、クラスは、オブジェクトのインスタンスを作成します

1.1。クラスもオブジェクトであります

ほとんどのプログラミング言語では、クラスは、オブジェクトのコードセグメントのセットを生成する方法を説明するために使用されますこれは、Pythonで真であります:

クラスによって以下ipython3のオブジェクト出力を生成します。

>>>class ObjectCreator(object):
…       pass
…
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

 

しかし、Pythonのミドルクラスはそれよりもはるかに多いです。クラスもオブジェクトの一種ですただ、キーワードクラスを使用して、Pythonインタプリタは自動的に実行時にオブジェクトを作成します。

次のコードスニペット:

class ObjectCreator(object):
    pass

Pythonは自動的にメモリ内のオブジェクトを作成しますが、名前はObjectCreatorです。

このオブジェクト(クラスオブジェクトObjectCreator)はオブジェクト(オブジェクトのインスタンス)されているを作成する機能を有しています。しかし、それは基本的にまだオブジェクトであることは、オブジェクトの操作を行うことができます:

  • 変数に代入
  • それをコピーします
  • それは財産が増加したよう
  • 関数のパラメータとして渡されました

 

注意:

  • グローバル()  関数は、すべてのグローバルオブジェクトへの参照を含む辞書タイプ内のすべてのグローバル変数の現在位置を返します。それはまた、変数の独自の定義を含む、辞書のpython-定義された変数が含まれています返され、それが直接使用することができます。
  • 内部でどのようなクラスやモジュールを取得するには、使用することができ  、クラス.__ dict__に  または  モジュール.__ dict__にします
  • グローバル()(__builtins __.__ dict__に見える)他の従来の印刷オブジェクトを含む、内蔵モジュール__builtins__返します。
  • とき)(グローバルに最初に直接、変数や関数名を使用して内部を見つけるために辞書を返し、その後、内蔵のモジュール辞書が行く__builtins__見つけることができません。

 

 

1.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())  # 你可以通过这个类创建类实例,也就是对象

# 输出:
# <class '__main__.choose_class.<locals>.Foo'>
# <__main__.choose_class.<locals>.Foo object at 0x7fa8b5ac2f98>

伝送パラメータは、返される参照は、同じクラスではなく、そのオブジェクトのインスタンスの作成につながった同じではない、同じではありません。しかし、これは十分に動的ではありません、まだ自分自身のクラス全体の書き込みコードに必要があるため、。あなたはできるダイナミック型クラスを作成するために使用します:一種類は、オブジェクトの関数戻り型とすることができる、そのような印刷として(タイプ(1))<タイプ「INT」を>印刷されます)
 

1.3。使用タイプは、クラスを作成します

機能の完全に異なる種類、すなわち動的なクラスを作成することもあります関数型は、実際のメタクラスです。

クラス型引数として許容される記述、およびクラスオブジェクトを返します

タイプ()名前と値(辞書属性を含む成るタプルによって、クラス名、親名(相続の場合の、空であることができます)、)

 

以下は、クラスとクラスのTest1 Test2をを作成するには、2つの方法があります。

In [13]: class Test:
   ....:     num = 100
   ....:     num2 = 200
   ....:     

In [14]: Test2 = type("Test2", (), {"num":100, "num2":200})

これら二つのカテゴリをテストするためにヘルプを使用します。

In [16]: 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)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  num = 100
 |  
 |  num2 = 200


In [18]: 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)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  num = 100
 |  
 |  num2 = 200

もちろん、二人は同じです。

 

1.4複雑なクラスを作成するタイプの使用

 

1.4.1。タイプが継承してクラスを作成します

Test11テストを継承し、Test22はTest2をを継承しています。

In [1]: class Test:
    num = 100
    num2 = 200
   ...:     

In [2]: Test2 = type("Test2", (), {"num":100, "num2":200})

In [3]: class Test11(Test):
   ...:     pass
   ...: 

In [4]: Test22 = type("Test22", (Test2,), {})

そして、ヘルプビューを使用して、2つのクラスが同じです。

 

1.4.2。方法の例が追加されます

In [15]: class Foo(object):
   ....:     bar = True
   ....:     

In [16]: def echo_bar(self):   # 定义了一个普通的函数
   ....:     print(self.bar)
   ....:    
 
In [17]: FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})  # 让FooChild类中的echo_bar属性,指向了上面定义的函数

In [18]: hasattr(Foo, 'echo_bar')  # 判断Foo类中 是否有echo_bar这个属性
Out[18]: False

In [19]: hasattr(FooChild, 'echo_bar')  # 判断FooChild类中 是否有echo_bar这个属性
Out[19]: True

In [20]: my_foo = FooChild()

In [21]: my_foo.echo_bar()
True

1.4.3。静的メソッドを追加します

上記のコードに基づいて

In [36]: @staticmethod
    ...: def test_static():
    ...:     print("static method ....")
    ...:

In [37]: Foochild = type('Foochild', (Foo,), {"echo_bar": echo_bar, "test_static": test_static})

In [38]: fooclid = Foochild()

In [39]: fooclid.test_static
Out[39]: <function __main__.test_static>

In [40]: fooclid.test_static()
static method ....

In [41]: fooclid.echo_bar()
True

 

1.4.4。クラスのメソッドを追加します

上記のコードに基づいて

In [42]: @classmethod
    ...: def test_class(cls):
    ...:     print(cls.bar)
    ...:

In [43]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar, "test_static": test_static, "test_class": test_class})

In [44]: fooclid = Foochild()

In [45]: fooclid.test_class()
True

 

1.4.5。型クラスのより完全な使用を作成します

class A(object):
    num = 100

def print_b(self):
    print(self.num)

@staticmethod
def print_static():
    print("----haha-----")

@classmethod
def print_class(cls):
    print(cls.num)

# B继承于A,
B = type("B", (A,), {"print_b": print_b, "print_static": print_static, "print_class": print_class})
b = B()
b.print_b()
b.print_static()
b.print_class()
# 结果
# 100
# ----haha-----
# 100

 

1.5メタクラス定義

 

  • 元のクラスは、「もの」のクラスを作成するために使用されますオブジェクトクラスのインスタンスを作成するためのクラスを作成します。
  • しかし、Pythonでのクラスは、オブジェクトがメタクラスは、これらのクラス(オブジェクト)を作成するために使用される、メタクラスは、クラスのクラスです
  • メタクラスは、クラスを作成し、クラスは、オブジェクトのインスタンスを作成します
MyClass = MetaClass() # 使用元类创建出一个对象,这个对象称为“类”
my_object = MyClass() # 使用“类”来创建出实例对象

使用以前の関数の型は、実際のメタクラスです。Pythonのタイプの後ろメタクラスのすべての種類を作成するために使用されます

  • タイプは、クラスオブジェクトクラスを作成することですあなたは__class__プロパティをチェックすることでこれを確認することができます。

:この例では(すなわち、クラスはインスタンスを作成する)に対応するクラスのインスタンス呼び出し__class__属性を指すようになります、あなたはまた、他のクラス属性を呼び出して行くことができます)

  • Pythonですべてが、オブジェクトですこれは、整数、文字列関数、およびクラスが含まれています。すべての彼らはオブジェクトであり、それらはタイプであるクラスを作成することから来ています。

 

In [24]: class T(object):
   ....:     pass
   ....: 

In [25]: t = T()

In [26]: t.__class__
Out[26]: __main__.T

# 以上可见t由当前模块的T创建


In [27]: t.__class__.__class__
Out[27]: type

# 以上可见T由type创建 (注:type还是type创建的)

 

1.6。__Metaclass__プロパティ

クラスを定義するときは、そのプロパティの__metaclass__を追加することができます。__Metaclass__定義は、このクラスのメタクラスを定義します

class Foo(object):  # python2
    __metaclass__ = something…
    ...省略...

class Foo(metaclass=something):  # python3
    __metaclass__ = something…

このように、PythonはクラスFooのを作成するために、メタクラスを使用します文の実行:クラスFooの(オブジェクト)、Fooクラスがメモリ内に作成されていません。見つかった場合はPythonは、クラスの定義で__metaclass__属性を見ていきます、Pythonはそれがビルトインタイプこのクラスを作成するために使用する、見つからない場合は、Fooクラスを作成するために使用します。

たとえば、次のコード:

class Foo(Bar):
    pass

Pythonは次のアクションを行いました。

  1. fooが__metaclass__このプロパティにそれを持っていますか?もしそうなら、Pythonは__metaclass__はFooクラス(オブジェクト)の名前を作成して渡します
  2. Pythonは__metaclass__見つからない場合は、バー(親)に__metaclass__プロパティを見て、そして上記と同じ手順を実行しようとしていきます。
  3. Pythonで任意の親クラスが__metaclass__を見つけることができない場合、それは__metaclass__を見つけるために、モジュールの階層構造であること、そして同じことをしようとします。
  4. それでも__metaclassを見つけることができない場合は__、Pythonはクラスオブジェクトを作成するために、組み込みの型を使用します。

Q:中__metaclass__に配置されたどのようなコード?

A:あなたは物事のクラスを作成することができますそれでは、それのクラスを作成するために使用することができますか?入力するか、いずれかのタイプを使用するか、サブクラスは、ものを入力することができます。

 

1.7。メタクラス

メタクラスの主な目的は、クラスが自動的に変更することができたときにクラスを作成することです

クラスを作成すると、メタクラスのメタクラスはクラスを作成するために、メタクラスデフォルトのコールタイプを書かれていません誰マークするために使用しました。

メタクラスものはまとめて行わ

  1. 作成傍受クラス
  2. クラスを変更
  3. 変更後のクラスに戻ります

 

DEMO1:

あなたが大文字である必要があり、あなたのモジュールのプロパティのすべてのクラスを決定したとします。一つの方法は、モジュールレベルで__metaclass__を設定することです。このように、このモジュール内のすべてのクラスが、このメタクラスによって作成され、私たちはすべてのプロパティは、罰金になります大文字のすべてになっていた元のクラスを指示する必要があります。

幸い、__ metaclass__が実際に任意に呼び出され、それが正式なクラスである必要はありません。

下記の例として、次のようにしても同じですメタクラスをカスタマイズ

#-*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

    #遍历属性字典,把不是__开头的属性名字变为大写
    new_attr = {}
    for name,value in class_attr.items():
        if not name.startswith("__"):  # 不是下划线开头时
            new_attr[name.upper()] = value

    #调用type来创建一个类
    return type(class_name, class_parents, new_attr)

class Foo(object, metaclass=upper_attr):
    bar = 'bip'

print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)

上記、クラス名のFooがCLASS_NAMEを通過し、親オブジェクトがclass_parentsを通過し、辞書道への内部クラスの内容はclass_attrを通過しました。タイプとリターンに続い大文字とする辞書のキー上を通過します。

 

upper_attr上記のパスはなく、かなりメタクラスの名前に合わせて、機能ではなく、クラスです。メタクラスとして実際のクラスを使用するには、ここDEMO2。

DEMO2:

#coding=utf-8

class UpperAttrMetaClass(type):  # 继承了type类的子类,相当于是个元类
    # __new__ 是在__init__之前被调用的特殊方法
    # __new__是用来创建对象并返回之的方法
    # 而__init__只是用来将传入的参数初始化给对象
    # 你很少用到__new__,除非你希望能够控制对象的创建
    # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
    # 如果你希望的话,你也可以在__init__中做些事情
    # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用

    def __new__(cls, class_name, class_parents, class_attr):
        # 遍历属性字典,把不是__开头的属性名字变为大写
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        # 方法1:通过'type'来做类对象的创建
        return type(class_name, class_parents, new_attr)

        # 方法2:复用type.__new__方法
        # 这就是基本的OOP编程,没什么魔法
        # return type.__new__(cls, class_name, class_parents, new_attr)

# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
    bar = 'bip'

# python2的用法
# class Foo(object):
#     __metaclass__ = UpperAttrMetaClass
#     bar = 'bip'


print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True

f = Foo()
print(f.BAR)
# 输出:'bip'

質問:かどうかに関係なく、書き込み対象の、カスタムクラスを実装する場合、オブジェクトを継承し、どのように達成するために?

 

。1.8アップデート:__call__メソッドメタクラス

 

注:新しいメタクラスと、init、コール

メタクラスで:

あなたは、一般的なリライト__new__それをカスタマイズしたい場合は、オブジェクトは、クラスを作成し;
;必要であれば、あなたもで__init__で何かを行うことができ
、特別な方法は__call__書き換えを伴う高度な使い方の一部。

 

 

1.9。メタクラスの利用シナリオ 

  • 機能を定義した場合の機能を追加しながら、あなたは、あなたがデコレータ使用できる機能に必要です。
  • クラスを定義した場合、あなたはメタクラスを使用することができ、クラスの機能を変更する必要があります。

Pythonコミュニティのリーダーティム・ピーターズは、元のクラスは、魔法の深さである」、と述べ、ユーザーの99%は、単に心配する必要はありません必要があります。あなたは元のクラスを使用する必要が正確かどうかを知りたい場合は、あなたがそれを必要としない。実用的なものメタクラス使用の人々は、彼らが何をすべきかを非常によく知っている、そしてなぜ使用メタクラスを説明する必要はありません。」 

 

 

2. ORMを実装するメタクラス(オブジェクトリレーショナルマッピング)

 

何2.1。ORMです

ORM(オブジェクトリレーショナルマッピング)、すなわち、オブジェクト - リレーショナル - マッピングは、バックエンドのプログラミング言語PythonのWebフレームワークジャンゴの核となるアイデアであり、その運用データベースにSQL文を使用する必要はないかもしれません。

文がことが理解される:オブジェクトのインスタンスを作成し、この操作のオブジェクトインスタンスは、MySQLステートメントに対応することができ、テーブル名、その対応するデータ・クラス属性テーブルによって作成されたフィールドなどのデータにより作成されたクラス名。

 

説明:

  1. いわゆるORMは、操作のようなオブジェクトを通して、開発者はデータベースを操作するための時間をできるようにすることですxxxx.属性=yyyy 、それがORMの心を開発しているような単純な
  2. ただ、ORMは、より複雑で実現し、Djangoは非常に複雑な操作で達成されています。主に同様の挿入ORMの完了による知識のこのセクションで、それに真実を理解します

 

class User(父类省略):
    uid = ('uid', "int unsigned")
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")
    ...省略...


u = User(uid=12345, name='Michael', email='[email protected]', password='my-pwd')
u.save()
# 对应如下sql语句
# insert into User (username,email,password,uid)
# values ('Michael','[email protected]','my-pwd',12345)

上記のデモ思考、クラスの定義、クラス名テーブル名、各フィールドに対応するクラスに関連する属性情報に対応していること。

オブジェクトのインスタンスを作成し、オブジェクトのインスタンスを作成するために、保存の方法により、テーブルの上で動作します。オブジェクトによる操作の例としては、自動的にSQL文を記述することなくSQL文操作に変換します

 

メタクラスによる関数の挿入の2.2。ORMの簡単な実装

 

デモ

  • (タイプから継承)メタクラスModelMetaclassを定義します。
  • クラスのユーザー定義、4クラス属性、方法及び方法のセーブ__ init__があります。ユーザーメタクラスクラスは、デフォルトのタイプを持つクラスを作成するつもりはなく、指定されたModelMetaclassの使用されていない、指定されたModelMetaclassを使用しています。
  • __new__方法のModelMetaclass呼び出し(クラス名のユーザー名を渡す、空のタプルは、クラス属性辞書のパスattrsにとして、拠点を通過した)、そして最後にタイプ.__ new__を返します。
  • uがユーザーを=(UID = 12345、名= 'マイケル、email='[email protected] 'パスワード=' 私の-PWD「)に行われ、ユーザーの__init__メソッドが呼び出されます。
  • 最後の呼び出しは()メソッドを保存するには:オブジェクトの自己監督のインスタンスを...
class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判断是否需要保存
        for key, values in attrs.items():  # 注:字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组
            # 判断是否是指定的StringField或者IntegerField的实例对象
            if isinstance(values, tuple):  # 判断values是否是元组
                print('Found mapping: %s ==> %s' % (key, values))
                mappings[key] = values  # 循环结束时传进来的类属性保存到了字典mappings

        # 删除这些已经在字典中存储的属性
        for key in mappings.keys():
            attrs.pop(key)

        # 将之前的uid/name/email/password以及对应的对象引用、类名字
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


class User(metaclass=ModelMetaclass):
    uid = ('uid', "int unsigned")  # !!注意:在Django中等号右边是对象,而非元祖
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")
    
    # 当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储
    # 以上User类中有(即最终User类的类属性对应为下面的样子)
    # __mappings__ = {
    #     "uid": ('uid', "int unsigned")
    #     "name": ('username', "varchar(30)")
    #     "email": ('email', "varchar(30)")
    #     "password": ('password', "varchar(30)")
    # }
    # __table__ = "User"
    
    def __init__(self, **keywargs):
        for name, value in keywargs.items():
            setattr(self, name, value)  # setattr 往self指向的对象添加一个属性。

    def save(self):
        fields = []
        args = []
        for key, values in self.__mappings__.items(): # 注:实例对象没有__mappings__,但是类对象里面有
            fields.append(values[0])  # 取元组values第一个值添加(如:第一次循环取元组('uid', "int unsigned")的uid)
            args.append(getattr(self, key, None)) # getattr取对象的key的值,添加到列表args

        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
        print()
        print('SQL: %s' % sql)


u = User(uid=12345, name='Michael', email='[email protected]', password='my-pwd')
# print(u.__dict__)
u.save()

出力:

Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')

SQL: insert into User (email,username,uid,password) values ([email protected],Michael,12345,my-pwd)

生成されたSQL文に問題があるかに注意してください:...値(テスト@ orm.org、マイケル、12345、私-PWD)は、ブラケットは、引用符でいくつかの値である必要があります。

 

改良版

声明

         sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))

変更

        args_temp = list()
        for temp in args:
            # 判断入如果是数字类型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
        # 例如,经过以上["12345", """'xxxx'""", """'[email protected]'""", """'567'"""]就会变成 12345,'xxxx','[email protected]','567'
       

出力の最後の行は次のようになります。

SQL: insert into User (email,username,password,uid) values ('[email protected]','Michael','my-pwd',12345)

 

拡張:

  • アイテムの辞書(辞典)()関数の戻りには、リスト(キー、値)の配列をタプル横断することができます。
  • SETATTR(オブジェクト、名前、値)関数関数に対応  GETATTR()は、プロパティは常に存在しない場合、プロパティの値を設定するために使用されています。自己指すオブジェクトは、属性を追加します。
    • オブジェクト - オブジェクト。
    • 名前 - Stringオブジェクトのプロパティ。
    • 値 - プロパティの値。
  • GETATTR()  関数は、オブジェクトの属性の値を返します。GETATTR(オブジェクト、名[、デフォルト ])
    • オブジェクト - オブジェクト。
    • 名前 - Stringオブジェクトのプロパティ。
    • デフォルト - デフォルトの戻り値は、パラメータが利用できない場合、トリガーはAttributeErrorには対応する属性が存在しません
  • join()メソッドは、新しい接続を生成するために、文字列中のシーケンスの要素を指定するために使用されます。戻り値指定した文字によって接続された要素のシーケンスの後に生成された新しい文字列。str.join(シーケンス)
    • 指定された文字をstr--
    • 配列 - 接続する要素の配列。

最後の文、操作対象を行うためのORM、動作するデータテーブルの同等

 

 

- - -終わり - - -

公開された50元の記事 ウォン称賛10 ビュー6603

おすすめ

転載: blog.csdn.net/qq_23996069/article/details/104556135