博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python中的metaclass
阅读量:6716 次
发布时间:2019-06-25

本文共 5123 字,大约阅读时间需要 17 分钟。

首先看下面的代码:

# coding: utf-8class Test(object):    passprint Test.__class__  # typeprint Test.__base__   # objectt = Test()print t.__class__     # Testprint t.__class__.__class__  # typeprint Test.__class__.__class__.__class__  # typea = type('Foo', (), {})print a  # class Foot = a()print type(t)  # Fooprint t.__class__  # Fooprint t.__class__.__class__  # type

从代码中可以看出:

  1. t的__class__,为Test,这是它所属的类
  2. Test.__class__ 为type,也就是说,Test这个类也是个对象,他是type这个类的对象。

在Python中,一切都是对象,所以类本身也是对象,类对象所属的类是type类。

那么,Python中的类都是type类的对象,而type类实现了__call__调用,实现__call__的类的对象都可以当做函数一样调用,所以所有的type对象都是函数,也就是说,所有的类都是函数。

以前一直以为type是个函数,但实际上,type本身是个类。

所以,Python中的类型体系可以看成:

type -> Test -> t

而Python中对象的类型可以这样追溯:

t -> Test -> type -> type -> ...

metaclass

我们使用类来创建对象,而Python中可以通过元类来动态创建类。

metaclass可以是一切可以执行的东西,也就是说,可以是函数,也可以是类。

# coding: utf-8def upper_attrs(class_name, class_parents, class_attrs):    attrs = {}    for key in class_attrs:        if not key.startswith('__'):            attrs[key.upper()] = class_attrs[key]    return type(class_name, class_parents, attrs)class Foo:    __metaclass__ = upper_attrs    bar = 'haha'if __name__ == '__main__':    print hasattr(Foo, 'bar')    print hasattr(Foo, 'BAR')    print type(Foo)

这里我们将upper_attrs当做一个元类,它的作用是将类的属性改为大写。所以Foo中的bar属性改为BAR属性。

metaclass的作用主要是:

  1. 拦截类的创建
  2. 修改类
  3. 返回被修改的类

使用类实现metaclass

# coding: utf-8class UpperAttrsMeta(object):    def __new__(metaclass, class_name, class_parents, class_attrs):        print metaclass        attrs = {}        for key in class_attrs:            if not key.startswith('__'):                attrs[key.upper()] = class_attrs[key]        return type(class_name, class_parents, attrs)class Test:    __metaclass__ = UpperAttrsMeta    foo = 'bar'if __name__ == '__main__':    print hasattr(Test, 'foo')    print hasattr(Test, 'FOO')    t = Test    print t.FOO    print type(t)    print type(Test)    print type(UpperAttrsMeta)

使用metaclass实现一个简易的ORM

假设我们想实现一个ORM,这样去使用:

class User(Model):    id = IntegerField('id')    name = StringField('username')    email = StringField('email')    password = StringField('password')u = User(id=123455, name='haha', email='haha@301.com', password='hhhhh')u.save()

我们先实现各种Field:

# coding: utf-8class Field(object):    def __init__(self, name, column_type):        self.name = name  # 列名        self.column_type = column_type  # 列的类型    def __str__(self):        # 实质是打印table名称和列名        return '<%s:%s>' % (self.__class__.__name__, self.name)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')

Field的主要作用是记录列名和类型之间的关系。就好比 name 和 varchar

上面实现了Field,代表数据类型。下面我们需要实现数据库的table,每种类型我们称为一种model。

下面我们使用metaclass实现model。

class ModelMetaClass(type):    def __new__(cls, name, bases, attrs):        if name == 'Model':            return type.__new__(cls, name, bases, attrs)        mappings = dict()        for k, v in attrs.iteritems():            if isinstance(v, Field):                # 记录name -> StringField                print 'Found mapping: %s->%s' % (k, v)                mappings[k] = v        for k in mappings.iterkeys():            # 防止重复 a.name            attrs.pop(k)        attrs['__table__'] = name  # 表名称        attrs['__mappings__'] = mappings  # 属性和列名的映射关系        return type.__new__(cls, name, bases, attrs)

上面提过,metaclass的主要作用是拦截类的生成,并进行修改。

这里的ModelMetaClass,将类的属性中定义的 列名和数据类型单独摘取出来,并将它们保存在__mappings__中。

另外,提取出列名和类型后,需要将这些属性去掉。

以User为例,提取完id,name,email和password后,需要将它们删除,因为这四个属性为类属性,根据User定义的对象u也具有这四个属性,此时u.name为StringField对象,这样显然不合理,所以需要将这些属性去掉。

这些属性的唯一价值也是提供给metaclass使用。

下面实现Model:

class Model(dict):    __metaclass__ = ModelMetaClass    def __init__(self, **kwargs):        super(Model, self).__init__(**kwargs)    def __getattr__(self, key):        try:            return self[key]        except KeyError:            raise AttributeError("'%s' object has not Attribute: %s" % (self.__class__.__name__, key))    def __setattr__(self, key, value):        self[key] = value    def save(self):        fields = []        params = []        args = []        # 注意k 和v.name可能不同 数据库中以v为主        for k, v in self.__mappings__.iteritems():            fields.append(v.name)  # 列名            params.append('?')            args.append(getattr(self, k, None))        _fields = ','.join(fields)        _params = ','.join(params)        sql = 'insert into %s (%s) value (%s)' % (            self.__table__, _fields, _params)        print 'SQL: %s' % sql        print str(args)

save函数的过程就是根据__mappings__中记录的列名,从dict中提取出值。将其保存在args中。fields中保存的是列名。

运行结果如下:

Found mapping: email->
Found mapping: password->
Found mapping: id->
Found mapping: name->
SQL: insert into User (password,email,username,id) value (?,?,?,?)['hhhhh', 'haha@301.com', 'haha', 123455]

参考资料:

http://blog.jobbole.com/21351/

http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386820064557c69858840b4c48d2b8411bc2ea9099ba000

转载于:https://www.cnblogs.com/inevermore/p/4620048.html

你可能感兴趣的文章
Linux网络——GW总结
查看>>
2017-08-06 前端日报
查看>>
canvas基础知识
查看>>
Go语言暴力入门1
查看>>
Java线程汇总
查看>>
javascript实现浏览器Ctrl+F页面搜索功能
查看>>
自己简单实现Spring的IOC原理
查看>>
解析Json更快的Gson的APT版本开源库
查看>>
慎用!BLEU评价NLP文本输出质量存在严重问题
查看>>
WebAssembly Studio:Mozilla提供的WASM工具
查看>>
专访何红辉:谈谈Android源码中的设计模式
查看>>
不用鼠标/键盘/显示器给树莓派安装系统
查看>>
InfoQ宣布成立CNUT容器技术俱乐部 欲连接中国容器社区
查看>>
你知道为什么Facebook的API以一个循环作为开头吗?
查看>>
Product Mastery 作者访谈
查看>>
极限编程创始人Ron Jeffries建议开发者放弃敏捷
查看>>
SSPL的MongoDB再被抛弃,GUN Health也合流PostgreSQL
查看>>
SegmentFault 2016 第四季度 Top Writer
查看>>
Go 领军人物谢孟军:智能制造渴望银弹,首先要摒弃偏见
查看>>
金丝雀测试实践
查看>>