很多人不理解“元编程”是个什么东西,关于它也没有一个十分准确的定义。这篇文章要说的是Python里的元编程,实际上也不一定就真的符合“元编程”的定义。只不过我无法找到一个更准确的名字来代表这篇文章的主题,所以就借了这么一个名号。

副标题是控制你想控制的一切,实际上这篇文章讲的都是一个东西,利用Python提供给我们的特性,尽可能的使代码优雅简洁。具体而言,通过编程的方法,在更高的抽象层次上对一种层次的抽象的特性进行修改。

首先说,Python中一切皆对象,老生常谈。还有,Python提供了许多特殊方法、元类等等这样的“元编程”机制。像给对象动态添加属性方法之类的,在Python中根本谈不上是“元编程”,但在某些静态语言中却是需要一定技巧的东西。我们来谈些Python程序员也容易被搞糊涂的东西。

我们先来把对象分分层次,通常我们知道一个对象有它的类型,老早以前Python就将类型也实现为对象。这样我们就有了实例对象和类对象。这是两个层次。稍有基础的读者就会知道还有元类这个东西的存在,简言之,元类就是“类”的“类”,也就是比类更高层次的东西。这又有了一个层次。还有吗? ImportTime vs RunTime

如果我们换个角度,不用非得和之前的三个层次使用同样的标准。我们再来区分两个东西:ImportTime和RunTime,它们之间也并非界限分明,顾名思义,就是两个时刻,导入时和运行时。

当一个模块被导入时,会发生什么?在全局作用域的语句(非定义性语句)被执行。函数定义呢?一个函数对象被创建,但其中的代码不会被执行。类定义呢?一个类对象被创建,类定义域的代码被执行,类的方法中的代码自然也不会被执行。

执行时呢?函数和方法中的代码会被执行。当然你要先调用它们。 元类

所以我们可以说元类和类是属于ImportTime的,import一个模块之后,它们就会被创建。实例对象属于RunTime,单import是不会创建实例对象的。不过话不能说的太绝对,因为如果你要是在模块作用域实例化类,实例对象也是会被创建的。只不过我们通常把它们写在函数里面,所以这样划分。

如果你想控制产生的实例对象的特性该怎么做?太简单了,在类定义中重写init方法。那么我们要控制类的一些性质呢?有这种需求吗?当然有!

经典的单例模式,大家都知道有很多种实现方式。要求就是,一个类只能有一个实例。

最简单的实现方法是这样的

工厂模式,不太优雅。我们再来审视一下需求,要一个类只能有一个实例。我们在类中定义的方法都是实例对象的行为,那么要想改变类的行为,就需要更高层次的东西。元类在这个时候登场在合适不过了。前面说过,元类是类的类。也就是说,元类的init方法就是类的初始化方法。 我们知道还有call这个东西,它能让实例像函数那样被调用,那么元类的这个方法就是类在被实例化时调用的方法。

代码就可以写出来了:

主要有两个地方和一般的类定义不同,一是Singleton的基类是type,一是Spam定义的地方有一个metaclass=Singleton。type是什么?它是object的子类,object是它的实例。也就是说,type是所有类的类,也就是最基本的元类,它规定了一些所有类在产生时需要的一些操作。所以我们的自定义元类需要子类化type。同时type也是一个对象,所以它又是object的子类。有点不太好理解,大概知道就可以了。 装饰器

我们再来说说装饰器。大多数人认为装饰器是Python里面最难理解的概念之一。其实它不过就是一个语法糖,理解了函数也是对象之后。就可以很轻易的写出自己的装饰器了。

这里我们还用到了一个装饰器@wraps,它是用来让我们返回的内部函数wrapper和原来的函数拥有相同的函数签名的,基本上我们在写装饰器时都要加上它。

我在注释里写了,@decorator这样的形式等价于func=decorator(func),理解了这一点,我们就可以写出更多种类的装饰器。比如类装饰器,以及将装饰器写成一个类。

注意普通的装饰器和类装饰器实现的不同点。 对数据的抽象--描述符

如果我们想让某一些类拥有某些相同的特性,或者说可以实现在类定义对其的控制,我们可以自定义一个元类,然后让它成为这些类的元类。如果我们想让某一些函数拥有某些相同的功能,又不想把代码复制粘贴一遍,我们可以定义一个装饰器。那么,假如我们想让实例的属性拥有某些共同的特点呢?有人可能会说可以用property,当然可以。但是这些逻辑必须在每个类定义的时候都写一遍。如果我们想让这些类的实例的某些属性都有相同的特点的话,就可以自定义一个描述符类。

这里我们给出一些例子。

在这里面有几个角色,TypedField是一个描述符类,Person的属性是描述符类的实例,看似描述符是作为Person,也就是类的属性而不是实例属性存在的。但实际上,一旦Person的实例访问了同名的属性,描述符就会起作用。需要注意的是,在Python3.5及之前的版本中,是没有set_name这个特殊方法的,这意味着如果你想要知道在类定义中描述符被起了一个什么样的名字,是需要在描述符实例化时显式传递给它的,也就是需要多一个参数。不过在Python3.6中,这个问题得到了解决,只需要在描述符类定义中重写set_name这个方法就好了。还需要注意的是get的写法,基本上对instance的判断是必需的,不然会报错。原因也不难理解,就不细说了。 控制子类的创建——代替元类的方法

在Python3.6中,我们可以通过实现init_subclass特殊方法,来自定义子类的创建,这样我们就可以在某些情况下摆脱元类这个讨厌的东西。

