什么是元类:

  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. python 爬取全本免费小说网的小说

    这几天朋友说想看电子书,但是只能在网上看,不能下载到本地后看,问我有啥办法?我找了好几个小说网址看了下,你只能直接在网上看,要下载txt要冲钱买会员,而且还不能在浏览器上直接复制粘贴.之后我就想到py ...

  2. DBA思考系列——凛冬将至,丧钟为谁而鸣!

    诸多迹象昭示着凛冬将至,大萧条终于正式在全国各地拉开了序幕,很多80后的国人没有经历过苦日子,也没有经历过真正的金融危机.这场经济危机必将摧毁一些无视经济能力,盲目购房,盲目消费的家庭或个人.个人对经 ...

  3. 关于.net中的DataSet和DataTable

    DataSet ds = new DataSet(); ds.Tables.Add(); ds.Tables[].Columns.Add("name"); ds.Tables[]. ...

  4. 高德地图 Service 创建服务 USERKEY_PLAT_NOMATCH

    在使用高的地图 创建服务的时候 { "errmsg": "USERKEY_PLAT_NOMATCH", "errcode": 10009, ...

  5. Unity ECS 视频笔记

    视频摘要 本文视频资料:使用Entity Component System开发<快乐的Minecraft>游戏 使用Unity2018及以上版本才有ECS功能. 本文是看视频的一些摘要. ...

  6. PySocks安装使用方法

    PySocks是一个基于Python的SOCKS代理客户端,它是SocksiPy的一个分支,修改了一些bug和增加了一些额外功能. ---------------------------------- ...

  7. windows service承载的web api宿主搭建(Microsoft.Owin+service)

    今天突然想起改良一下以前搭建的“windows service承载的web api”服务,以前也是直接引用的类库,没有使用nuget包,时隔几年应该很旧版本了吧.所以本次把需要nuget获取的包记录一 ...

  8. iOS WebView 加载本地资源(图片,文件等)

    https://www.cnblogs.com/dhui69/p/5596917.html iOS WebView 加载本地资源(图片,文件等) NSString *path = [[NSBundle ...

  9. MINIST深度学习识别:python全连接神经网络和pytorch LeNet CNN网络训练实现及比较(三)

    版权声明:本文为博主原创文章,欢迎转载,并请注明出处.联系方式:460356155@qq.com 在前两篇文章MINIST深度学习识别:python全连接神经网络和pytorch LeNet CNN网 ...

  10. Node之安装篇

    本篇主要介绍node的安装与相关配置 官网: https://nodejs.org/en/ Linux: Windows: