python中的元类 metaclass

在python中,类(class)本身也是一个实例对象, 它的类型则是元类, 如果没有指明, 则自定义类的类型是type. 换言之, 我们所定义的普通类都是type的实例对象, 如果一个类继承了type, 那么这个类就是元类.

1. 什么是元类

一个类继承了type,那么这个类就是元类

class A(type):

pass

A就是一个元类,元类能用来做什么呢,应该说,绝大多数时候都用不上元类,如果你想使用元类,请确保你非常理解它

2. 元类的__new__方法

在定义一个类时,指定metaclass,就意味着这个类将有所指定的metaclass来创建

class MyMeta(type):

def __new__(cls, *args, **kwargs):

_class = super().__new__(cls, *args, **kwargs)

print(_class.__name__)

return _class

class Animal(metaclass=MyMeta):

def __init__(self, name):

self.name = name

类MyMeta是元类,在定义Animal这个类时,我指定了它的元类是MyMeta,因此,类Animal将由MyMeta的__new__方法来创建,换一个角度来描述,类Animal是类MyMeta的实例对象。在MyMeta的__new__方法中,我使用print语句输出了__class的__name__属性,理论分析告诉我们,这个值应该是Animal, 实际结果也确实是如此。

元类是用来创建普通类(自定义类)的,我们可以利用元类对普通类进行一些限制和要求,比如,我们可以要求所有继承Animal的类必须拥有run方法

from inspect import isfunction

class MyMeta(type):

def __new__(cls, *args, **kwargs):

_class = super().__new__(cls, *args, **kwargs)

if _class.__name__ != 'Animal':

if not hasattr(_class, 'run') or not isfunction(getattr(_class, 'run')):

raise Exception('类{name}没有实现run方法'.format(name=_class.__name__))

return _class

class Animal(metaclass=MyMeta):

def __init__(self, name):

self.name = name

class Cat(Animal):

def __init__(self, name):

super().__init__(name)

cat = Cat('加菲猫')

类Cat继承了Animal,那么它的元类也是MyMeta,在MyMeta的__new__方法里将创建出类Cat,创建以后会检查类Cat是否有run属性且该属性是一个函数,如果不满足条件则抛出异常。如果类Cat实现了run方法,那么上述代码将正常执行

class Cat(Animal):

def __init__(self, name):

super().__init__(name)

def run(self):

print('run')

cat = Cat('加菲猫')

cat.run()

我们务必想清楚一点,尽管我们在脚本里使用class定义了类Cat, 但并不是真正的创建了类Cat,我们所写的代码仅仅是一个定义,创建的过程使用元类MyMeta来完成的。

3. 元类的__init__方法

元类的__new__负责构建普通类,__init__负责对这个普通类进行初始化

from inspect import isfunction

class MyMeta(type):

def __new__(cls, *args, **kwargs):

_class = super().__new__(cls, *args, **kwargs)

if _class.__name__ != 'Animal':

if not hasattr(_class, 'run') or not isfunction(getattr(_class, 'run')):

raise Exception('类{name}没有实现run方法'.format(name=_class.__name__))

return _class

def __init__(self, *args, **kwargs):

super().__init__(*args, **kwargs)

self.home = 'earth'

class Animal(metaclass=MyMeta):

def __init__(self, name):

self.name = name

class Cat(Animal):

def __init__(self, name):

super().__init__(name)

def run(self):

print('run')

print(Animal.home)

print(Cat.home)

在元类的__init__方法里,self参数就是我们所创建的类,Animal和Cat, 我们为他们增加了类属性home, 重载__init__方法,可以更加优雅的实现单例模式

class Singleton(type):

def __init__(self, *args, **kwargs):

self.__instance = None

super().__init__(*args, **kwargs)

def __call__(self, *args, **kwargs):

if self.__instance is None:

self.__instance = super().__call__(*args, **kwargs)

return self.__instance

else:

return self.__instance

class FileLock(metaclass=Singleton):

pass

file_lock_1 = FileLock()

file_lock_2 = FileLock()

print(file_lock_1 is file_lock_2)

4. 元类的__call__方法

class MyMeta(type):

def __call__(self, *args, **kwargs):

raise TypeError('不能创建实例')

class FileTool(metaclass=MyMeta):

@staticmethod

def iter_folder(path):

print('遍历文件夹')

ft = FileTool()

上面的代码执行会报错

Traceback (most recent call last):

File "/Users/kwsy/kwsy/coolpython/demo.py", line 13, in

ft = FileTool()

File "/Users/kwsy/kwsy/coolpython/demo.py", line 5, in __call__

raise TypeError('不能创建实例')

TypeError: 不能创建实例

类FileTool是元类MyMeta的一个示例,那么当执行FileTool()时,不正是在调用元类MyMeta的__call__方法么,而MyMeta的__call__方法偏偏抛出一个类型异常,这就导致FileTool不能被实例化,我们只能使用它的静态方法。

重载元类的__call__方法和类cat的__del__方法可以让我们控制类的实例化过程,我们可以控制一个类的实例数量

class MyMeta(type):

def __init__(self, *args, **kwargs):

self.instance_count = 0

super().__init__(*args, **kwargs)

def __call__(self, *args, **kwargs):

