一、 type()

1、创建类的两种方式

方式一

class MyClass(object):
def func(self,name):
print(name) myc = MyClass() print(MyClass, type(MyClass))
print(myc, type(myc))

我们创建了一个名为MyClass的类,并实例化了这个类,得到其对象myc

上面代码打印的结果为:

<class '__main__.MyClass'>    <class 'type'>
<__main__.MyClass object at 0x0288F8F0> <class '__main__.MyClass'>

type()函数可以查看一个类型或变量的类型,MyClass是一个class,它的类型就是type,而myc是一个实例,它的类型就是class MyClass

我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。

type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出MyClass类,而无需通过Class MyClass(object)...的定义:

方式二

动态创建类

type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name) MyClass = type('MyClass', (object,), {'func':fn}) # 创建MyClass类,得到一个type的类对象
# MyClass = type('MyClass', (object,), {'func':lambda self,name:name}) # 创建MyClass类 myc=MyClass() print(MyClass, type(MyClass))
print(myc, type(myc))

打印结果:

<class '__main__.MyClass'>   <class 'type'>
<__main__.MyClass object at 0x0364B830>   <class '__main__.MyClass'>

要创建一个class对象,type()函数依次传入3个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名func上。

通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类(元类,默认为type,也可以自定制)创建而来。type也是由type创建。。

二、元类(metaclass)

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义元类(不自定义时,默认用type),然后创建类。

连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是元类创建出来的“实例”。

默认情况下,类是使用type()构造的。类主体在一个新的名称空间中执行,类名在本地绑定到类型的结果(名称、基、名称空间)。

可以通过在类定义行中传递元类关键字参数来定制类创建过程,或者从包含此类参数的现有类继承。在下面的示例中,MyClass和MySubclass都是Meta的实例:

class Meta(type):
pass class MyClass(metaclass=Meta):
pass class MySubclass(MyClass):
pass

使用metaclass的两种方式

class MyType(type):  # 自定义一个type的派生类
def __init__(self,*args,**kwargs):
    print('xx')
super(MyType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls,*args, **kwargs)
cls.__init__(obj,*args, **kwargs)
return obj def with_metaclass(base):
return MyType("MyType2",(base,),{}) # 方式一
class Foo(metaclass=MyType):  # metaclass=MyType,即指定了由MyType创建Foo类,当程序运行,用到class Foo时,即调用MyType的__init__方法,创建Foo类
def __init__(self,name):
self.name = name #方式二 在Flask的wtform的源码中用到过
# class Foo(with_metaclass(object)):
# def __init__(self,name):
# self.name = name a=Foo('name')

方式一:即用类的形式

  执行代码后,当遇到class Foo时即声明要创建一个Foo类,就会调用type的__init__方法创建类,由于此处(metaclass=MyType),即指定了Foo类的创建方式,所以会执行type的派生类MyType的__init__方法,创建Foo类,打印一次'xx'

   *一般情况下, 如果你要用类来实现metaclass的话,该类需要继承于type,而且通常会重写type的__new__方法来控制创建过程。

  * 在metaclass里面定义的方法会成为类的方法,可以直接通过类名来调用

方式二:用函数的形式

  构建一个函数,返回一个type的派生类对象,例如叫type的派生类, 需要3个参数:name, bases, attrs

  name: 类的名字

  bases: 基类,通常是tuple类型

  attrs: dict类型,就是类的属性或者函数

metaclass 原理

1.基础

metaclass的原理其实是这样的:当定义好类之后,创建类的时候其实是调用了type的__new__方法为这个类分配内存空间,创建好了之后再调用type的__init__方法初始化(做一些赋值等)。所以metaclass的所有magic其实就在于这个__new__方法里面了。

说说这个方法:__new__(cls, name, bases, attrs)

cls: 将要创建的类,类似与self,但是self指向的是instance,而这里cls指向的是class

name: 类的名字,也就是我们通常用类名.__name__获取的。

bases: 基类

attrs: 属性的dict。dict的内容可以是变量(类属性),也可以是函数(类方法)。

所以在创建类的过程,我们可以在这个函数里面修改name,bases,attrs的值来自由的达到我们的功能。这里常用的配合方法是

getattr和setattr(just an advice)

2.查找顺序

元类是由以下优先规则决定的:

如果“元类”存在,它就被使用了。

否则,如果至少有一个基类,则使用它的元类(这首先查找类属性,如果没有找到,则使用它的类型)。

否则,如果一个名为元类的全局变量存在,就会使用它。

