什么是元类:

  python中类也是一种对象, 可以称为类对象.

  元类就是用来创建类对象的"东西". 你创建类就是为了创建类的实例对象, 不是吗? 但是我们已经学习了python中的类也是对象. 元类就是用来创建这些类对象的, 元类就是类的类, 你可以这样理解:

MyClass = MetaClass()
MyObject = MyClass()

你已经看到了type可以这样来动态的创建类:

MyClass = type("MyClass", (), {})

这是因为type实际上是一个元类. type就是python在背后用来创建所有类的元类. 那么现在你肯定特别想知道type既然是一个类(元类), 为什么首字母没有大写? 好吧, 我猜这是为了与int, str, function这些python内置数据类型保持一致, str是用来创建字符串对象的类, 而int是用来创建整数对象的类, type就是用来创建类对象的类. 你可以通过检查__class__属性来证明这一点. python中所有的东西都是对象(这一点和javascript相同), 这包括整数, 字符串, 函数以及类, 它们全部都是对象, 而且它们都是从一个类创建而来, 这个类就是type.

>>> age =
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
...
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
...
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

看一下上面这些任何一个__class__的__class__属性又是什么呢?

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
>>> Bar.__class__
<type 'type'>

从上面结果可以看出, python中的所有普通的类也是对象, 当然这些类对象也有类型. 可以看出, int, str, 函数, 普通自定义类, 这些类对象的类型都是type.

  元类就是创建类对象的类. 如果你喜欢, 可以把元类看做"类工厂". type就是python的内建元类, 当然也可以创建自己的元类.

__metaclass__属性:

  你可以在实现一个类的时候为其添加__metaclass__属性:

class Foo(object):
__metaclass__ = something
...

如果你这么做了, python就会用元类来创建类Foo. 当在内存中创建类对象Foo时, python会在类的定义中寻找__metaclass__属性, 如果找到了, python就用它来创建Foo, 如果没有找到, 就会用内建的type元类来创建这个类.

当你实现一个有继承的类时:

class Foo(Bar):
pass

python会做如下操作:

Foo中有__metaclass__这个属性吗? 如果是, python会在内存中通过__metaclass__创建一个名字为Foo的类对象. 如果python没有找到__metaclass__, 它会继续在Bar(父类)中寻找__metaclass__, 以此类推. 如果python在任何父类中都没有找到__metaclass__, 它就会在模块层次中寻找__metaclass__. 如果还是找不到__metaclass__, python就会用内置的type元类来创建这个类对象.

自定义元类:

  元类的主要目的是为了当创建类时能够自动的改变类, 或者说定制类. 为什么这么说呢, 我们可以这样理解: 把类的创建过成想象成一个管道, 如果遍历完继承链+模块层次都没有发现__metaclass__属性, 那么用type元类来创建该类. 这种情况下我们在类中怎样定义属性, 最终生成的类对象也会具有怎样的属性, 如下图所示:

类创建时的样子---(type)-->类最终的样子(由于是用type元类, 所以类最终样式与类创建时样式一样)

如果遍历过程中找到了__metaclass__属性, 那么就会用自定义元类来创建该类, 如下图所示:

类创建时的样子---(__metaclass__属性指向的元类)--->类最终的样子

由于在我们自定义的元类里面可以定制那些__metaclass__指过来的类, 因此元类的主要目的一般是为了当创建类时自动的改变类, 或者说定制类.

  到底什么时候才会用到元类呢? 通常你会为API做这样的事情, 你希望可以创建复合当前上下文的类. 假想一个很傻的例子, 你希望在你的模块里所有类的属性都应该是大写形式. 有好几种方法可以办到, 但其中一种就是通过在模块级别设定__metaclass__. 采用这种方法, 这个模块中的所有类都会通过这个元类来创建, 我们只需要告诉元类把所有的属性都改成大写形式就行:

# !/usr/bin/python
# -*- coding: utf-8 -*-
# type实际上是一个类, 就像str和int一样, 所以你可以从type继承
class UpperAttrMetaClass(type):
def __new__(cls, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
uppercase_attrs = dict((name.upper(), value) for name, value in attrs)
return type.__new__(cls, name, bases, uppercase_attrs) class Foo(object):
__metaclass__ = UpperAttrMetaClass
bar = 'bip' print hasattr(Foo, 'bar')
# 输出false
print hasattr(Foo, 'BAR')
# 输出true f = Foo()
print f.BAR
# 输出bip

也可以使用super方法:

class UpperAttrMetaClass(type):
def __new__(cls, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
uppercase_attrs = dict((name.upper(), value) for name, value in attrs)
#return type.__new__(cls, name, bases, uppercase_attrs)
return super(UpperAttrMetaClass, cls).__new__(cls, name, bases, uppercase_attrs)

就是这样, 除此之外关于元类真的没有别的可以说的了. 就元类本身而言, 它其实很简单:

1. 拦截类的创建

2. 修改类(或者说定制类)

3. 返回修改之后的类

究竟为什么要使用元类:

元类的主要作用是创建API. 一个典型的例子是Django ORM. 它允许你像这样定义类:

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

但是如果你像这样做的话:

guy = Person(name = "bob", age = "")
print guy.age

调用guy.age并不会返回一个IntegerField对象, 而是会返回一个int, 是指可以直接从数据库中取出数据. 这是有可能的, 因为models.Model定义了__metaclass__, 并且在元类中将你刚刚定义的简单的Person类给修改了, 变的能够访问数据库了. Django框架将这些看起来很复杂的东西通过暴露出一个简单的使用元类的API(models.Model)将其化简, 通过这个API重新创建代码, 在背后完成真正的工作.

结语:

  现在我们知道了, 类本身也是实例, 只不过它们是元类的实例. python中的一切都是对象, 这些对象要么是类对象的实例, 要么是元类的实例, 除了type. 元类是比较复杂的, 大多数情况下, 我们不会对类做修改. 如果需要对类做修改,  大多数情况下我们会通过下面两种技术来动态修改类:

1. Monkey patching

2. class decorators

python中的__metaclass__的更多相关文章

  1. python2.7高级编程 笔记二(Python中的描述符)

    Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些" ...

  2. python中的 descriptor

    学好和用好python, descriptor是必须跨越过去的一个点,现在虽然Python书籍花样百出,但是似乎都是在介绍一些Python库而已,对Python语言本身的关注很少,或者即使关注了,但是 ...

  3. python 中的metaclass和baseclasses

    提前说明: class object  指VM中的class 对象,因为python一切对象,class在VM也是一个对象,需要区分class对象和 class实例对象. class instance ...

  4. python中的metaclass

    首先看下面的代码: # coding: utf-8 class Test(object): pass print Test.__class__ # type print Test.__base__ # ...

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

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

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

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

  7. Python中metaclass解释

    Classes as objects 首先,在认识metaclass之前,你需要认识下python中的class.python中class的奇怪特性借鉴了smalltalk语言.大多数语言中,clas ...

  8. Python中的元类(metaclass)

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

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

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

随机推荐

  1. js实现表格无缝滚动效果

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  2. 一些常用的meta标签

    <!DOCTYPE html> <!-- 使用 HTML5 doctype,不区分大小写 --> <html lang="zh-cmn-Hans"&g ...

  3. C#基础学习第一天

    ..net与C# .NET是一个框架.一种平台.一种技术 C#是一种编程语言,可以开发基于.NET平台的应用 .NET能干什么 Winform ASP.NET Wwb wphone Unity3D游戏 ...

  4. 【腾讯云服务器】基于centos7搭建ftp服务器(vsftpd)

    该博客分为三部分设置,1.ftp服务器搭建.2.防火墙设置  3.腾讯云安全组 一.ftp服务器搭建 1.1  安装vsftpd yum install vsftpd -y 1.2 启动vsftpd服 ...

  5. AngularJS学习之旅—AngularJS SQL(十二)

    一.使用 PHP 从 MySQL 中获取数据 <div ng-app="myApp" ng-controller="customersCtrl"> ...

  6. Cs231n课堂内容记录-Lecture 8 深度学习框架

    Lecture 8  Deep Learning Software 课堂笔记参见:https://blog.csdn.net/u012554092/article/details/78159316 今 ...

  7. python selenium2 中的显示等待WebDriverWait与条件判断expected_conditions举例

    #coding=utf-8from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium. ...

  8. 明天研究下jpa直接像django一样生成

    https://blog.csdn.net/yztezhl/article/details/79390714 自动生成 教程-- https://blog.csdn.net/mxjesse/artic ...

  9. springboot + mybatis +pageHelper分页排序

    今天下午写查出来的数据的排序,原来的数据没有排序,现在把排序功能加上...原来用的,是xml中的sql动态传参 ,,1个小数没有弄出来,果断放弃... 网上百度一下,发现用pageHelper  可以 ...

  10. Vue 自定义一个插件的用法、小案例及在项目中的应用

    1.开发插件 install有两个参数,第一个是Vue构造器,第二个参数是一个可选的选项对象   MyPlugin.install = function (Vue, options) {   // 1 ...