http://cizixs.com/2015/08/30/metaclass-in-python

动态类型也是类型

python 是一种动态类型语言,换句话说每个变量可以在程序里任何地方改变它的类型。想要获取变量的类型信息,可以使用 type

>>> a = 2

>>> type(a)
int >>> a = '1' >>> type(a)
str >>> type(str)
type >>> type(type)
type >>> class Test1 (object):
pass >>> class Test2 (Test1):
pass >>> a = Test1()
>>> b = Test2() >>> type(a) is Test1
True >>> type(b) is Test2
True

从上面的例子可以得到下面的结论:

  1. 每个变量都有自己的类型
  2. 变量的类型是可以随时改变的
  3. type 只会返回对象的直接 type,就是定义该对象的类
  4. 类的 type 还是 type,这说明 type 定义了 python 中所有类的某些基本属性

type 的第二种用法

上面提到了 type 的一种用法,查看 python 对象的类型。type 的另外一个用法就比较独特:生成类。

Help on class type in module __builtin__:

class type(object)
| type(object) -> the object's type
| type(name, bases, dict) -> a new type

来我们看一下 type 到底怎么用! 一般情况下,类的定义式这样的:

class Foo(object):
"""A class that does nothing."""
def __init__(self):
self.a = 1 def magic(self):
return self.a

使用 type 也可以做到同样的效果:

def __init__(self):
self.a = 1 def magic(self):
return self.a Foo = type('Foo', (object,), {"__doc__": "A class that does nothing.", "__init__": __init__, "magic": magic}) foo = Foo()
print foo
print foo.a # 1
print foo.magic # <bound method Foo.magic of <__main__.Foo object at 0x100fa5d50>>
print foo.magic() # 1

type 的三个参数分别是:

  1. name: 要生产的类名
  2. bases:包含所有基类的 tuple
  3. dict:类的所有属性,是键值对的字典

现在再回顾一下 “python 中一切皆对象”这句话,可能会更容易理解。

metaclass 就是类的类

我们在前面看到怎么使用 type 来动态创建类,其实在 python 内部也进行着同样的步骤。这就是 metaclass 的概念!

想弄明白 metaclass,我们要搞清楚 class。因为类似于 class 定义了 instance 的行为,metaclass 则定义了 class 的行为。可以说,class 是 metaclass 的 instance。

instance 创建的时候会调用 __init__ 函数,类似的,class 创建的时候会调用 __new__函数。通过自定义 __new__ 函数,你可以在创建类的时候做些额外的事情:把这个类注册到某个地方作为记录或者后续的查询,给类注入一些属性,或者干脆替换成另外一个类。

当遇到类定义的时候,

class MyClass(object):
pass

不会立即去创建这个类,而是把这段代码当做正常的 code block 来执行,结果就是生成一个命名空间(namespace),就是包含了要生成类(class-to-be)所有属性的字典,然后才会调用 __new__ 函数,把类名、类的父类、类的属性传递过去,生成这个类。

看到没有,上面一段话是不是很熟悉,没错,和我们使用 type 来创建类是一样的。

总结一下:metaclass 的功能是什么?根据类的信息,来创建这个类。没错,type 就是 python 自带的 metaclass。

创建自己的 metaclass

既然可以使用 type 来创建 python 标准的类,自然而然地,继承 type 这个类,覆盖已有的内置函数,就可以创建自己的 metaclsss。

下面的内容都会基于一个这样的并metaclass:它为要创建的类自动添加一个属性__cizixs。这个 metalcss 没有任何实际的意义,但可以帮助理解 metaclss 。

根据上面的思路,我们可以写出这样的代码:

class MyMetaclass(type):
def __init__(cls, name, bases, attrs):
cls.__cizixs = "Don't panic"
print("In MyMetaclass for {}".format(name))
super(MyMetaClass, cls).__init__(name, bases, attrs)

其实还可以覆写 __new__ 函数来达到相同的目的:

class MyMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['__cizixs'] = "Don't panic"
print("In MyMetaclass for {}".format(name))
return super(MyMetaclss, cls).__new__(cls, name, bases, attrs) class Foo(object):
__metaclass__ = MyMetaclass pass # In MyMetaclass for Foo foo = Foo()
print foo.__cizixs # Don't panic

关于 __new__ 和 __init__ 的不同,就不在此讲述,感兴趣可以自行搜索。只想提一句,__new__ 创建类,__init__ 初始化类。所以 __new__ 会在 __init__ 之前被调用,而且 __new__ 必须要返回新创建的类,__init__ 不需要。

上面这段代码需要说明的几点:

  • __new__ 函数就是类创建的时候可以使用的
  • 注意 __new__ 是一个 classmethod,代码中参数 cls 在这里就是<class '__main__.MyMetaclass'>
  • 只要在类的定义里指定 __metaclss__,那么这个类在创建的时候就会自动使用自定义的 metaclass,而不是系统默认的 type
  • 在创建类的时候,python 会按照这样的顺序去找要使用的 metaclass:
    • 类定义时候的类变量 __metaclass__
    • 类所在MODULE(就是当前文件)里的 __metaclss__ 变量
    • 第一个父类定义的 __metaclas__ 变量
    • 系统默认的 type metaclass

