从前面"Python对象"文章中了解到,在Python中一切都是对象,类可以创建实例对象,但是类本身也是对象。

  1. class C(object):
  2. pass
  3.  
  4. c = C()
  5. print c.__class__
  6. print C.__class__

代码中,通过"__class__"属性来查看对象的类型,对于类C对象本身,它的类型是type。

由于类也是对象,所以就可以在运行时动态的创建类,那么这时候就要用到内建函数type了。

再看type

从前面的文章了解到,可以通过内建函数type来获取对象的类型。

  1. class C(object):
  2. pass
  3.  
  4. c = C()
  5. print c.__class__ is type(c)
  6. print C.__class__ is type(C)

这里,我们就看看内建函数type的另一个强大的功能,动态的创建类。当使用type创建类的时候,有以下形式:

  1. type(类名, 父类的元组(可以为空), 属性的字典)
  • 要创建的class的名字
  • 父类集合,如果只有一个父类,别忘了tuple的单元素写法
  • class的属性字典

看看type创建类的例子:

  1. def printInfo(self):
  2. print "%s is %d years old" %(self.name, self.age)
  3.  
  4. S = type("Student", (object, ), {"name": "Wilber", "age": 28, "printStudentInfo": printInfo})
  5.  
  6. print type(S)
  7. s = S()
  8. print type(s)
  9. s.printStudentInfo()

例子中,通过type动态的创建了一个Studnent类,并且通过这个类可以创建实例:

__metaclass__

函数type实际上是一个元类,元类就是用来创建类的"模板"。我们可以通过类"模板"创建实例对象,同样,也可以使用元类"模板"来创建类对象;也就是说,元类就是类的类。

在创建一个类的时候,可以设置"__metaclass__"属性来指定元类。

"__metaclass__"属性对应的代码就是创建类的代码(这段代码可以是一个函数,也可以是一个类);如果这段代码是类,"__metaclass__"的类名总是以Metaclass结尾,以便清楚地表示这是一个元类。

对于元类的查找,Python有一套规则:

  1. Python解释器会在当前类中查找"__metaclass__"属性对于的代码,然后创建一个类对象
  2. 如果没有找到"__metaclass__"属性,会继续在父类中寻找"__metaclass__属性",并尝试前面同样的操作
  3. 如果在任何父类中都找不到"__metaclass__",就会用内置的type来创建这个类对象
  1. def queueMeta(name, bases, attrs):
  2. attrs['InQueue'] = lambda self, value: self.append(value)
  3.  
  4. def deQueue(self):
  5. if len(self) > 0:
  6. return self.pop(0)
  7. attrs['DeQueue'] = deQueue
  8.  
  9. # 直接调用type内建函数
  10. return type(name, bases, attrs)
  11.  
  12. # 元类从`type`类型派生
  13. class QueueMetaclass(type):
  14. def __new__(cls, name, bases, attrs):
  15. attrs['InQueue'] = lambda self, value: self.append(value)
  16.  
  17. def deQueue(self):
  18. if len(self) > 0:
  19. return self.pop(0)
  20. attrs['DeQueue'] = deQueue
  21.  
  22. # 直接调用type内建函数
  23. # return type(name, bases, attrs)
  24.  
  25. # 通过父类的__new__方法
  26. return type.__new__(cls, name, bases, attrs)
  27.  
  28. class MyQueue(list):
  29. # 设置metaclass属性,可以使用一个函数,也可以使用一个类,只要是可以创建类的代码
  30. #__metaclass__ = queueMeta
  31. __metaclass__ = QueueMetaclass
  32. pass
  33.  
  34. q = MyQueue("hello World")
  35. print q
  36. q.InQueue("!")
  37. print q
  38. q.DeQueue()
  39. print q

代码中的MyQueue类型继承自list,但是通过设置"__metaclass__"属性,可以修改创建的类。也就是说,元类做了下面的事情:

  1. 拦截类的创建
  2. 根据"__metaclass__"对应的代码修改类
  3. 返回修改之后的类

元类的__init__和__new__

