metaclass小结

先前学习看到ORM的时候,需要用到metaclass相关的知识,于是,回过头来又去看关于metaclass的知识,看metaclass的时候,我又发现,一些和super相关的知识掌握的不是很透彻,于是又去复习了一下super相关的知识,看super的时候,又发现不了解Python的MRO,于是又去看MRO,MRO,super看完之后,又发现还需要看构造个性化的类相关的知识(__init__,与__new__),相关的都看完之后,终于又回到metaclass上来了。还好,看到了一篇翻译自stack overflow的文章,讲得特别好,无论是他书写的内容,还是他书写的方式,都非常的值得学习。

元类就是创建其他类的类。

作者先从类也是对象这个命题入手(相比于其他面向对象语言,这个概念扭转不过来,影响对一些Python语法的理解),它是一组描述如何生成一个对象的代码,它具有实例化一个对象的能力,不过,它本质上是还是一个对象,Python解释器执行完class这样的语句之后就在内存里面创建一个类的对象,它也可以执行像其他的普通对象一样的操作,比如,将它赋值给另一个变量(类似于取个别名),拷贝它,增加它的属性,把它作为参数进行传递。

介绍了类之后,紧接着引入另一个话题“如何动态的创建类”。class语法定义的类,属于静态定义的类,如何动态的,创建,如何使某些类,具有相似的特性,这都是metaclass可以做到的。作者举了两个不同级别“动态”类的例子,先是一个函数内定义多个类,利用传入的函数的字符串来决定返回类的例子,又举了用type创建类的例子,type(类名,父类的元组(可以为空),包含属性的字典)。

介绍了以上的基础知识后,作者终于切入到主题了,元类到底是什么:

元类就是创建类的类,type就是Python内建的元类,当然你也可以创建自己的元类。

先介绍__metaclass__属性

这个属性会影响类的创建,解释器会现在类体内,然后类的祖先类,模块去查找这个属性,找到就用它去创建类,找不到,就用type去创建。__metaclass__属性,为callable对象,函数或者类,但是他们必须返回一个类,

很显然函数的话,肯定要使用type()返回类,使用类的话肯定是元类,即type或者它的派生类。

一个把类属性名称全部统一成“大写”的例子

用函数实现__metaclass__属性:

def upper_attr(future_class_name, future_class_parents, future_class_attr):
    ‘‘‘返回一个类对象,将属性都转为大写形式‘‘‘
    #  选择所有不以‘__‘开头的属性
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith(‘__‘))
# 将它们转为大写形式
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)

    # 通过‘type‘来做类对象的创建
    return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr  #  这会作用到这个模块中的所有类

class Foo(object):
    # 我们也可以只在这里定义__metaclass__,这样就只会作用于这个类中
    bar = ‘bip‘
print hasattr(Foo, ‘bar‘)
# 输出: False
print hasattr(Foo, ‘BAR‘)
# 输出:True

f = Foo()
print f.BAR
# 输出:‘bip‘

一个自定义元类来实现:

演化之后所谓符合面向对象的版本:

class UpperAttrMetaclass(type):
    def __new__(cls, name, bases, dct):
        attrs = ((name, value) for name, value in dct.items() if not name.startswith(‘__‘))
        uppercase_attr = dict((name.upper(), value) for name, value in attrs)
        return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)

我有个疑问就是:修改后的字典重新作为参数,被传递到type的__new__()方法,在这个方法里面就把这个字典赋值到新创建的类对象里面了?这个修改在__new__()就能得到体现了?这个地方,暂时不明白__new__()与__init__如何合作的????

不用再调用type的__init__()?

一个小结论就是:定义calss的语句,定义的类只是一个半成品,Python允许程序员通过__metaclass__属性再来定义自己的构造类对象的规则,并且,解释器会把已经得到的“半成品”的三项重要信息传递给__metaclass__属性的callable对象,供他使用,使它生成新的type实例。

“。使用到元类的代码比较复杂,这背后的原因倒并不是因为元类本身,而是因为你通常会使用元类去做一些晦涩的事情,依赖于自省,控制继承等等。确实,用元类来搞些“黑暗魔法”是特别有用的,因而会搞出些复杂的东西来。但就元类本身而言,它们其实是很简单的:”

1)   拦截类的创建

2)   修改类

3)   返回修改之后的类

为什么要用metaclass类而不是函数?

由于__metaclass__可以接受任何可调用的对象,那为何还要使用类呢,因为很显然使用类会更加复杂啊?这里有好几个原因:

1)  意图会更加清晰。当你读到UpperAttrMetaclass(type)时,你知道接下来要发生什么。

2) 你可以使用OOP编程。元类可以从元类中继承而来,改写父类的方法。元类甚至还可以使用元类。

3)  你可以把代码组织的更好。当你使用元类的时候肯定不会是像我上面举的这种简单场景,通常都是针对比较复杂的问题。将多个方法归总到一个类中会很有帮助,也会使得代码更容易阅读。

4) 你可以使用__new__, __init__以及__call__这样的特殊方法。它们能帮你处理不同的任务。就算通常你可以把所有的东西都在__new__里处理掉,有些人还是觉得用__init__更舒服些。

5) 哇哦,这东西的名字是metaclass,肯定非善类,我要小心!

究竟为什么要使用元类?

现在回到我们的大主题上来,究竟是为什么你会去使用这样一种容易出错且晦涩的特性?好吧,一般来说,你根本就用不上它:

“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。”  —— Python界的领袖 Tim Peters

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

1

2

3

class Person(models.Model):

name = models.CharField(max_length=30)

age = models.IntegerField()

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

1

2

