目次
列挙型クラス列挙型
定数を定義する必要がある場合、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つのパラメーターを順番に渡します。
- クラスの名前。
- 継承された親クラスのコレクション。Pythonは多重継承をサポートしていることに注意してください。親クラスが1つしかない場合は、タプルの単一要素の記述を忘れないでください。
- クラスのメソッド名は関数にバインドされています。ここでは、関数
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__()
メソッドが受け取るパラメーターは次のとおりです。
-
現在作成されるクラスのオブジェクト。
-
クラスの名前。
-
クラスによって継承された親クラスのコレクション。
-
クラスのメソッドのコレクション。
メソッド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
など、StringField
、IntegerField
など.:
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
、合計でいくつかのことが行われました。
-
Model
クラスへの変更を除外します。 -
現在の
User
クラスで定義されたクラスのすべての属性を検索します(たとえば)。Field属性が見つかった場合、それは__mappings__
dictに保存され、Field属性は同時にクラス属性から削除されます。それ以外の場合。ランタイムエラーが発生するのは簡単です(例の属性は、クラスの同じ名前の属性をカバーします)。 -
テーブル名を
__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]