小结

诸如元类等元编程对于大多数人来说有些晦涩难懂,大多数时候也无需用到它们。但是大多数框架背后的实现都使用到了这些技巧,这样才能让使用者写出来的代码简洁易懂。如果你想更深入的了解这些技巧,可以参看一些书籍例如《Fluent Python》、《Python Cookbook》(这篇文章有的内容就是参考了它们),或者看官方文档中的某些章节例如上文说的描述符HowTo,还有Data Model一节等等。或者直接看Python的源码,包括用Python写的以及CPython的源码。

记住,只有在充分理解了它们之后再去使用,也不要是个地方就想着使用这些技巧。

打开APP精彩内容

点击阅读全文

python加上子类的特性_Python里的元编程:控制产生的实例对象的特性以及实例相关推荐

  1. python加上子类的特性_Python 中 Meta Classes详解

    接触过 Django 的同学都应该十分熟悉它的 ORM 系统.对于 python 新手而言,这是一项几乎可以被称作"黑科技"的特性:只要你在models.py中随便定义一个Mode ...

  2. python元编程运用_Python 中的元编程

    就像元数据是有关数据的数据一样,元编程就是编写用于操纵程序的某些程序.人们普遍认为,元程序就是生成其他程序的某些程序,但范式更加广泛.所有旨在自我读取.分析.转换或修改的程序都是元编程的范例.例如: ...

  3. python 什么是原类_Python 什么是元类(metaclasses)?

    1.什么是类 在理解元类之前,我们必须先掌握Python中的类(class). 和大多数语言一样,Python中的类知识用来描述如何"生成一个对象": 但是,在Python中,类不 ...

  4. python的抽象类详解_Python抽象类以及元类

    抽象基类: 继承的约束与协议 __doc__ = """ 抽象基类: 继承的约束与协议 # 抽象基类 --- 有点java的味道,也有点golang的味道,继承,协议,接 ...

  5. python 类可以调用实例变量_python面向对象中类对象、实例对象、类变量、实例变量、类方法、实例方法、静态方法...

    1. 类对象和实例对象 Python中一切皆对象,Python类本身也是一种对象,类定义完成后,会在当前作用域中定义一个以类名为名字的命名空间.类对象具有以下两种操作: 可以通过"类名()& ...

  6. python加上子类的特性_Python--面向对象三大特性

    一.面向对象三大特性 什么是类的继承? 类的继承跟现实生活中的父.子.孙子.重孙子.继承关系一样,父类又称为基类. python中类的继承分为:单继承和多继承 1.继承 1 class ParentC ...

  7. python输出一首诗_Python里隐藏的 诗

    在 Python 的Lib目录里有一个:this.Py (或者在交互式解释器中输入import this) 它其实是隐藏的一首诗 The Zen of Python, by Tim Peters &l ...

  8. python读excel 包含格式_python里读写excel等数据文件的几种常用方式

    python处理数据文件第一步是要读取数据,文件类型主要包括文本文件(csv.txt等).excel文件.数据库文件.api等. 下面整理下python有哪些方式可以读取数据文件. 1. python ...

  9. python哪里最难用_Python里最难的Asyncio,这里有一份非常适合小白的教程

    所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束,你可以继续做其他事情,当它结束时,你会得到通知. Asyncio 是并发(concurrency)的一种方式.对 Python 来说,并 ...

最新文章

  1. PPP协议详细图解实验
  2. 一块网卡绑定多个ip
  3. sklearn数据预处理
  4. CSS中position的4种定位详解
  5. 云计算引入可能破坏之前建立的IT秩序
  6. 初识Unity3D(项目结构、ProBuilder第三方插件)
  7. bigemap 百度教程
  8. 什么是Overlay网络?Underlay 网络 vs. Overlay网络
  9. CSS(定位、淘宝轮播图案例、网页布局总结、元素的显示和隐藏、鼠标经过显示遮罩)
  10. 麋鹿分布图制作(二)—— 用Python和R在地图上打点
  11. 新路由2VS斐讯k2,这样的“0元购”你还买么?
  12. 【Redis核心原理和应用实践】应用 2:缓兵之计 —— 延时队列
  13. butter中文意思_butter中文是什么意思(Butterfly蝴蝶和butter黄油有什么关系)
  14. Qt QML 菜单/目录/工具栏的全面攻略(TabBar、MenuBar、ToolBar、Button定制、Listview、Repeater)
  15. Hadoop相关参数调优
  16. HTML5第三弹:亦酷亦萌的网络拓扑图
  17. Python编程求解指数增长问题
  18. MATLAB插值笔记
  19. macOS如何修改默认打开方式
  20. 投资商业智能BI的六大理由

热门文章

  1. linux suid 脚本,Linux使用suid vim.basic文件实现提权
  2. Django3与Vue3前后端分离搭建
  3. Python中setdefault,deepcopy,copy函数(一分钟读懂)
  4. springboot 不使用 thymeleaf_springboot 使用swagger 不显示basic-error-controller解决
  5. import java.io6_JavaIO(六) 转换流
  6. Python中的浅复制(shallow copy)和深复制(deep copy)
  7. Python网络爬虫学习笔记(十):PyQuery库的使用
  8. 中文信息处理(三)—— 词性标注
  9. 洛谷P1182 数列分段 Section II(二分+贪心)
  10. Vivado设计流程(二)设计文件输入