04 -- 元类和ORM
本篇主要介绍元类,为什么说一切皆对象;如何动态的创建类等;以及ORM,即什么是ORM等知识
一、元类
1.1 在Python中一切皆对象
在学习元类中我们首先需要了解一个概念-- python中一切皆对象,那么为什么这么说呢?
扩展:通过globals()--查看所有的全局变量,当我们定义全局变量时,python会将这些全局变量存放至一个字典中;而其中包含 __builtin__内嵌模块,当我们 globals()["__builtin__"].__dict__,可以看到内嵌模块中的所有内置函数或者内置模块即其对应的引用指针,例如:(只截取了小部分)
类也是对象,当我们定义一个类,其实也是通过另外一个类(type)类创建一个实例对象,只是这个对象比较特殊,拥有着创建实例对象的特殊功能。而可能你就会问了,那么type是由什么类实例化得到的呢? type就是元类,其自身实例对象了其自身。
1.2 使用type创建类
通常我们定义类有使用以下的方式创建类:
>>> class ObjectCreator(object):
… pass
而同时 我们也可以通过type实例化得到类对象,其效果是一摸一样的,这种创建类的方式我们称为动态的创建类;
class Test1(object):
name="alex"
age = 30 Test2 = type("Test2",(object,),{"name":"alex","age":30})
以上两种形式定义的类是完全一样的(除了类名),前者是通过关键字定义的类对象,而后者是通过type类实例化得到的类对象;
当然也可以为该类定义实例方法、类方法、以及静态方法等,例如:
# 析构方法
def __init__(self,name,age):
self.name = name
self.age = age # 实例方法
def foo(self):
print("in the foo",self) # 类方法
@classmethod
def bar(cls):
print("in the foo",cls) #静态方法
@staticmethod
def cal():
print("in the cal") Test =type("Test",(object,),{"info":"这是一个type实例化的类","foo":foo,"bar":bar,"cal":cal,"__init__":__init__}) test = Test("alex",22)
test.bar()
test.foo()
test.cal()
1.3 什么是元类?
元类:通俗来将 元类就是可以创建类的"东西";准确来说:元类就是可以实例化得到类对象的一种类,上述的type类就是元类;
MyClass = MetaClass() # 使用元类创建出一个对象,这个对象称为“类”
my_object = MyClass() # 使用“类”来创建出实例对象
即:
1、可以这样理解:元类通过实例化得到类对象,类通过实例化得到实例化对象。
例如上述你看到的:
MyClass = type('MyClass', (), {})
这种方式便是:通过元类type传入参数(参数1-- 得到类对象的名称 , 参数二----该类对象的父类们的父类名,参数三-- 存储该类对象的类属性、类方法),即便是通过这种方式得到一个类对象。
Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。
1.4 自定义元类
在上述我们知道可以通过type动态的创建类,那么我们是否可以定义一个类,利用继承或者组合的方式,通过派生来为创建的类对象增设一些新的功能呢?
1.4.1 metaclass
在自定义元类之前我们需要了解以下 metaclass:
class MyType(type):
def __new__(cls,name,prants,attrs):
print("in the MyType..")
return type(name,prants,attrs)
pass class Foo(object,metaclass=MyType):
pass print(Foo.__class__)
Python做了如下的操作:
- Foo中有 metaclass=MyType 这个属性吗?如果是,Python会通过 meteclass=MyType 创建一个名字为Foo的类(对象)
- 如果Python没有找到 metaclass=xxx ,它会继续在Bar(父类)中寻找metaclass=xxx 属性,并尝试做和前面同样的操作。
- 如果Python在任何父类中都找不到 metaclass=xxx ,它就会在模块层次中去寻找metaclass=xxx ,并尝试做同样的操作。
- 如果还是找不到metaclass=xxx ,Python就会用内置的type来创建这个类对象。
1.4.2 自定义元类
自定义元类的主要目的就是为了当创建类时能够自动地改变类。接下来我们实现 元类把所有的属性都改成大写形式,即相当于为原来通过type类创建类对象添加或修改其方法,而不改变源代码的效果,优点类似于装饰器的效果;
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
def __new__(cls, class_name, class_parents, class_attr):
# 遍历属性字典,把不是__开头的属性名字变为大写
new_attr = {}
for name, value in class_attr.items():
if not name.startswith("__"):
new_attr[name.upper()] = value # 方法1:通过'type'来做类对象的创建
return type(class_name, class_parents, new_attr) # 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, class_name, class_parents, new_attr) # python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
bar = 'bip' # python2的用法
# class Foo(object):
# __metaclass__ = UpperAttrMetaClass
# bar = 'bip' print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True f = Foo()
print(f.BAR)
# 输出:'bip'
其本质是:
就是这样,除此之外,关于元类真的没有别的可说的了。但就自定义元类本身而言,它们其实是很简单的:
- 拦截类的创建
- 修改类
- 返回修改之后的类
二、ORM
2.1 什么是ORM?
ORM 是 python编程语言后端web框架 Django的核心思想,“Object Relational Mapping”,即对象-关系映射,简称ORM。
一个句话理解就是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,用它实例化传入的实例属性作为记录,当对这个实例对象操作时,能够对应MySQL语句。
- 所谓的ORM就是让开发者在操作数据库的时候,能够像操作对象时通过
xxxx.属性=yyyy
一样简单,这是开发ORM的初衷; - 只不过ORM的实现较为复杂,Django中已经实现了 很复杂的操作,本节知识 主要通过完成一个 insert相类似的ORM,理解其中的道理就就可以了;例如:
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
mappings = dict()
# 判断是否需要保存
for k, v in attrs.items():
# 判断是否是指定的StringField或者IntegerField的实例对象
if isinstance(v, tuple):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v # 删除这些已经在字典中存储的属性
for k in mappings.keys():
attrs.pop(k) # 将之前的uid/name/email/password以及对应的对象引用、类名字
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs) class User(metaclass=ModelMetaclass):
uid = ('uid', "int unsigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)")
# 当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储
# 以上User类中有
# __mappings__ = {
# "uid": ('uid', "int unsigned")
# "name": ('username', "varchar(30)")
# "email": ('email', "varchar(30)")
# "password": ('password', "varchar(30)")
# }
# __table__ = "User"
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value) def save(self):
fields = [] # 用来存储获取的字段名
args = [] # 用来存储值
for k, v in self.__mappings__.items():
fields.append(v[0])
args.append(getattr(self, k, None)) # 由于传入的值需为字符串类型则进行转换
args_temp = list()
for temp in args:
# 判断入如果是数字类型
if isinstance(temp, int):
args_temp.append(str(temp))
elif isinstance(temp, str):
args_temp.append("""'%s'""" % temp)
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
print('SQL: %s' % sql) u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
# print(u.__dict__)
u.save()
优化如下:
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
mappings = dict()
# 判断是否需要保存
for k, v in attrs.items():
# 判断是否是指定的StringField或者IntegerField的实例对象
if isinstance(v, tuple):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v # 删除这些已经在字典中存储的属性
for k in mappings.keys():
attrs.pop(k) # 将之前的uid/name/email/password以及对应的对象引用、类名字
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs) class Model(object, metaclass=ModelMetaclass):
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value) def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v[0])
args.append(getattr(self, k, None)) args_temp = list()
for temp in args:
# 判断入如果是数字类型
if isinstance(temp, int):
args_temp.append(str(temp))
elif isinstance(temp, str):
args_temp.append("""'%s'""" % temp)
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
print('SQL: %s' % sql) class User(Model):
uid = ('uid', "int unsigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)") u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
# print(u.__dict__)
u.save()
over~~~
04 -- 元类和ORM的更多相关文章
- Python 元类实现ORM
ORM概念 ORM(Object Ralational Mapping,对象关系映射)用来把对象模型表示的对象映射到基于 SQL 的关系模型数据库结构中去.这样,我们在具体的操作实体对象的时候,就不 ...
- 元类实现ORM
1. ORM是什么 ORM 是 python编程语言后端web框架 Django的核心思想,"Object Relational Mapping",即对象-关系映射,简称ORM. ...
- 使用元类 编写ORM
元类 一句话: 元类定制类的创建行为 知识点 1.类的创建: python这种动态语言,函数和类的定义,不是编译时定义的,而是运行时动态创建的. Python解释器遇到class定义时,仅仅是扫描一下 ...
- 元类、orm
目录 一.内置函数exec 二.元类 1. 什么是元类 2. 元类的作用 3. 创建类的两种方法 4. 怎么自定义创建元类 三.ORM 1. ORM中可能会遇到的问题 2. ORM中元类需要解决的问题 ...
- 元类应用ORM实现
首先看下一个简单的例子 # 需求 import numbers class Field: pass class IntField(Field): # 数据描述符 def __init__(self, ...
- python-元类和使用元类实现简单的ORM
元类 面向对象中,对象是类的实例,即对象是通过类创建出来的,在python中,一切皆对象,同样,类也是一个对象,叫做类对象,只是这个类对象拥有创建其子对象(实例对象)的能力.既然类是对象,那么类是通过 ...
- Python面向对象篇之元类,附Django Model核心原理
关于元类,我写过一篇,如果你只是了解元类,看下面这一篇就足够了. Python面向对象之类的方法和属性 本篇是深度解剖,如果你觉得元类用不到,呵呵,那是因为你不了解Django. 在Python中有一 ...
- 神级程序员通过两句话带你完全掌握Python最难知识点——元类!
千万不要被所谓"元类是99%的python程序员不会用到的特性"这类的说辞吓住.因为 每个中国人,都是天生的元类使用者 学懂元类,你只需要知道两句话: 道生一,一生二,二生三,三生 ...
- Python中的元类
从前面"Python对象"文章中了解到,在Python中一切都是对象,类可以创建实例对象,但是类本身也是对象. class C(object): pass c = C() prin ...
随机推荐
- 运维笔记--linux下忘记mysql root密码
补充链接:Windows下忘记密码处理: https://www.cnblogs.com/hellojesson/p/5972950.html 场景描述: Linux环境下忘记 root 密码, 1. ...
- torch7安装的坑
https://github.com/torch/torch7/issues/1086 sudo su export TORCH_NVCC_FLAGS="-D__CUDA_NO_HALF_O ...
- LeetCode 541. 反转字符串 II(Reverse String II)
541. 反转字符串 II 541. Reverse String II
- 【C++札记】内联函数
概述 函数的使用使得相同代码不必多次重写,但会带来额外的开销,函数调用的过程中会有入栈和出栈,这些都会消耗时间. 如果一个函数在程序运行过程中被成千上万次调用,那么这个开销也是不容忽视的,C++中引入 ...
- linux 创建虚拟机常见错误
无法打开内核设备global vmx86 重启虚拟机所有服务 无法创建虚拟机 需要使用管理员身份运行vm即可
- 爬虫请求库之requests库
一.介绍 介绍:使用requests可以模拟浏览器的请求,比之前的urllib库使用更加方便 注意:requests库发送请求将网页内容下载下来之后,并不会执行js代码,这需要我们自己分析目标站点然后 ...
- 【LEETCODE】38、167题,Two Sum II - Input array is sorted
package y2019.Algorithm.array; /** * @ProjectName: cutter-point * @Package: y2019.Algorithm.array * ...
- 【C#】上机实验九
1. 设计一个Windows登陆窗体应用程序,能够实现根据现有表中数据模拟登陆,并设置相关属性,具体界面如下. 可能使用到的类: (1)SqlConnection (2)SqlCommand (3)S ...
- HBuilder 方便局域网访问调试
同一个局域网,通过IP不能访问我本地的项目,各种测试发现原来是防火墙的问题: 这里附上参考文档:内置web服务器被防火墙禁用导致预览和运行异常的解决方案
- ICO学习说明
IOC叫做控制反转,可以理解为我要做一件事,分为1,2,3,4这4部,我们可以在一个函数实现这四步,控制反转就是将这个流程体现在框架中.将原来实现在应用程序流程控制转移到框架中,框架利用一个引擎驱动整 ...