三、 __init__,__new__,__call__三个特殊方法

__new__: 对象的创建,是一个静态方法,第一个参数是cls。(想想也是,不可能是self,对象还没创建,哪来的self)

      其必须要有返回值,返回实例化出来的实例,需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。

__init__ : 对象的初始化, 是一个实例方法,第一个参数是self,该self参数就是__new__()返回的实例,__init__()在__new__()的基础上可以完成一些其它初始化的动作,__init__()不需要返回值。
__call__ : 对象可call,注意不是类,是对象。

1.对于__new__

class Bar(object):
pass class Foo(object):
def __new__(cls, *args, **kwargs):
return Bar() print(Foo())

打印结果为:

<__main__.Bar object at 0x0090F930>

可以看到,输出来是一个Bar对象。

  __new__方法在类定义中不是必须写的,如果没定义,默认会调用object.__new__去创建一个对象。如果定义了,就是会覆盖,使用自定义的,这样就可以自定制创建对象的行为。

单例模式也可以通过这种方式来创建。点击查看【几种单例模式的创建方法】

2.对于__init__

class Person(object):

  def __init__(self, name, age):
self.name = name
self.age = age
print('执行__init__') def __new__(cls, *args, **kwargs):
obj = object.__new__(cls) # 创建对象
print('执行__new__方法')
return obj p1 = Person('hc', 24)
print(p1)

打印结果:

执行__new__方法
执行__init__
<__main__.Person object at 0x028EB830>

  __init__ 方法通常用在初始化一个类实例的时候,但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 __new__ 方法。从打印结果就可以看出来

  若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。

3.对于__call__

  对象通过提供__call__(slef, *args ,**kwargs)方法可以模拟函数的行为,如果一个对象x提供了该方法,就可以像函数一样使用它,也就是说x(arg1, arg2...) 等同于调用x.__call__(self, arg1, arg2) 。

class Foo(object):
def __call__(self):
pass f = Foo() #类(),即执行元类的__call__
f() #对象(),即执行Foo的__call__

4、实例化对象的完整过程

class Foo(Bar):
pass

当我们写如这段代码时,Python做了如下的操作:

  Foo中有metaclass这个属性吗?如果是,Python会在内存中通过metaclass创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到metaclass,它会继续在Bar(父类)中寻找metaclass属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到metaclass,它就会在模块层次中去寻找metaclass,并尝试做同样的操作。如果还是找不到metaclass,Python就会用内置的type来创建这个类对象。

  把上面这段话反复读几次,现在的问题就是,你可以在metaclass中放置些什么代码呢?

  答案就是:可以创建一个类的东西。

  那么什么可以用来创建一个类呢?

  type,或者任何使用到type或者子类化type的东东都可以。

以上面的代码为例,我们实例化一个对象obj=Foo()时,会先执行Foo类的__new__方法,没写时,用父类的__new__方法,创建一个对象,并返回,然后执行__init__方法(自己有就用自己的,没有就用父类的),对创建的对象进行初始化。

obj()会执行Foo类的__call__方法,没有则用父类的

我们现在已经知道,类也是对象,是元类的对象,即我们实例化一个类时,调用其元类的__call__方法。

元类处理过程:定义一个类时,使用声明或者默认的元类对该类进行创建,对元类求type运算,得到父元类(该类声明的元类的父元类),调用父元类的__call__函数,在父元类的__call__函数中, 调用该类声明的元类的__new__函数来创建对象(该函数需要返回一个对象(指类)实例),然后再调用该元类的__init__初始化该对象(此处对象是指类,因为是元类创建的对象),最终返回该类

1.对象是类创建,创建对象时候类的__init__方法自动执行,对象()执行类的 __call__ 方法
2.类是type创建,创建类时候type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)

原始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://blog.jobbole.com/21351/