当创建元类的时候,为了定制创建出来的类的特性,一般会实现元类的"__init__"和"__new__"方法。

  1. class MyMetaclass(type):
  2. def __new__(meta, name, bases, attrs):
  3. print '-----------------------------------'
  4. print "Allocating memory for class", name
  5. print meta
  6. print bases
  7. print attrs
  8. return super(MyMetaclass, meta).__new__(meta, name, bases, attrs)
  9.  
  10. def __init__(cls, name, bases, attrs):
  11. print '-----------------------------------'
  12. print "Initializing class", name
  13. print cls
  14. print bases
  15. print attrs
  16. super(MyMetaclass, cls).__init__(name, bases, attrs)
  17.  
  18. class MyClass(object):
  19. __metaclass__ = MyMetaclass
  20.  
  21. def foo(self, param):
  22. pass
  23.  
  24. barattr = 2

通过这个例子演示了使用元类的"__init__"和"__new__"方法:

元类的__call__

"__call__"是另外一个经常在实现元类时被重写的方法,与"__init__"和"__new__"不同的是,当调用"__call__"的时候,类已经被创建出来了,"__call__"是作用在类创建的实例过程。

看下面的代码:

  1. class MyMetaclass(type):
  2. def __call__(cls, *args, **kwds):
  3. print '__call__ of ', str(cls)
  4. print '__call__ *args=', str(args)
  5. return type.__call__(cls, *args, **kwds)
  6.  
  7. class MyClass(object):
  8. __metaclass__ = MyMetaclass
  9.  
  10. def __init__(self, a, b):
  11. print 'MyClass object with a=%s, b=%s' % (a, b)
  12.  
  13. print 'gonna create foo now...'
  14. foo = MyClass(1, 2)

代码的输出为:

元类使用举例

前面已经介绍了很多关于元类的知识了,下面看看怎么实际使用元类。

元类在ORM中是比较常用的,因为需要在运行时创建类型,看下面简单的例子:

  1. class Field(object):
  2. def __init__(self, fname, ftype):
  3. self.fname = fname
  4. self.ftype = ftype
  5. def __str__(self):
  6. return '{%s: (%s, %s)}' % (self.__class__.__name__, self.fname, self.ftype)
  7.  
  8. class StringField(Field):
  9. def __init__(self, fname):
  10. super(StringField, self).__init__(fname, 'varchar(100)')
  11.  
  12. class IntegerField(Field):
  13. def __init__(self, fname):
  14. super(IntegerField, self).__init__(fname, 'bigint')
  15.  
  16. class ModelMetaclass(type):
  17. def __new__(cls, name, bases, attrs):
  18. if name == "Model":
  19. return super(ModelMetaclass, cls).__new__(cls, name, bases, attrs)
  20. else:
  21. mapping = {}
  22. print "Create Model for:", name
  23. for k, v in attrs.items():
  24. if isinstance(v, Field):
  25. print "mapping %s with %s" %(k, v)
  26. mapping[k] = v
  27. attrs['_table'] = name
  28. attrs['_mapping'] = mapping
  29. return type.__new__(cls, name, bases, attrs)
  30.  
  31. class Model(dict):
  32. __metaclass__ = ModelMetaclass
  33.  
  34. def __init__(self, **kwargs):
  35. for key in kwargs.keys():
  36. if key not in self.__class__._mapping.keys():
  37. print "Key '%s' is not defined for %s" %(key, self.__class__.__name__)
  38. return
  39. super(Model, self).__init__(**kwargs)
  40.  
  41. def save(self):
  42. fields = []
  43. params = []
  44. args = []
  45. for k, v in self.__class__._mapping.items():
  46. fields.append(k)
  47. params.append("'{0}'".format(self[k]))
  48. sql = 'insert into %s (%s) values (%s)' % (self.__class__._table, ','.join(fields), ','.join(params))
  49. print 'SQL: %s' %sql
  50.  
  51. class Student(Model):
  52. id = IntegerField('id_c')
  53. name = StringField('username_c')
  54. email = StringField('email_c')
  55.  
  56. print "-------------------------------------------------"
  57. print Student._table
  58. print Student._mapping
  59. print "-------------------------------------------------"
  60. s1 = Student(id = 1, name = "Wilber", email = "wilber@sh.com")
  61. s1.save()
  62. print "-------------------------------------------------"
  63. s2 = Student(id = 1, name = "Wilber", gender = "male")

代码中通过元类创建Student类,并将类的属性与数据表关联起来:

总结

本文介绍了Python中元类的概念,通过元类可以在运行时创建类。

当用户定义一个类class的时候,Python解释器就会在当前类中查找"__metaclass__"属性,如果找到,就通过该属性对应的代码创建类;如果没有找到,就继续以相同的规则查找父类。如果在任何父类中都找不到"__metaclass__",就会用内置的type来创建类对象。

