Classes as objects

首先,在认识metaclass之前,你需要认识下python中的class。python中class的奇怪特性借鉴了smalltalk语言。大多数语言中,classes仅仅是用于描述怎样创建一个对象的代码端。在某种程度上说,python中的class也是这样的。

  1.   >>> class ObjectCreator(object):
  2. ... pass
  3. ...
  4.  
  5. >>> my_object = ObjectCreator()
  6. >>> print my_object
  7. <__main__.ObjectCreator object at 0x8974f2c>

但是,python中的classes同时还是objects,是的,看的没错,是objects,一旦你使用关键字class,python将执行并且生成一个对象(object),命令

  1. >>> class ObjectCreator(object):
  2. ... pass
  3. ...

将在内存中创建一个名字为ObjectCreator的对象。

这个对象(类)自己具有创建对象(实例)的能力,这也是为什么被称之为类

因为它是一个对象,所以它应该具有对象的一些特性:

  • 你可以把它assign给一个变量
  • 你可以copy它
  • 你可以给它添加属性
  • 你可以把它作为一个函数的参数

例如:

  1. >>> print ObjectCreator # you can print a class because it's an object
  2. <class '__main__.ObjectCreator'>
  3. >>> def echo(o):
  4. ... print o
  5. ...
  6. >>> echo(ObjectCreator) # you can pass a class as a parameter
  7. <class '__main__.ObjectCreator'>
  8. >>> print hasattr(ObjectCreator, 'new_attribute')
  9. False
  10. >>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
  11. >>> print hasattr(ObjectCreator, 'new_attribute')
  12. True
  13. >>> print ObjectCreator.new_attribute
  14. foo
  15. >>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
  16. >>> print ObjectCreatorMirror.new_attribute
  17. foo
  18. >>> print ObjectCreatorMirror()
  19. <__main__.ObjectCreator object at 0x8997b4c>

Creating classes dynamically

因为classes是对象,所以你可以想对象一样动态的创建他们。

首先,你可以在一个函数中使用关键字创建它:

  1. >>> def choose_class(name):
  2. ... if name == 'foo':
  3. ... class Foo(object):
  4. ... pass
  5. ... return Foo # return the class, not an instance
  6. ... else:
  7. ... class Bar(object):
  8. ... pass
  9. ... return Bar
  10. ...
  11. >>> MyClass = choose_class('foo')
  12. >>> print MyClass # the function returns a class, not an instance
  13. <class '__main__.Foo'>
  14. >>> print MyClass() # you can create an object from this class
  15. <__main__.Foo object at 0x89c6d4c>

但是,这个不是很动态,因为你还是要自己写全整个函数。因为类是对象,所以它们可以被生成。当你使用关键字class的时候,python自动的创建这些对象,所以就像python中大部分事情一样,我们同样能够手动的生成。

还记得函数type吗?就是那个能告诉你对象类型的函数:

  1. >>> print type(1)
  2. <type 'int'>
  3. >>> print type("1")
  4. <type 'str'>
  5. >>> print type(ObjectCreator)
  6. <type 'type'>
  7. >>> print type(ObjectCreator())
  8. <class '__main__.ObjectCreator'>

令人惊讶的是type同样具有一种完全不同的能力,就是我们需要的动态创建函数。type可以将类的描述作为参数,然后生成一个类。type是这样工作的:

  1. type(name of the class,
  2. tuple of the parent class (for inheritance, can be empty),
  3. dictionary containing attributes names and values)

例如:

  1. >>> class MyShinyClass(object):
  2. ... pass

可以利用以下方式手动创建:

  1. >>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
  2. >>> print MyShinyClass
  3. <class '__main__.MyShinyClass'>
  4. >>> print MyShinyClass() # create an instance with the class
  5. <__main__.MyShinyClass object at 0x8997cec>

可以看出,我们以MyShinyClass作为class的name, 函数很明显了,只不过是些不同的参数变化,没有理由去复杂化,用原文作者的话说:They can be different,but there is no reason to complicate things。

What are metaclasses (finally)

终于要开是解释metaclasses了,我也等了好久,Metaclasses are the 'stuff' that creates classes. 这个是文章的作者说的,就是说它是个可以创建类的东东,你定义类是为了创建对象是吗?但是在python中我们视类为对象,so,metaclasses就是来创建对象的。它们是类的类,你可以这样想象它们:

  1. MyClass = MetaClass()
  2. MyObject = MyClass()

你可以看出type让你做了同样的事情:

  1. MyClass = type('MyClass', (), {})

这是因为函数type事实上是一个metaclass,在python中,type是一个metaclass用于在后台创建类。现在你知道为什么它为什么用的是小写,而不是大写的Type?
well, 你是不是想到了str/int等函数呢,str用于创建字符串对象,int创建整型对象,那type只不过是创建类对象的类而已。你可以通过查看__class__属性看到。所有的一切都是对象,所以的对象都可以通过一个函数创建:

  1. >>> age = 35
  2. >>> age.__class__
  3. <type 'int'>
  4. >>> name = 'bob'
  5. >>> name.__class__
  6. <type 'str'>
  7. >>> def foo(): pass
  8. >>> foo.__class__
  9. <type 'function'>
  10. >>> class Bar(object): pass
  11. >>> b = Bar()
  12. >>> b.__class__
  13. <class '__main__.Bar'>

那创建的class的创建者是谁呢?

  1. >>> age.__class__.__class__
  2. <type 'type'>
  3. >>> name.__class__.__class__
  4. <type 'type'>
  5. >>> foo.__class__.__class__
  6. <type 'type'>
  7. >>> b.__class__.__class__
  8. <type 'type'>

这下应该稍微明白了些吧,当然,我们也可以创建我们自己的metaclass。

The __metaclass__ attribute

你可以在创建类的时候添加__metaclass__属性:

  1. class Foo(object):
  2. __metaclass__ = something...
  3. [...]

python将使用metaclass去创建类Foo,要小心哦,你写了class Foo(object),但是Foo还没有在内存中创建,python会在你的类中寻找__metaclass__如果找到了就用它创建,如果没有找到,它将使用type去创建类。

详细点说吧,当你定义类时:

  1. class Foo(object):
  2. pass

python将首先查看在类定义中有没有__metaclass__属性,没有将在parent中找,没有去module级别找,如果还没有找到,最后的杀招type。

好,最后的问题是,我们该在__metaclass__中放些什么东东呢?

Custom metaclasses

一个metaclass的主要目的是在类创建的时候自动的去改变它,读起来有点拗。一般来说,当你写些APIs,而且这些APIs要满足当前的上下文的时候,可以考虑使用metaclass。想象一下,当你的module中的类需要将它们的属性写为小写的时候,我们就可以试试在moudle级别metaclass,这时,module中的所有类都将由metaclass创建,我们所要做的是告诉metaclass将所有的属性转化为小写。幸运的是,我们不一定非要将__metaclass__定义为一个类:

  1. # the metaclass will automatically get passed the same argument
  2. # that you usually pass to `type`
  3. def upper_attr(future_class_name, future_class_parents, future_class_attr):
  4. """
  5. Return a class object, with the list of its attribute turned
  6. into uppercase.
  7. """
  8.  
  9. # pick up any attribute that doesn't start with '__' and uppercase it
  10. uppercase_attr = {}
  11. for name, val in future_class_attr.items():
  12. if not name.startswith('__'):
  13. uppercase_attr[name.upper()] = val
  14. else:
  15. uppercase_attr[name] = val
  16.  
  17. # let `type` do the class creation
  18. return type(future_class_name, future_class_parents, uppercase_attr)
  19.  
  20. __metaclass__ = upper_attr # this will affect all classes in the module
  21.  
  22. class Foo(): # global __metaclass__ won't work with "object" though
  23. # but we can define __metaclass__ here instead to affect only this class
  24. # and this will work with "object" children
  25. bar = 'bip'
  26.  
  27. print hasattr(Foo, 'bar')
  28. # Out: False
  29. print hasattr(Foo, 'BAR')
  30. # Out: True
  31.  
  32. f = Foo()
  33. print f.BAR
  34. # Out: 'bip'

