python中的metaclass
首先看下面的代码:
# coding: utf-8
class Test(object):
pass
print Test.__class__ # type
print Test.__base__ # object
t = Test()
print t.__class__ # Test
print t.__class__.__class__ # type
print Test.__class__.__class__.__class__ # type
a = type('Foo', (), {})
print a # class Foo
t = a()
print type(t) # Foo
print t.__class__ # Foo
print t.__class__.__class__ # type
从代码中可以看出:
- t的__class__,为Test,这是它所属的类
- 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-8
def 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的作用主要是:
- 拦截类的创建
- 修改类
- 返回被修改的类
使用类实现metaclass
# coding: utf-8
class 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-8
class 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-><StringField:email>
Found mapping: password-><StringField:password>
Found mapping: id-><IntegerField:id>
Found mapping: name-><StringField:username>
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
python中的metaclass的更多相关文章
- python 中的metaclass和baseclasses
提前说明: class object 指VM中的class 对象,因为python一切对象,class在VM也是一个对象,需要区分class对象和 class实例对象. class instance ...
- python 中的 metaclass
最遇到一个问题. class Meta(type): pass class M1(Meta): pass class M2(metaclass=M1): pass class Test(M2,meta ...
- Python中的__init__和__new__
一.__init__ 方法是什么? 使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候.例如: # -*- c ...
- python中的__init__和__new__的区别
一.__init__ 方法是什么?(init前后的线是双下划线) 使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例 ...
- Python中__new__的作用
__new__ 的作用 依照Python官方文档的说法,__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径.还 ...
- Python中__init__和__new__的区别详解
__init__ 方法是什么? 使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候.例如: # -*- cod ...
- 详解Python中的__init__和__new__(静态方法)
一.__init__ 方法是什么? 使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候.例如: #-*- co ...
- Python中的 _init__和 _new__的区别
使用python 的面向对象写过程序之后,相信童鞋对 __init__ 方法已经非常的熟悉了.这个方法通常是 在初始化一个实例的时候使用的. 例如: class MysqlConnector(obje ...
- 详解Python中的__init__和__new__
转载:https://my.oschina.net/liuyuantao/blog/747164 1.__init__ 方法是什么? 使用Python写过面向对象的代码的同学,可能对 __init__ ...
随机推荐
- Xcode 设置代码不自动换行
"command + ,"打开设置界面后,找到"Text Editing" 然后选择"Indentation", 最后找到"lin ...
- Java 测试URL地址是否能正常连接
public static int testWsdlConnection(String address) throws Exception { int status = 404; try { URL ...
- 手机驱动无法正常安装,出现adb interface失败
手机一直无法用usb连接上电脑,试了各种方法,总是提示安装驱动失败,或者找不到文件. 在网上找了各种方法,后来结果证明,是我自己手贱了,... 方法: Win7系统用户已经碰到几次在安装adb驱动时提 ...
- P1-概率论基础(Primer on Probability Theory)
2.1概率密度函数 2.1.1定义 设p(x)为随机变量x在区间[a,b]的概率密度函数,p(x)是一个非负函数,且满足 注意概率与概率密度函数的区别. 概率是在概率密度函数下对应区域的面积,如上图右 ...
- Ubuntu10.0.4安装NDK
android版本遇到.so文件crash,需要使用ndk来定位报错代码. 从这里下载ndk安装文件: http://www.androiddevtools.cn/ 运行 ./android-ndk- ...
- C++矢量图形库系列(1)——矢量图形库乱谈(转)
转自:http://blog.sina.com.cn/s/blog_4265e1760100lg03.html 本系列篇章的主要内容是讲解矢量图形库的编译.开发和使用.并不对他们周边的内容做过多的描述 ...
- python基础整理笔记(三)
一. python的几种入参形式:1.普通参数: 普通参数就是最一般的参数传递形式.函数定义处会定义需要的形参,然后函数调用处,需要与形参一一对应地传入实参. 示例: def f(a, b): pri ...
- 思维导图软件TheBrain 8全新发布 提供更强大的信息管理
TheBrain思维导图软件是全球唯一一款动态的网状结构的思维导图软件,广泛用于学习.演讲.项目管理.会议.需求调研与分析等.其独特的信息组织方式使得用户可以创建并连接到数以万计的数字想法,为此在全球 ...
- dialogic d300语音卡驱动重装后启动报错问题解决方法
dialogic d300 驱动重装后 dlstart 报错解决 问题描述:dlstart 后如下报错 [root@BJAPQ091 data]#dlstop Stopping Dialogic ...
- socket基础
一.socket简介 1. 套接字 套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象.它们允许程序接受并进行连接,如发送和 ...