if self.instance_count < 3:

self.instance_count += 1

return type.__call__(self, *args, **kwargs)

else:

raise Exception("类{name}的实例总数超出限制".format(name=self.__name__))

def __del__(self):

self.instance_count -= 1

class Cat(metaclass=MyMeta):

def __init__(self, name):

self.name = name

def __del__(self):

Cat.instance_count -= 1

c1 = Cat('c1')

c2 = Cat('c2')

c3 = Cat('c3')

c4 = Cat('c4')

上面的代码中,当创c4的时候会抛出异常,因为实例的数量已经达到上限,想要创建c4,必须销毁一个之前创建的对象实例

c1 = Cat('c1')

c2 = Cat('c2')

c3 = Cat('c3')

del c1

c4 = Cat('c4')

销毁c1时,类属性instance_count执行了减一操作,因此可以创建出c4。

5.小结

以上示例代码,不保证有工程实践意义,纯粹是为了讲解元类的功能作用而认为制造出来的,坦率的讲,在实际工作中,几乎用不到元类,但我仍然秉持一个观点:面试造火箭,工作拧螺丝的意义在于,能造火箭的人必然牛逼,你可以放心的把拧螺丝的工作交给他,至于是否浪费资源,如果你不会造火箭,那么请慎言,这还不是你这个层次所能讨论的问题。

python元类_python中的元类 metaclass相关推荐

  1. python中的元类_python中的元类

    类也是对象,但是类有创建对象的能力 动态创建一个类: classmonkey():defbanana(self):print 'banana!' defapple(self):print 'i wan ...

  2. python中的元类_Python中的元类(metaclass)

    提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自身有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子和代码片段以帮助理 ...

  3. python 类中定义类_Python中的动态类定义

    python 类中定义类 Here's a neat Python trick you might just find useful one day. Let's look at how you ca ...

  4. python class类_python中的class(类)

    编码注释: 在源文件的第一行或第二行写入如下内容: # -*- coding:gbk -*- # 设置源文件编码格式为:gbk 或 # -*- coding:utf-8 -*- # 设置源文件编码格式 ...

  5. python中如何调用类_python中如何调用类的方法

    类的方法的调用: 与普通的函数调用类似 1.类的内部调用:self.<方法名>(参数列表). 2.在类的外部调用:<实例名>.<方法名>(参数列表). 注意:以上两 ...

  6. python 导入自己写的类_python中自己的类不能被导入

    我自己编写了一个类 class Settings(): def __init__(self): self.screen_width=1200 self.screen_height=800 self.b ...

  7. android 遍历实体类,Java中遍历实体类(处理MongoDB)

    在实际过程中,经常要将实体类进行封装,尤其是处理数据库的过程中:因此,对于遍历实体类能够与数据库中的一行数据对应起来. 我是使用的环境是Spring boot,访问的数据库时MongoDB 实体类遍历 ...

  8. python元类的概念_Python中的元类编程 | 学步园

    过去有这样的概念,一直没有深究它的意义.今天同事问到,刚好也好好了解下. #===============================================Python中的元类编程=== ...

  9. 编写python程序、创建名为class的数据库_Python中的元类(metaclass)以及元类实现单例模式...

    一.理解类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在P ...

最新文章

  1. java串口通信DataRecive_串口通信之DataReceive事件触发时机
  2. Eclipse Memory Analyzer 的使用
  3. Vue.js 深入响应式原理
  4. Java静态方法调用非静态方法
  5. OCP考试052考试,新的考题还有答案整理-23题
  6. linux如何使用uboot的命令,Uboot常用命令使用
  7. 如何开发出优秀的APICloud应用
  8. oracle学习一二
  9. 链路追踪在ERP系统中的应用实践
  10. 计算出你和另一个人的关系,准的邪门了!
  11. vs2013 使用vs调试器,发现调试器显示的数据错误。查看内存,发现内存是正确的。...
  12. Linux环境下安装和使用Hyperledger Composer
  13. System center 2012 R2 实战三、windows server 2012R2安装sharepoint2010及排错
  14. 计算机工程工艺,中国计算机学会第十届计算机工程与工艺学术年会.pdf
  15. Spring的Orderd接口以及@Order、@Primary、@Priority三个注解介绍
  16. HDFS 系列二:HFDS 命令行操作
  17. 最小二乘法在编程中的实现
  18. android订单管理系统源码,生鲜o2o配送开源系统,包括Android源码+SSH后台管理系统...
  19. MIT线性代数:4.矩阵A的LU分解
  20. MATLAB求分段函数

热门文章

  1. 第三次学JAVA再学不好就吃翔(part90)--TreeSet
  2. 走近分形与混沌(part10)--用简单的规律来描述复杂的大自然
  3. 我与无影的初体验:使用无影云桌面进行一个开源 Angular 项目的端到端测试
  4. SAP Spartacus CMSFlexComponent
  5. 关闭excel多余的addin,提供excel启动速度
  6. TypeScript里的类型合并操作符
  7. SAP BSP和JSP页面里UI元素的ID生成逻辑
  8. 各种ABAP调用的性能比较,附详细参数
  9. 一些我工作中经常使用的C4C ABSL代码片段
  10. react-native run-android的输出