元类介绍 #
type可以直接生成类(class),但也可以先生成元类(metaclass),再使用元类批量定制类(class)
使用 class 创建类
class Hello():
def say(self, name='world'):
print('Hello, %s' % name)
h = Hello()
h.say()
使用 type 直接创建
def say(self, name='world'):
print("Hello, %s" % name)
Hello = type('Hello', (object, ), dict(say_hello=say))
h = Hello()
h.say()
先生成元类 metaclass,再批量创建
# 传入type
class SayMetaClass(type):
# 传入 类名称、父类、属性
def __new__(cls, name, bases, attrs):
# 添加属性
attrs['say_'+name] = lambda self,value,saying=name: print(saying+','+value+'!')
# 传承三大:类名称、父类、属性
return type.__new__(cls, name, bases, attrs)
# 创建类
class Hello(object, metaclass=SayMetaClass):
pass
# 创建实列
hello = Hello()
# 调用实例方法
hello.say_Hello('world!') # Hello, world! 类名 + 传入的参数
class NiHao(object, metaclass=SayMetacalss):
pass
n = NiHao()
n.say_NiHao("中国") # NiHao, 中国!
元类的应用 #
参考文章 快速掌握元类
ORM 对象关系映射 #
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)
class StringField(Field):
def __init__(self, name):
super().__init__(name, 'varchar(100)')
class IntegerField(Field):
def __init__(self, name):
super().__init__(name, 'bigint')
class ModelMetaClass(type):
"""元类"""
"""
创建一个新的字典mapping
将每一个类的属性,通过.items()遍历其键值对。
如果值是Field类,则打印键值,并将这一对键值绑定到mapping字典上。
将刚刚传入值为Field类的属性删除。
创建一个专门的__mappings__属性,保存字典mapping。
创建一个专门的__table__属性,保存传入的类的名称。
"""
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)
class Model(dict, metaclass=ModelMetaClass):
"""创建 Modle 来自于元类"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError("'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
def save(self):
"""模拟 sql 语句"""
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(str(i) for i in args))
print("SQL: %s" % sql)
print("Args: %s" % str(args))
class User(Model):
"""创建子类"""
"""
id= IntegerField(‘id’)就会自动解析为:
Model.__setattr__(self, ‘id’, IntegerField(‘id’))
因为IntergerField(‘id’)是Field的子类的实例,自动触发元类的__new__,
所以将IntergerField(‘id’)存入__mappings__并删除这个键值对
"""
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
"""
实例化过程中
先调用Model.__setattr__,将键值载入私有对象
然后调用元类的“天赋”,ModelMetaclass._new_,将Model中的私有对象,只要是Field的实例,都自动存入u.__mappings__
"""
# 实例化 User
u = User(id=12345, name='Batman', email='12343@qq.com', password='qwerqwer')
u.save()