Python中的元类的更多相关文章

  1. Python中的元类(metaclass)

    推荐+收藏:深刻理解Python中的元类(metaclass) 做一些笔记学习学习: 在大多数编程语言中,类就是用来描述如何生成一个对象的代码段,在Python中类也是一个对象,这个(类)对象自身拥有 ...

  2. [转]深刻理解Python中的元类(metaclass)以及元类实现单例模式

    使用元类 深刻理解Python中的元类(metaclass)以及元类实现单例模式 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例 ...

  3. 深刻理解Python中的元类metaclass(转)

    本文由 伯乐在线 - bigship 翻译 英文出处:stackoverflow 译文:http://blog.jobbole.com/21351/ 译注:这是一篇在Stack overflow上很热 ...

  4. 深刻理解Python中的元类(metaclass)

    译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得 ...

  5. [转] 深刻理解Python中的元类(metaclass)

    非常详细的一篇深入讲解Python中metaclass的文章,感谢伯乐在线-bigship翻译及作者,转载收藏. 本文由 伯乐在线 - bigship 翻译.未经许可,禁止转载!英文出处:stacko ...

  6. 深刻理解Python中的元类(metaclass)【转】

    译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得 ...

  7. 深刻理解Python中的元类(metaclass)以及元类实现单例模式

    在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍 ...

  8. Python中的元类(译)

    add by zhj: 这是大stackoverflow上一位小白提出的问题,好吧,我承认我也是小白,元类这块我也是好多次想搞明白, 但终究因为太难懂而败下阵来.看了这篇文章明白了许多,再加下啄木鸟社 ...

  9. 深入理解Python中的元类(metaclass)

    原文 译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍 ...

随机推荐

  1. 自学Zabbix2.4-web页面配置zabbix

    点击返回:自学Zabbix之路 ....

  2. BZOJ 5308 [ZJOI2018] Day2T2 胖 | 二分 ST表

    题目链接 LOJ 2529 BZOJ 5308 题解 这么简单的题 为什么考场上我完全想不清楚 = = 对于k个关键点中的每一个关键点\(a\),二分它能一度成为哪些点的最短路起点(显然这些点在一段包 ...

  3. 【AGC016E】Poor Turkeys

    Description 有\(n\)(\(1 \le n \le 400\))只鸡,接下来按顺序进行\(m\)(\(1 \le m \le 10^5\))次操作.每次操作涉及两只鸡,如果都存在则随意拿 ...

  4. 洛谷 P3253 [JLOI2013]删除物品 解题报告

    P3253 [JLOI2013]删除物品 题目描述 箱子再分配问题需要解决如下问题: (1)一共有\(N\)个物品,堆成\(M\)堆. (2)所有物品都是一样的,但是它们有不同的优先级. (3)你只能 ...

  5. AAD Service Principal获取azure user list (Microsoft Graph API)

    本段代码是个通用性很强的sample code,不仅能够操作AAD本身,也能通过Azure Service Principal的授权来访问和控制Azure的订阅资源.(Azure某种程度上能看成是两个 ...

  6. 深入理解JVM结构

    JVM结构探究---- 1.JVM结构示意图 2.JVM运行时数据区 1)程序计数器(Program Counter Register) 程序计数器是用于存储每个线程下一步将执行的JVM指令,如该方法 ...

  7. Mac上配置idea的项目上传到GitHub

    1.安装git,Mac默认已经安装了Git,可以通过命令git —version查询一下. 2.创建SSH KEY(如果已经创建过,则不用再次创建.查看~/.ssh/id_rsa.pub是否存在) 生 ...

  8. 收藏:FLASH中键检测与右键屏蔽

    原文:http://space.flash8.net/space/?591172/viewspace-708726.html <!DOCTYPE html PUBLIC "-//W3C ...

  9. IntelliJ IDEA工具的安装使用

    一:解压,到目录E:\IDEA\bin下,本机是64位,就点击idea64.exe,如下: 二:注册码获取地址:http://idea.lanyus.com/.如图: 将此注册码复制到上图中去. 三: ...

  10. ffmpeg基本用法

    FFmpeg FFmpeg 基本用法 本课要解决的问题 1.FFmpeg的转码流程是什么? 2.常见的视频格式包含哪些内容吗? 3.如何把这些内容从视频文件中抽取出来? 4.如何从一种格式转换为另一种 ...