【原创】Python 对象创建过程中元类, __new__, __call__, __init__ 的处理
原始type:
type是最原始的元类,其__call__方法是在你使用" t_class = type(classname_string, base_classes_tuple, attributes_dict)" 这种语法来使用时, 在__call__方法内使用又会调用type的__new__和__init__方法来创建classname_string的具体类,并初始化类信息。当type(***)调用完成, classname_string代表的类可以用来创建实例了。
元类调用过程: 原始type元类同理
如下流程:假设是MyMeta元类,而不是原始type元类
例子: MyClass = MyMeta('MyClass', bases, attributes)
my_meta_type = type(MyMeta)MyClass= my_meta_type.__call__(MyMeta, cls, bases, attributes)
在__call__中应该是如下操作:
MyClass = MyMeta.__new__(MyMeta, cls, bases, attributes)
meta_class = MyClass.__metaclass__
meta_class.__init__(MyClass, cls, bases, attributes)
return MyClass 最终返回MyClass类
上述元类有一个很令人迷惑的地方,需要注意到,当你的元类是自定义的元类的时候,假设是MyMeta,此时调用的是MyMeta的父元类type的__call__,所以假设MyMeta自定义了__call__,你要知道当调用MyMeta()的时候,该函数并没有被调用,调用的是type的__call__,你定义MyClass对象实例时才会调用该函数。如果你在MyClass对象中也定义了__call__函数,那么假设你定义了一个MyClass的对象myobj,你使用myobj()形式用法时,调用的是MyClass的__call__ 。
总结: 元类处理过程:定义一个类时,使用声明或者默认的元类对该类进行创建,对元类求type运算,得到父元类(该类声明的元类的父元类),调用父元类的__call__函数,在父元类的__call__函数中, 调用该类声明的元类的__new__函数来创建对象(该函数需要返回一个对象(指类)实例),然后再调用该元类的__init__初始化该对象(此处对象是指类,因为是元类创建的对象),最终返回该类。
你可以简单实验以下,自定义俩个元类,该俩个元类是父子关系,在定义一个类,设置使用自定义元类的子元类,发现会调用自定义元类的父元类中call的输出,子元类的call并没有输出,在定义类的对象时才输出了
例子如下:
class SuperMeta(type):
def __call__(metaname, clsname, baseclasses, attrs):
print 'SuperMeta Called'
clsob = type.__new__(metaname, clsname, baseclasses, attrs)
type.__init__(clsob, clsname, baseclasses, attrs)
return clsob
class MyMeta(type):
__metaclass__ = SuperMeta
def __call__(cls, *args, **kwargs):
print 'MyMeta called', cls, args, kwargs
ob = object.__new__(cls, *args)
ob.__init__(*args)
return ob
print 'create class'
class Kls(object):
__metaclass__ = MyMeta
def __init__(self, data):
self.data = data
def printd(self):
print self.data
print 'class created ---------------------'
# 你会发现定义了 Kls 类后输出了 SuperMeta 父元类的输出
ik = Kls('arun')
ik.printd()
ik2 = Kls('avni')
ik2.printd()
# 定义Kls对象实例才真的执行了MyMeta的call
为什么type会调用自己的呢,因为type的type还是type, 蛋疼一小会……
附加:
原始type的__call__应该是参数结构应该是:
metaname, clsname, baseclasses, attrs
原始type的__new__
metaname, clsname, baseclasses, attrs
原始type的__init__
class_obj, clsname, baseclasses, attrs
元类的__new__和__init__影响的是创建类对象的行为,父元类的__call__控制对子元类的 __new__,__init__的调用,就是说控制类对象的创建和初始化。父元类的__new__和__init__由更上层的控制,
一般来说,原始type是最初的父元类,其__new__和__init__是具有普遍意义的,即应该是分配内存、初始化相关信息等
元类__call__影响的是创建类的实例对象的行为,此时如果类自定义了__new__和__init__就可以控制类的对象实例的创建和初始化
__new__和__init__ 影响的是创建对象的行为,当这些函数在元类中时,影响创建的是类;同理,当这俩个函数在普通类中时,影响创建的是普通的对象实例。
__call__ 影响()调用行为, __call__是在创建类的时候调用,即: class Test(object): __metaclass__=type, 定义类时就是创建类,此时会调用元类的__call__,如果元类有继承,子元类定义时执行的是父元类的__call__。
如果是普通类实例化对象,调用的是普通类的__call__
有点绕啊。。。
参考:
http://pythoncentral.io/how-metaclasses-work-technically-in-python-2-and-3/
http://stackoverflow.com/questions/2608708/what-is-the-difference-between-type-and-type-new-in-python Florentin的答案
【原创】Python 对象创建过程中元类, __new__, __call__, __init__ 的处理的更多相关文章
- 类和对象的创建过程(元类,__new__,__init__,__call__)
一. type() 1.创建类的两种方式 方式一 class MyClass(object): def func(self,name): print(name) myc = MyClass() pri ...
- 通过ORM模型看python对象创建过程
简易django ORM模型如下所示: #!/usr/bin/env python # encoding: utf-8 """ @version: 1.0 @author ...
- exec , 元类,__new__, __call__ , 单例模式 , 异常
1,类也是对象 ''' 动态语言 可以在运行期间 动态生成类 修改对象属性 静态语言 ''''' ''' type(object_or_name, bases, dict) type(object) ...
- python中对象、类型和元类之间的关系
在python中对象.类型和元类构成了一个微妙的世界. 他们有在这个世界里和平共处,相辅相成.它们遵循着几条亘古不变的定律: 1.python中无处不对象 2.所有对象都有三种特性:id.类型.值 3 ...
- Python中对象、类型、元类之间的关系
Python里的对象.类型和元类的关系很微妙也很有意思. 1989年圣诞节期间,上帝很无聊,于是创造了一个世界. 对象 在这个世界的运转有几条定律. 1.一切都是对象 对象(object)是这个世界的 ...
- python中元类(metaclass)的理解
原文地址:http://www.cnblogs.com/tkqasn/p/6524879.html 一:类也是对象 类就是一组用来描述如何生成一个对象的代码. 类也是一个对象,只要你使用关键字clas ...
- 谈谈Python中元类Metaclass(一):什么是元类
简单的讲,元类创建了Python中所有的对象. 我们说Python是一种动态语言,而动态语言和静态语言最大的不同,就是函数和类不是编译时定义的,而是运行时动态创建的. 比方说我们要定义一个HelloW ...
- 对python中元类的理解
1. 类也是对象 在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍然成立: >>> class ObjectCreator(object): ...
- Java中对象创建过程
本文介绍的对象创建过程仅限于普通Java对象,不包括数组和Class对象. 1.类加载检查 虚拟机遇到一条new指令时,首先去检查该指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用 ...
随机推荐
- JQuery判断浏览器类型(IE, Firefox…)
1 2 3 4 5 6 7 8 9 10 11 $(function() { if ($.browser.msie) { alert("这是一个IE浏览器&q ...
- ajax请求下载Execl表
Execl表是经常要用到的存放二位数据的表格,Java也可以直接操作Execl表,经常用到的方式就是jxl和poi. 在这次项目中,我用到的poi往Execl中写数据,刚开始设计的是前端发送一个aja ...
- 聊聊MyBatis缓存机制【美团-推荐】
聊聊MyBatis缓存机制 2018年01月19日 作者: 凯伦 文章链接 18778字 38分钟阅读 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用My ...
- element-ui中使用font-awesome字体图标
element-ui提供的字体图标是很少的,所以我们需要集成其它图标来使用,nodejs的集成官方有说明,这里说明一下非nodejs开发集成图标 首先下载fontawesome,需要更改里面图标前缀, ...
- Java向数据库中一次性插入大量数据
String sql = “insert into username.tablename(id) values(?)”; PreparedStatement stmt = conn.prepareSt ...
- [转]c# winform tcp connect timeout 连接超时设置
转自:https://www.cnblogs.com/jhlong/p/5622336.html 简单的c# TCP通讯(TcpListener) C# 的TCP Socket (同步方式) C# 的 ...
- 读《锋利的jQuery》中first-child时的一个细节
今天在看<锋利的jQuery>这书时,看到过滤选择器那一节.有个知识点引起了我的注意. (我不用书里一模一样的代码做例子)举个简单的例子-代码: <ul> <li> ...
- 2016最新Java学习计划
一.Java学习路线图 二.Java学习路线图--视频篇 六大阶段 学完后目标 知识点 配套免费资源(视频+笔 记+源码+模板) 密码 第一阶段 Java基础 入门 学习周期: 35天 ...
- js获取当前页面url信息方法(JS获取当前网址信息)
设置或获取对象指定的文件名或路径. alert(window.location.pathname) 设置或获取整个 URL 为字符串. alert(window.location.href); 设置或 ...
- 使用docker安装使用gitlab
1.下载镜像 gitlab/gitlab-ce:latest 当前gitlab最新版本为10.0.4 2.在服务器上创建目录 mkdir -p /home/work/ins/co ...