类和对象的创建过程(元类,__new__,__init__,__call__)的更多相关文章

  1. 深入理解Java对象的创建过程:类的初始化与实例化

    摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类 ...

  2. Java对象的创建过程:类的初始化与实例化

    一.Java对象创建时机 我们知道,一个对象在可以被使用之前必须要被正确地实例化.在Java代码中,有很多行为可以引起对象的创建,最为直观的一种就是使用new关键字来调用一个类的构造函数显式地创建对象 ...

  3. python cookbook第三版学习笔记十二:类和对象(三)创建新的类或实例属性

    先介绍几个类中的应用__getattr__,__setattr__,__get__,__set__,__getattribute__,. __getattr__:当在类中找不到attribute的时候 ...

  4. 深入理解java虚拟机---对象的创建过程(八)

    1.对象的创建过程 由于类的加载是一个很复杂的过程,所以这里暂时略过,后面会详细讲解,默认为是已加载过的类.着重强调对象的创建过程. 注意: 最后一步的init方法是代码块和构造方法. 以上是总图,下 ...

  5. python中对象、类型和元类之间的关系

    在python中对象.类型和元类构成了一个微妙的世界. 他们有在这个世界里和平共处,相辅相成.它们遵循着几条亘古不变的定律: 1.python中无处不对象 2.所有对象都有三种特性:id.类型.值 3 ...

  6. Python中对象、类型、元类之间的关系

    Python里的对象.类型和元类的关系很微妙也很有意思. 1989年圣诞节期间,上帝很无聊,于是创造了一个世界. 对象 在这个世界的运转有几条定律. 1.一切都是对象 对象(object)是这个世界的 ...

  7. 图解JAVA对象的创建过程

    前面几篇博文分别介绍了JAVA的Class文件格式.JVM的类加载机制和JVM的内存模型,这里就索性把java对象的创建过程一并说完,这样java对象的整个创建过程就基本上说明白了(当然你要有基础才能 ...

  8. Emit学习(2) - IL - 对象的创建过程

    上一篇的介绍中, 并没有介绍到对象的创建过程, 这一篇主要就介绍一下, 对象的创建过程. 其实熟悉了IL语法之后, 完全可以用Reflector反编译代码去查看. 而且正因为有这个工具, 可以对照着R ...

  9. 对象的创建过程(chapter5.7.3)

    总结一下对象的创建过程,假设有一个名为Dog的类: 1. 即使没有显示地使用static关键字,构造器实际上也是静态的方法,因此,当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dog类 ...

随机推荐

  1. java学习笔记之日期日历类

    java学习笔记之日期日历 Date日期类概述: 表示特定的瞬间,精确到毫秒 Date类的构造方法: 1.空参数构造方法 Date date = new Date(); 获取到当前操作系统中的时间和日 ...

  2. day4、Linux基础题目

    第一题 我想在/data/da 目录下面创建 一个 da.txt 文件 [root@ll ~]# cd /data/oldboyedu -bash: cd: /data/oldboyedu: No s ...

  3. 5个步骤,将 storyboard 从 iphone 版转变为 ipad 版

    1.将 iPhone 版的 Storyboard 复制为 iPad 的,比如 Main_iPad.storyboard 2.用文本编辑器(不要用 Xcode)打开 Main_iPad.storyboa ...

  4. 学习日记之工厂方法模式和Effective C++

    简单工厂模式VS工厂方法模式: 简单工厂模式:最大长处在于工厂类中必须包括必要的逻辑推断,依据client选择条件动态实例化相关类,对于client来说,去除了与详细产品的依赖. 工厂方法模式(Fac ...

  5. 【Android Studio快捷键】之导入对应包声明(import packages)

    可能import 单个声明的快捷键大家都非常easy找到.Alt+Enter.可是假设我要一次性import文件里全部的声明.这个快捷键是什么呢,找啊找的,就是没找到,曾经在Eclipse是Ctrl+ ...

  6. spool命令、创建一个表,创建而且copy表,查看别的用户下的表,rowid行地址 索引的时候使用,表的增删改查,删除表,oracle的回收站

      1.spool命令 spool "D:\test.txt" spool off SQL> host cls 2.创建一个表 SQL> --条件(1):有创建 ...

  7. Iframe简单介绍(一)

    Iframe可以用在以下几个场景 1.典型系统结构,左侧是功能树,右侧就是一些常见table或者表单之类的.为了每一个功能,单独分离出来,采用iframe. 2.AJAX上传文件 3.加载别的网站内容 ...

  8. vue路由

    vue-router 现在的应用都流行SPA应用(single page application) 传统的项目大多使用多页面结构,需要切换内容的时候我们往往会进行单个html文件的跳转,这个时候受网络 ...

  9. iOS tableView 数据处理,数据分类相同数据整合、合并计算总数总价

    // 数据下载得到数组数据 modelArray = [MZPriceModel mj_objectArrayWithKeyValuesArray:data[@"info"]]; ...

  10. 监听键盘弹起View上调

    可以用三方库IQKeyboardManager 用这个第三方 http://www.jianshu.com/p/f8157895 #pragma mark - keyboard events - // ...