参悟python元类(又称metaclass)系列实战

现在我们自定义一个dict的子类Dict, 使其可以Dict.key的形式获取value

class Dict(dict):
‘’‘dict子类, 扩展了value的访问方式; 还支持传入两个长度相等的tuple, 组成key-value’’’
def init(self, names=(), values=(), **kw):
‘’’
@names: tuple形式的key集合
@values: tuple形式的value集合
‘’’
super().init(**kw)
for k, v in zip(names, values):
self[k] = v

def __getattr__(self, item):
    '''
    当试图访问实例不存在的属性时, 会自动调用该方法; 访问方式就是'点'
    '''
    try:
        return self[item]
    except KeyError:
        raise AttributeError(r"'Dict' object has no attribute '%s'" % item)

def __setattr__(self, key, value):
    '''
    当试图给不存在的属性赋值时, 会自动调用该方法
    '''
    self[key] = value

再定义一个方法, 可以把dict类型转为Dict类型

def toDict(d: dict):
D = Dict()
for k, v in d.items():
D[k] = toDict(v) if isinstance(v, dict) else v
return D
热身完毕, 但toDict有一种情况无法转为.的形式访问

d = {
‘k1’: [{‘kk1’:‘vv1’}]
}

无法以 d.k1[0].kk1访问vv1

能复用则复用
考虑到数据库里肯定不止一张表, 所以我们需要抽象出一个类, 用来概括所有表的特征, 粗略设计如下

可以继承Dict类, 使其具有key value的特征

提供一个可以根据key获取value的方法

再提供一个跟1类似, 但如果获取不到还可以返回其默认值的方法

初版实现如下

class Model(Dict):
def init(self, **kw):
super().init(**kw) # 调用父类Dict的方法

def getValue(self, key):
    return getattr(self, key, None)

def getValueOrDefault(self, key):
    value = getattr(self, key, None)
    if value is None:
        # TODO: 设置成default
        pass
    return value

因为每张表的字段名和类型都不一样, 而Model又得能概括所有表的字段, 因此就要求能对Model类动态创建, 自然就想到元类可以帮我们实现

class ModelMetaClass(type):
def new(cls, name, bases, attrs):
if name == ‘Model’:
# 当出现与’Model’同名的类时, 直接创建这类
return type.new(cls, name, bases, attrs)

    # 定义表名: 要么在类中定义__table__属性, 否则与类名相同
    tableName = attrs.get('__table__', None) or name
    print(f'建立映射关系: {name}类 --> {tableName}表')

    mappings = Dict()   # 存储column与Field 子类的对应关系, Field在上一章中定义的, 忘了回去翻
    fields = []         # 用来存储除主键以外的所有字段名
    primaryKey = None   # 用来记录主键字段的名字, 初始没有

    for k, v in attrs.items():
        # 遍历所有属性, 即映射表的字段, 读不懂请回看第二章 Users 的定义
        if isinstance(v, Field):     # Field类, 所有字段类型的父类
            print(f'建立映射... column: {k} ==> class: {v}')
            mappings[k] = v

            if v.primaryKey:         # 判断字段是否被设置成了主键
                if primaryKey:       # 因为一张表只能有一个主键
                    raise Exception(f'Duplicate primary key for field {k}')
                primaryKey = k
            else:
                fields.append(k)

    if not primaryKey:               # 这里做了一步强制要求设置主键, 你也可以去掉
        raise Exception(f'请给表{tableName}设置主键')

    for k in mappings.keys():
        # 删除原属性, 避免实例的属性遮盖类的同名属性, 因为我们已经保存到 mappings 中了
        attrs.pop(k)

    # 接下来给本元类(ModelMetaClass)创建的class(如 Model)设置私有属性
    attrs['__mappings__'] = mappings
    attrs['__table__'] = tableName
    attrs['__primaryKey__'] = primaryKey
    attrs['__fields__'] = fields

    return type.__new__(cls, name, bases, attrs)

第二版Model, 即完成TODO

class Model(Dict, metaclass=ModelMetaClass):
“”“指定metaclass, 以实现动态定制”""
def init(self, **kw):
super().init(**kw)

def getValue(self, key):
    return getattr(self, key, None)

def getValueOrDefault(self, key):
    value = getattr(self, key, None)
    if value is None:
        field = self.__mappings__[key]   # 从所有column中获取value
        if field.default is not None:
            # 如果default指向是方法(如time.time), 则调用方法获取其值; 否则直接赋值
            value = field.default() if callable(field.default) else field.default
            print(f'using defalut value for {key}: {value}')
            setattr(self, key, value)    # 其实是调 Dict.__setattr__, 以支持用"."访问
    return value

重新设计上一章的 Users 类
“”“映射到表 Users; 同理定义其他映射关系 “””
class Users(Model):
“”"
继承自Model, 这样Users就有了Dict特性, 同时在实例化Users时, 又会以ModelMetaClass定制的特性创建
“”"
uid = IntegerField(primaryKey=True, ddl=‘int(11)’)
email = StrField(ddl=‘varchar(50)’)
passwd = StrField(ddl=‘char(32)’)
admin = IntegerField(default=0, ddl=‘tinyint(1)’)
name = StrField(ddl=‘varchar(50)’)
birthday = DateTimeField(ddl=‘DATE’)
image = StrField(default=‘about:blank’, ddl=‘varchar(500)’)
created_at = DateTimeField(default=‘0000-00-00 00:00:00’, ddl=‘timestamp’)
updated_at = DateTimeField(ddl=‘timestamp’)
created_by = IntegerField(ddl=‘int(11)’)
updated_by = IntegerField(ddl=‘int(11)’)
is_deleted = IntegerField(default=0, ddl=‘tinyint(1)’)
参考文本
学习视频资料:http://www.makeru.com.cn/live/1392_1164.html?s=143793

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值