如果要控制类实例化 instance 的行为,还可以覆写 __call__ 函数。

python metaclass 入门简介的更多相关文章

  1. Python之入门篇1

    一.安装python解释器 官网: https://www.python.org/downloads/windows/ 自行下载安装,添加环境变量 #测试安装是否成功 windows --> 运 ...

  2. python从入门到大神---Python的jieba模块简介

    python从入门到大神---Python的jieba模块简介 一.总结 一句话总结: jieba包是分词技术,也就是将一句话分成多个词,有多种分词模型可选 1.分词模块包一般有哪些分词模式(比如py ...

  3. Python生态环境简介[转]

    Python生态环境简介 作者: Mir Nazim 原文: Python Ecosystem - An Introduction 译者: dccrazyboy  原译: Python生态环境简介 当 ...

  4. MongoDB入门简介

    MongoDB入门简介 http://blog.csdn.net/lolinzhang/article/details/4353699 有关于MongoDB的资料现在较少,且大多为英文网站,以上内容大 ...

  5. Python基础入门教程

    Python基础入门教程 Python基础教程 Python 简介 Python环境搭建 Python 基础语法 Python 变量类型 Python 运算符 Python 条件语句 Python 循 ...

  6. Python爬虫入门教程 43-100 百思不得姐APP数据-手机APP爬虫部分

    1. Python爬虫入门教程 爬取背景 2019年1月10日深夜,打开了百思不得姐APP,想了一下是否可以爬呢?不自觉的安装到了夜神模拟器里面.这个APP还是比较有名和有意思的. 下面是百思不得姐的 ...

  7. (转)python生态环境简介

    Python生态环境简介 作者: Mir Nazim 原文: Python Ecosystem - An Introduction 译者: dccrazyboy  原译: Python生态环境简介 当 ...

  8. Django笔记 —— 入门简介

    最近在学习Django,打算玩玩网页后台方面的东西,因为一直很好奇但却没怎么接触过.Django对我来说是一个全新的内容,思路想来也是全新的,或许并不能写得很明白,所以大家就凑合着看吧- 本篇笔记(其 ...

  9. [转帖]Flink(一)Flink的入门简介

    Flink(一)Flink的入门简介 https://www.cnblogs.com/frankdeng/p/9400622.html 一. Flink的引入 这几年大数据的飞速发展,出现了很多热门的 ...

随机推荐

  1. OpenJudge计算概论-单词倒排【把句子内单词的顺序翻转】

    /*================================================== 单词倒排 总时间限制: 1000ms 内存限制: 65536kB 描述 编写程序,读入一段英文 ...

  2. Jquery使用ajax以及angularjs 动态模板加载并进行渲染

    1. 源码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <tit ...

  3. JQuery Pagenation 知识点整理——(function($){...})应用(20150517)

    首先:(function($){...})为Jquery提供的匿名函数: 代码实例(一) <script type="text/javascript"> (functi ...

  4. 未在本地计算机上注册“Microsoft.Jet.OLEDB.4.0

    昨天修改了一个工具,编译后本机运行正常,但放到服务器上执行却不行,提示: message:GetOleDbSchemaTable requires an open and available Conn ...

  5. php自动转换pfx到pem和cer(dem格式)到pem

    经常做银行的支付接口,私钥一般都是pfx格式(私钥用来加密生成签名发送报文),公钥是cer格式(公钥用来验证返回报文里的签名).但是php里openssl只能用pem格式,每次转换都要用openssl ...

  6. 使用 OWIN 作为 ASP.NET Web API 的宿主

    使用 OWIN 作为 ASP.NET Web API 的宿主 ASP.NET Web API 是一种框架,用于轻松构建可以访问多种客户端(包括浏览器和移动 设备)的 HTTP 服务. ASP.NET ...

  7. SSH_框架整合5--验证用户名是否可用

    SSH_框架整合5--验证用户名是否可用 1 emp-input.jsp中编写ajax验证用户名是否可用: <script type="text/javascript" SR ...

  8. svn 分支

    网上的SVN分支的教程真的不好用,我这里自己写的,绝对靠谱: SVN的分支跟GIT的分支不一样,SVN的分支,包括文件夹的分支或者是文件的分支,都是重复复制文件的,步骤如下: 1.branch/tag ...

  9. java工程师分享:我是如何自学成才的?

    原文:http://www.java800.com/peixun-79062115.html 我是10年河南工业大学的毕业生,当时我们专业许多学生都去报了java培训机构,去达内的都不少.我也想去培训 ...

  10. 黄聪:jquery.confirm弹出确认消息

    1.插件介绍 该确认框的默认样式如: 1.1.插件默认参数 // 默认参数 $.confirm.defaults = { // 样式 css: "http://static.qianduan ...