Python-列挙型クラスenum / typeを使用して、クラス/メタクラスメタクラスを動的に作成します

目次

 

列挙型クラス列挙型

type()は動的にクラスを作成します

メタクラス


列挙型クラス列挙型

定数を定義する必要がある場合、1つの方法は、大文字の変数を使用して月などの整数を定義することです。

JAN = 1
FEB = 2
MAR = 3
...
NOV = 11
DEC = 12

利点は単純さですが、欠点は型がint変数であり、変数であるということです。

より良い方法は、そのような列挙型のクラス型を定義することです。そうすれば、各定数はクラスの一意のインスタンスになります。PythonはEnum、この機能を実現するためのクラスを提供しています。

from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12

value属性はintデフォルトで1最初から数えて、メンバー自動的に割り当てられる定数です

列挙型をより正確に制御する必要がある場合は、以下Enumからカスタムクラスを派生させることができます。

from enum import Enum, unique
@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
#调用枚举成员的 3 种方式
>>>print(print(Weekday.Tue))
Weekday.Tue
>>>print(Weekday['Tue'])
Weekday.Tue
>>>print(Weekday(1))
Weekday.Mon

#调取枚举成员中的 value 和 name
>>>print(Weekday.Mon.value)
1
>>>print(Weekday.Mon.name)
Mon

#遍历枚举类中所有成员的2种方式
>>> for name, member in Weekday.__members__.items():
...     print(name, '=>', member)
...
Sun => Weekday.Sun
Mon => Weekday.Mon
Tue => Weekday.Tue
Wed => Weekday.Wed
Thu => Weekday.Thu
Fri => Weekday.Fri
Sat => Weekday.Sat

>>>for weekday in Weekday:
...    print(weekday)
Weekday.Sun
Weekday.Mon
Weekday.Tue
Weekday.Wed
Weekday.Thu
Weekday.Fri
Weekday.Sat

@uniqueデコレータは、重複する値がないことを確認するのに役立ちます。

列挙型クラスのメンバーを比較することはできませんが、==を使用するか、それらが等しいかどうかを比較することはできます

type()は動的にクラスを作成します

type()関数はオブジェクトの型を返すことができ、新しい型を作成することもできます。たとえば、次の定義を渡さなくても、type()関数Hello介してクラス作成できますclass Hello(object)...

>>> def fn(self, name='world'): # 先定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>

 クラスオブジェクトを作成するために、type()関数は3つのパラメーターを順番に渡します。

  1. クラスの名前。
  2. 継承された親クラスのコレクション。Pythonは多重継承をサポートしていることに注意してください。親クラスが1つしかない場合は、タプルの単一要素の記述を忘れないでください
  3. クラスのメソッド名は関数にバインドされています。ここでは、関数fnをメソッド名にバインドしますhello

メタクラス

通常、最初にクラスを定義してから、インスタンスを作成します。ただし、メタクラスに基づいてクラスを作成できるため、最初にメタクラスを定義し、クラスを作成して、最後にインスタンスを作成できます。メタクラスを使用すると、クラスを作成または変更できます。つまり、クラスはメタクラスによって作成された「インスタンス」と考えることができます

たとえば、メタクラスはカスタムMyListにaddメソッドを追加できます

1.定義ListMetaclass。デフォルトの習慣によれば、メタクラスのクラス名は、これがメタクラスであることを明確に示すために、常にメタクラスで終わります。

# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

2.クラスを定義するときに、ListMetaclassを使用してクラスをカスタマイズし、キーワードパラメーターを渡すmetaclassように指示します

class MyList(list, metaclass=ListMetaclass):
    pass

私たちは、キーワードパラメータを渡すとmetaclass、魔法が有効になり、それが作成するために、Pythonインタプリタを指示しMyListて、それをListMetaclass.__new__()、それが作成されたとき。ここでは、例えば、クラスの定義を変更し、新しいメソッドを追加し、その後に戻ることができます定義後に変更します。

__new__()メソッドが受け取るパラメーターは次のとおりです。

  1. 現在作成されるクラスのオブジェクト。

  2. クラスの名前。

  3. クラスによって継承された親クラスのコレクション。

  4. クラスのメソッドのコレクション。

メソッドMyListを呼び出すことができるかどうかをテストしますadd()

>>> L = MyList()
>>> L.add(1)
>>> L
[1]

メタクラスの主な目的はAPIを作成することであり、ORMは典型的な例です。ORMのフルネームは「オブジェクトリレーショナルマッピング」、つまり、リレーショナルデータベースの行をオブジェクトにマッピングするオブジェクトリレーショナルマッピングです。つまり、クラスはテーブルに対応します。このように、 SQLステートメントを直接操作せずにコードを書く方が簡単です。ORMフレームワークを作成するには、テーブルの構造に従って対応するクラスを定義できるのはユーザーのみであるため、すべてのクラスを動的に定義することしかできません。

 

まず、Fieldデータベーステーブルのフィールド名とフィールドタイプの格納を担当するクラスを定義します。

class Field(object):

    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)

上でFieldこれに基づい、さらに様々なタイプの定義Fieldなど、StringFieldIntegerFieldなど.:

class StringField(Field):

    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')

class IntegerField(Field):

    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')

次のステップは、最も複雑なものを書くことModelMetaclassです:

class ModelMetaclass(type):

    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        attrs['__table__'] = name # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)

そして基本クラスModel

class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

ユーザーが1つを定義するclass User(Model)と、Pythonインタープリターは最初に現在のクラスのUser定義を検索しmetaclassます。見つからない場合は、親クラスModelを検索し続けます。metaclass見つかった場合は、でModel定義さmetaclassれたものModelMetaclassを使用してUserクラスを作成します。つまり、メタクラスはある意味でサブクラスに継承されて非表示にすることができますが、サブクラス自体はそれを認識しません。

ではModelMetaclass、合計でいくつかのことが行われました。

  1. Modelクラスへの変更を除外します。

  2. 現在のUserクラス定義されたクラスのすべての属性を検索します(たとえば)。Field属性が見つかった場合、それは__mappings__dictに保存され、Field属性は同時にクラス属性から削除されます。それ以外の場合。ランタイムエラーが発生するのは簡単です(例の属性は、クラスの同じ名前の属性をカバーします)。

  3. テーブル名を__table__保存します。ここではテーブル名に簡略化され、デフォルトでクラス名になります。

Modelクラス、メソッドのような様々なデータベース操作を定義することができsave()delete()find()updateなどが挙げられます。

save()インスタンスをデータベースに保存するメソッドを実装しました。テーブル名、属性からフィールドへのマッピング、および属性値の収集により、INSERTステートメントを作成できます

コードを書いてみてください:

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

出力は次のとおりです。

Found model: User
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found mapping: id ==> <IntegerField:uid>
Found mapping: name ==> <StringField:username>
SQL: insert into User (password,email,username,id) values (?,?,?,?)
ARGS: ['my-pwd', '[email protected]', 'Michael', 12345]

おすすめ

転載: blog.csdn.net/zangba9624/article/details/106326004