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 ...
随机推荐
- Spring cloud微服务安全实战-7-1章节概述
前面的章节都是围绕这微服务的安全在讲一些东西,包括微服务本身api的安全.网关的安全.怎么去做安全中心,包括认证服务器,权限的服务.权限的设计,怎么来实现SSO.然后sentinel来实现统一的熔断, ...
- 【python基础】python开启GPU加速
前言 训练时使用GPU可以加速程序运行,本文介绍如何使用GPU加速. 前提条件 1. 机子有GPU显卡,并安装GPU显卡驱动: 2. 安装GPU的使用环境,CUDA等: 3. 打开nvidia-smi ...
- Jenkins配置发送邮件步骤
Jenkins配置邮件步骤,以下我是以腾讯企业邮箱账号作为演示 1.配置Jenkins系统管理员邮件地址: 下拉找到以下位置 2.配置邮件通知 继续下拉,找到邮件配置.输入SMTp服务器以及邮箱后缀, ...
- Java之Java程序的基本结构
Java的基本结构是 /** * 可以用来自动创建文档的注释 */ public class Hello { public static void main(String[] args) { // 向 ...
- 【视频开发】ffmpeg 的编译选项
ffmpeg xxxx$ ./configure --help Usage: configure [options] Options: [defaults in brackets after desc ...
- vue-cli3 每次打包都改变css img js文件名,还有自带版本号
let Version = new Date().getTime(); css: { // 是否使用css分离插件 ExtractTextPlugin extract: { //一种方式,打包后的cs ...
- 本地dev环境,运行时用node模块自动读取并整合文件
const http = require('http'); const fs = require('fs'); const path = require('path'); const glob = r ...
- docker下安装nginx并实现https访问
一.启动容器 docker run --detach --name wx-nginx -p 443:443 -p 80:80 -v /home/nginx/data:/usr/share/nginx/ ...
- 【原创】C++STL multiset
资料来源:官方文档 multiset是一个按照特定排序储存元素的容器,多个元素可以有相同的值.元素的值即为其本身的键值.multiset中的值无法修改,可插入删除.常用于实现二叉树. 定义一个mult ...
- 小程序常用操作,if,for,跳转,弹出提示
if <block wx:if="{{result.child_items}}"> ... </block> <block wx:else> . ...