python元类_python中的元类 metaclass
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相关推荐
- python中的元类_python中的元类
类也是对象,但是类有创建对象的能力 动态创建一个类: classmonkey():defbanana(self):print 'banana!' defapple(self):print 'i wan ...
- python中的元类_Python中的元类(metaclass)
提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自身有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子和代码片段以帮助理 ...
- python 类中定义类_Python中的动态类定义
python 类中定义类 Here's a neat Python trick you might just find useful one day. Let's look at how you ca ...
- python class类_python中的class(类)
编码注释: 在源文件的第一行或第二行写入如下内容: # -*- coding:gbk -*- # 设置源文件编码格式为:gbk 或 # -*- coding:utf-8 -*- # 设置源文件编码格式 ...
- python中如何调用类_python中如何调用类的方法
类的方法的调用: 与普通的函数调用类似 1.类的内部调用:self.<方法名>(参数列表). 2.在类的外部调用:<实例名>.<方法名>(参数列表). 注意:以上两 ...
- python 导入自己写的类_python中自己的类不能被导入
我自己编写了一个类 class Settings(): def __init__(self): self.screen_width=1200 self.screen_height=800 self.b ...
- android 遍历实体类,Java中遍历实体类(处理MongoDB)
在实际过程中,经常要将实体类进行封装,尤其是处理数据库的过程中:因此,对于遍历实体类能够与数据库中的一行数据对应起来. 我是使用的环境是Spring boot,访问的数据库时MongoDB 实体类遍历 ...
- python元类的概念_Python中的元类编程 | 学步园
过去有这样的概念,一直没有深究它的意义.今天同事问到,刚好也好好了解下. #===============================================Python中的元类编程=== ...
- 编写python程序、创建名为class的数据库_Python中的元类(metaclass)以及元类实现单例模式...
一.理解类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在P ...
最新文章
- java串口通信DataRecive_串口通信之DataReceive事件触发时机
- Eclipse Memory Analyzer 的使用
- Vue.js 深入响应式原理
- Java静态方法调用非静态方法
- OCP考试052考试,新的考题还有答案整理-23题
- linux如何使用uboot的命令,Uboot常用命令使用
- 如何开发出优秀的APICloud应用
- oracle学习一二
- 链路追踪在ERP系统中的应用实践
- 计算出你和另一个人的关系,准的邪门了!
- vs2013 使用vs调试器,发现调试器显示的数据错误。查看内存,发现内存是正确的。...
- Linux环境下安装和使用Hyperledger Composer
- System center 2012 R2 实战三、windows server 2012R2安装sharepoint2010及排错
- 计算机工程工艺,中国计算机学会第十届计算机工程与工艺学术年会.pdf
- Spring的Orderd接口以及@Order、@Primary、@Priority三个注解介绍
- HDFS 系列二:HFDS 命令行操作
- 最小二乘法在编程中的实现
- android订单管理系统源码,生鲜o2o配送开源系统,包括Android源码+SSH后台管理系统...
- MIT线性代数:4.矩阵A的LU分解
- MATLAB求分段函数
热门文章
- 第三次学JAVA再学不好就吃翔(part90)--TreeSet
- 走近分形与混沌(part10)--用简单的规律来描述复杂的大自然
- 我与无影的初体验:使用无影云桌面进行一个开源 Angular 项目的端到端测试
- SAP Spartacus CMSFlexComponent
- 关闭excel多余的addin,提供excel启动速度
- TypeScript里的类型合并操作符
- SAP BSP和JSP页面里UI元素的ID生成逻辑
- 各种ABAP调用的性能比较,附详细参数
- 一些我工作中经常使用的C4C ABSL代码片段
- react-native run-android的输出