# 一张表一个类,表内每一行就是一个实例
'''
一个单独的元类使用的程序分析。
''' 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) # self.__class__:得到当前实例的类 self.__class__.__name__:得到当前实例的类的名字
# self.name:实例名字 __repr__ = __str__ 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这个元类的__new__()方法在本程序中的作用:
# 1.在依据ModelMetaclass创建Model时,由Model向元类传入的参数为:cls:<class '__main__.ModelMetaclass>;name:Model;bases()传入的bases是一个tuple:(<class 'dict'>,),attrs是一个由Model类中的所有的方法名和方法函数地址组成的dict.
# 然后再传入该元类后,执行__new__(),由于name=='Model'所以返回由type('Model', (dict,), attrs)所组成的类。return了,不往下执行。
# 2.在准备创建User类时,可以见class User上的注释。此时name==User。在__new__()内执行时。if失效。往后运行,创建一个名为mappings的dict。mappings的作用是用来存储此时User里面的4个属性(id,name,password,email).并将作为一个属性返回。
# 由于attrs包含了User中的所有的属性。所以先要用isinstance(v,Field)判断是不是id/name/password/email中的一个。若找到,则复制如mappings中。
# 删除User的4个id/name/password/email属性(防止造成实例的属性遮盖类的同名属性)。
# 添加两个属性__mappings__和__table__。并把最后的attrs作为最后要生成的名为User的类的所有方法。 class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
# print('hello',cls,'---',name,'---',bases,'---',attrs)
if name == 'Model': # 如果名字==Model。那么久用type创建一个类。在这里,name:要创建的类的名字;bases:要创建的类的父类;attrs:类的方法与函数对照字典。
# print(attrs)
return type.__new__(cls, name, bases, attrs)
# return super().__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():
# print([(k, v) for k, v in attrs.items() if isinstance(v, Field)])
attrs.pop(k)
attrs['__mappings__'] = mappings
attrs['__table__'] = name
# print(attrs)
return type.__new__(cls, name, bases, attrs) class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
# print(Model.mro())
# 调用父类dict类的__init__方法,需要传入一个dict。
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():
# print(v.name)
fields.append(v.name)
params.append('?')
# 当u=User(......)后,u就具有了4个属性u.id=12345 u.name='Michael' u.email='test@orm.org' u.password='my-pwd'
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__,
','.join(fields),
','.join(params))
# join()函数
# 语法: 'sep'.join(seq) 该例为:','.join(fields) 对应的:sep=, seq=fields.
# 参数说明
# sep:分隔符。可以为空
# seq:要连接的元素序列、字符串、元组、字典
# 上面的语法即:以sep作为分隔符,将seq所有的元素合并成一个新的字符串
# 返回值:返回一个以分隔符sep连接各个元素后生成的字符串
print('SQL: %s' % sql)
# print(args)
print('ARGS: %s' % str(args)) # 当用户定义一个class User(Model)时,Python解释器首先在当前类User的定义中查找metaclass,如果没有找到,
# 就继续在父类Model中查找metaclass,找到了,就使用Model中定义的metaclass的ModelMetaclass来创建User类,
# 也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。 # 在创建User时,程序会先执行完里面的4条属性声明语句。然后再将包含该4条属性和所有的方法组成一个dict。传入ModelMetaclass的__new__中。 # 通过使用元类,程序员可以在这里自定义一个类(数据库的一张表)需要输入哪些元素。 class User(Model):
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password') # 在这里时,User类里面没有4个单独的id/name/password/email属性,只有__mappings__和__table__属性。 # print(type(User.id))
# print(dir(User))
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# print(u.name.name)
# print(u.id.name)
# print(u.email.name)
# print(u.password.name)
# print(dir(u.id))
# print(u.id.name)
u.save()
# print(dir(Model))