guy  = Person(name=‘bob‘, age=‘35‘)

print guy.age

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

结语

首先,你知道了类其实是能够创建出类实例的对象。好吧,事实上,类本身也是实例,当然,它们是元类的实例。

1

2

3

>>>class Foo(object): pass

>>> id(Foo)

142630324

Python中的一切都是对象,它们要么是类的实例,要么是元类的实例,除了type。type实际上是它自己的元类,在纯Python环境中这可不是你能够做到的,这是通过在实现层面耍一些小手段做到的。其次,元类是很复杂的。对于非常简单的类,你可能不希望通过使用元类来对类做修改。你可以通过其他两种技术来修改类:

1) Monkey patching

2)   class decorators

当你需要动态修改类时,99%的时间里你最好使用上面这两种技术。当然了,其实在99%的时间里你根本就不需要动态修改类

时间: 2024-10-20 18:17:37

metaclass小结的相关文章

Python-深入理解元类(metaclass)

1.使用 type 动态创建类(type 是一个类, 用来创建类对象的元类, 所以也可以继承) type("Person", (), {"name": "John"}) 2.元类 Python 中类也是对象, 元类就是创建这些类对象的类, 可以理解为 MyClass = MetaClass() MyObject = MyClass() 3.type实际上是一个元类, type就是Python在背后用来创建所有类的元类, 类似 str 是创建字符串

陌生的 metaclass(转)

add by zhj:这是我见过的对metaclass解释最清楚的文章了,例子很好,真是一例胜千言 原文:http://wiki.jikexueyuan.com/project/explore-python/Class/metaclass.html Python 中的元类(metaclass)是一个深度魔法,平时我们可能比较少接触到元类,本文将通过一些简单的例子来理解这个魔法. 类也是对象 在 Python 中,一切皆对象.字符串,列表,字典,函数是对象,类也是一个对象,因此你可以: 把类赋值给

14.6面向对象小结

面向对象小结 1.如何创建类class 类名:pass 2.创建方法构造方法,init(self,arg)obj = 类('a1')普通方法obj = 类('xxx')obj.普通方法名() 3.面向对象三大特性之一:封装 class Bar: def __init__(self, n,a): self.name = n self.age = a self.xue = 'o' b1 = Bar('a1', 123) b2 = Bar('a2', 456) 4.适用场景:如果多个函数中有一些相同参

面向对象进阶小结

面向对象进阶小结 一.面向对象进阶小结 面向对象最本质解决的问题就是:提供可扩展性 类与对象:程序中必须现有类,再有对象 类中有属性,有方法 绑定方法:定义在类内部,没有装饰器装饰的方法都是对象的绑定方法,需要对象来调用,对象调用的时候,会把自身传入 1.1 类的继承 继承父类,则会有父类的所有属性和方法 class ParentClass1(): pass class ParentClass2(): pass class SubClass(ParentClass1,ParentClass2):

使用Apache POI导出Excel小结--导出XLS格式文档

使用Apache POI导出Excel小结 关于使用Apache POI导出Excel我大概会分三篇文章去写 使用Apache POI导出Excel小结--导出XLS格式文档 使用Apache POI导出Excel小结--导出XLSX格式文档 使用Apache POI导出Excel--大数量导出 导出XLS格式文档 做企业应用项目难免会有数据导出到Excel的需求,最近在使用其,并对导出Excel封装成工具类开放出来供大家参考.关于Apache POI Excel基本的概念与操作我在这里就不啰嗦

【转载】小结一下linux 2.6内核的四种IO调度算法

在LINUX 2.6中,有四种关于IO的调度算法,下面综合小结一下: 1) NOOP NOOP算法的全写为No Operation.该算法实现了最最简单的FIFO队列,所有IO请求大致按照先来后到的顺序进行操作.之所以说“大致”,原因是NOOP在FIFO的基础上还做了相邻IO请求的合并,并不是完完全全按照先进先出的规则满足IO请求.NOOP假定I/O请求由驱动程序或者设备做了优化或者重排了顺序(就像一个智能控制器完成的工作那样).在有些SAN环境下,这个选择可能是最好选择.Noop 对于 IO

Android基础入门教程——8.1.3 Android中的13种Drawable小结 Part 3

Android基础入门教程--8.1.3 Android中的13种Drawable小结 Part 3 标签(空格分隔): Android基础入门教程 本节引言: 本节我们来把剩下的四种Drawable也学完,他们分别是: LayerDrawable,TransitionDrawable,LevelListDrawable和StateListDrawable, 依旧贴下13种Drawable的导图: 1.LayerDrawable 层图形对象,包含一个Drawable数组,然后按照数组对应的顺序来

Android基础入门教程——8.1.2 Android中的13种Drawable小结 Part 2

Android基础入门教程--8.1.2 Android中的13种Drawable小结 Part 2 标签(空格分隔): Android基础入门教程 本节引言: 本节我们继续来学习Android中的Drawable资源,上一节我们学习了: ColorDrawable:NinePatchDrawable: ShapeDrawable:GradientDrawable!这四个Drawable~ 而本节我们继续来学习接下来的五个Drawable,他们分别是: BitmapDrawable:Insert

安卓小结《1》

Activity的生命周期和启动模式的知识点小结: 1.如果Activity切换的时候,新Activity是透明,旧的不会走onStop方法. 2.新的Activity切换的时候,旧Activity  会先执行,onpause,然后才会启动新的activity. 3. Activity在异常情况下被回收时,onSaveInstanceState方法会被回调,回调时机是在onStop之前,当Activity被重新创建的时 候,onRestoreInstanceState方法会被回调,时序在onSt