现在,我们用一个类来实现metaclass:

  1. # remember that `type` is actually a class like `str` and `int`
  2. # so you can inherit from it
  3. class UpperAttrMetaclass(type):
  4. # __new__ is the method called before __init__
  5. # it's the method that creates the object and returns it
  6. # while __init__ just initializes the object passed as parameter
  7. # you rarely use __new__, except when you want to control how the object
  8. # is created.
  9. # here the created object is the class, and we want to customize it
  10. # so we override __new__
  11. # you can do some stuff in __init__ too if you wish
  12. # some advanced use involves overriding __call__ as well, but we won't
  13. # see this
  14. def __new__(upperattr_metaclass, future_class_name,
  15. future_class_parents, future_class_attr):
  16.  
  17. uppercase_attr = {}
  18. for name, val in future_class_attr.items():
  19. if not name.startswith('__'):
  20. uppercase_attr[name.upper()] = val
  21. else:
  22. uppercase_attr[name] = val
  23.  
  24. return type(future_class_name, future_class_parents, uppercase_attr)

但是这个不是真正的OOP。我们可以直接调用type的__new__:

  1. class UpperAttrMetaclass(type):
  2.  
  3. def __new__(upperattr_metaclass, future_class_name,
  4. future_class_parents, future_class_attr):
  5.  
  6. uppercase_attr = {}
  7. for name, val in future_class_attr.items():
  8. if not name.startswith('__'):
  9. uppercase_attr[name.upper()] = val
  10. else:
  11. uppercase_attr[name] = val
  12.  
  13. # reuse the type.__new__ method
  14. # this is basic OOP, nothing magic in there
  15. return type.__new__(upperattr_metaclass, future_class_name,
  16. future_class_parents, uppercase_attr)

你可能已经注意到了额外的参数upperattr_metaclass,这个没有什么特别:一个方法常常将当前的实例作为首个参数,就像平常的方法中的self。当然,我这里用过长的名字视为了更清晰的解释,就像self一样,所有的参数有惯例性的名字,所以一下是个产品性的metaclass:

  1. class UpperAttrMetaclass(type):
  2.  
  3. def __new__(cls, clsname, bases, dct):
  4.  
  5. uppercase_attr = {}
  6. for name, val in dct.items():
  7. if not name.startswith('__'):
  8. uppercase_attr[name.upper()] = val
  9. else:
  10. uppercase_attr[name] = val
  11.  
  12. return type.__new__(cls, clsname, bases, uppercase_attr)

我们可以使用super让代码更干净:

  1. class UpperAttrMetaclass(type):
  2.  
  3. def __new__(cls, clsname, bases, dct):
  4.  
  5. uppercase_attr = {}
  6. for name, val in dct.items():
  7. if not name.startswith('__'):
  8. uppercase_attr[name.upper()] = val
  9. else:
  10. uppercase_attr[name] = val
  11.  
  12. return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

就这么多,metaclass就是这么简单。其实,使用metaclass的代码复杂的原因不是因为使用了metaclass,而是因为你常常使用metaclass去做些需要introspection,操作继承/变量比如__dict__等。事实上,metaclass也确实能做些复杂的事。但是,以下是不复杂的:

  • 理解一个类的创建
  • 改变类
  • 返回一个修改的类

Why would you use metaclasses classes instead of functions?

还是坚持写完吧!因为__metaclass__能接受任意的调用,那为什么要选用看起来比较复杂的类呢?有一下几个原因:

  • 兴建类的目的比较清晰,参考上面的UpperAttrMetaclass(type)
  • 你可以使用OOP。Metaclass能继承自metaclass,覆盖父类方法
  • 你可以更好的组织你的代码结构。make code easy to read
  • 你可以使用__new__, __init__, __call__
  • 都将做了metaclass,总要作点事吧!(这个比较牵强(:-)

Why the hell would you use metaclasses?

python guru写的解释道行太浅看不懂,不翻译了,不过他说了个例子,就是Django中的ORM模型使用了,例如我们可以定义数据模型如下:

  1. class Person(models.Model):
  2. name = models.CharField(max_length=30)
  3. age = models.IntegerField()

但是你这样调用的时候:

  1. guy = Person(name='bob', age='35')
  2. print guy.age

它不会返回IntegerField对象。它将返回int,而且能从数据库中取值。

有一种解释是models.Model定义了__metaclass__,而且将你定义的简单的Person转化为复杂的链接到数据库字段。

Django通过expose 一个简单的API,将许多复杂的事情让我们看起来很简单,而且通过metaclass,在后台利用API重新生成code。

The last word

最后,作者提到了type的exception,还有class alterations:意思就是说我们大部分时间是用不到class alteration的,用到的时候呢,可以使用monkey patching和class decorators来实现,所以留给metaclass的空间很小了。

全剧终!!!

文章的详情清查看:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

偶尔翻看stackoverflow的python帖子,受益匪浅,今天看到metaclass讲的太好了,就试着翻译出来试试,欢迎大家互相交流!

Python中metaclass解释的更多相关文章

  1. python中Metaclass的理解

    今天在学习<python3爬虫开发实战>中看到这样一段代码3 class ProxyMetaclass(type): def __new__(cls, name, bases, attrs ...

  2. python中MetaClass的一些用法

    元类在很多编程语言中都有这样的概念,我们都知道,类可以创建对象,类本身也是对象,既然是对象,那么它肯定也是被创造出来的,元类就专门用来创造类对象,于是,这就给我们提供了一种操纵或者监听类的能力. 平时 ...

  3. python中metaclass的工作原理

    class TMetaclass(type): def __new__(cls, name, bases, attrs): print(cls, name, bases, attrs) return ...

  4. pandas 中有关isin()函数的介绍,python中del解释

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

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

  6. Python中dunder名称的来历

    版权声明:博客为作者原创,允许转载,但必须注明原文地址:https://www.cnblogs.com/byronxie/p/10741084.html 在 Python 中,我们经常会看到被双下划线 ...

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

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

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

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

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

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

随机推荐

  1. Oracle Trunc

    http://www.cnblogs.com/xiaoyudz/archive/2011/03/18/1988467.html

  2. LeetCode_Longest Palindromic Substring

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  3. Qt的十六进制的控件

    Qt没有这样的Widget,自己写一个吧.我曾经用MFC写过一个,代码不多,不到2000行,估计用Qt写不到1000行就够了. 可以参考这个qhexedit2 - QHexEdit is a Bina ...

  4. SQL Server 数据库定时自动备份【转】

    在SQL Server中出于数据安全的考虑,所以需要定期的备份数据库.而备份数据库一般又是在凌晨时间基本没有数据库操作的时候进行,所以我们不可能要求管理员每天守到晚上1点去备份数据库.要实现数据库的定 ...

  5. 开源 java CMS - FreeCMS1.9公布。

    FreeCMS商业版V1.9更新功能 添加Oracle数据库支持.

  6. MonoDevelop with Visual Studio to Linux and Mac OSX maintaining a single code base for all platforms.

    Home | Screenshots | Download | Contact | FAQ | Documentation | Development | Search   MonoDevelop i ...

  7. for循环、穷举法和迭代

    循环:初始条件,循环条件,状态改变,循环体.for(初始条件;循环条件;状态改变){ 循环体}for(int i=1;i<=10;i++){ }例子:100以内与7有关的数.求100以内所有数的 ...

  8. 关于使用Html5 canvas、 map、jquery构造不规则变色点击区域 热点区域

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. linq读书笔记3-操作符之select与selectmany

    linq对数据的查询方式的表达形式主要有两种: var demo =from p in pList where p.id=*** select p; var demo =pList.where(p=& ...

  10. Spring整合Hibernate 一 - 注入SessionFactory

    Spring3 整合 Hibernate4 - 注入SessionFactory 版本: spring-framework-3.2.4.RELEASE hibernate-release-4.2.5. ...