python的MetaClass的代码分析。基于廖雪峰博客代码的更多相关文章

  1. step2: 爬取廖雪峰博客

    #https://zhuanlan.zhihu.com/p/26342933 #https://zhuanlan.zhihu.com/p/26833760 scrapy startproject li ...

  2. git入门笔记汇总——(廖雪峰博客git入门)

    本文内容是对廖雪峰老师Git教程做的笔记,外加一些自己的学习心得,还抱着学以致用的心态来实践一番 如有显示错误 请移步本人github:git教程小结 Git学习笔记 Git简介 安装Git 创建版本 ...

  3. python 3 廖雪峰博客笔记(二) python解释器

    python 解释器用于理解 python代码,存在多种python解释器 CPython 官方版本python解释器,用C语言开发,使用最广泛 IPython 基于CPython,在交互方式上有所增 ...

  4. python 1 学习廖雪峰博客

    输出 用print()在括号中加上字符串,就可以向屏幕上输出指定的文字.比如输出'hello, world',用代码实现如下: >>> print('hello, world') p ...

  5. python 3 廖雪峰博客笔记(三) 命令行模式与交互模式

    python 的代码一般保存为 .py结尾的文本文件格式 比如 add.py 里写下如下内容 100 + 200 执行 add.py有两种方式: 1. 命令行方式:将python代码写入脚本中执行 p ...

  6. python 3 廖雪峰博客笔记(一) python特性

    python 是一种解释性语言,代码在执行时会一行一行翻译成CPU能理解的机器语言. python 的特点是简单优雅. python 的优点是 代码优雅 基础代码库丰富,包括网络.文件.GUI.数据库 ...

  7. git 廖雪峰博客笔记

    其他参考 http://www.softwhy.com/article-8498-1.html 本地模拟多用户来学习 参考   其实就是克隆两个目录,两个目录的git config 不同的名字和邮箱, ...

  8. Linux内核分析第二周学习博客——完成一个简单的时间片轮转多道程序内核代码

    Linux内核分析第二周学习博客 本周,通过实现一个简单的操作系统内核,我大致了解了操作系统运行的过程. 实验主要步骤如下: 代码分析: void my_process(void) { int i = ...

  9. 基于Hexo搭建博客并部署到Github Pages

    基于Hexo搭建博客并部署到Github Pages 之前在简书上写东西,觉得自己还是太浮躁.本来打算用Flask自己写一个,以为是微框架就比较简单,naive.HTML.CSS.JS等都要学啊,我几 ...

随机推荐

  1. Asp.Net MVC源码调试

    首先下载MVC源代码,下载地址为:https://aspnetwebstack.codeplex.com/ 打开项目,卸载test文件夹下的所有项目和System.Web.WebPages.Admin ...

  2. January 12 2017 Week 2 Thursday

    Although it rains, throw not away your watering pot. 纵然天下雨,休把水壶丢. Don't throw away your watering pot ...

  3. 二值形态学——腐蚀与膨胀 及 C语言代码实现

    参考文献:数字图像处理(第三版) 何东健 西安电子科技大学出版社 二值形态学中的运算对象是集合, 但实际运算中, 当涉及两个集合时并不把它们看作是互相对等的. 一般设A为图像集合, S为结构元素, 数 ...

  4. BZ2763: [JLOI2011]飞行路线

    Description Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司.该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并 ...

  5. SpringBoot实战(十三)之缓存

    什么是缓存? 引用下百度百科的解释: 缓存就是数据交换的缓冲区(又称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,找到了则直接执行,找不到的话则从内存中查找.由于缓存的运行速度 ...

  6. Spring(十一)之AOP

    Spring 框架的一个关键组件是面向方面的编程(AOP)框架.面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点.跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于 ...

  7. pymongo的安装和使用

    1.安装 MongoDB的python接口pymongo的安装方法有多种,如源码.easy_install.pip都可以.采用pip安装,很简单. pip install pymongo 安装完成后可 ...

  8. 用javascript调用表单验证事件时,为什么return false了还是把表单submit了?

    表单提交前,都会有定义一个验证的方法以对用户提交的内容进行限定,今天写到了这个,但出现了一个好郁闷的东西,就是一点提交了,调用我自己写的一个CheckForm()方法时,我明明写了return fal ...

  9. crobtab不执行定时任务的原因及解决办法

    服务未能启动或者权限问题.路径问题,网上很多种解决办法,就不多的说了. 1.查看crond日志: cat /var/log/cron 刚开始我的日志里面并没有执行写的脚本. 原因在于在脚本开始没有写s ...

  10. 用javascript编写地区表单ES6

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...