Python 中最黑魔法、最难懂的概念
共 5400字,需浏览 11分钟
·
2021-06-19 17:28
大家好,我是老胡
最近在看一个开源框架的源码,其中大量使用了 metaclass 方法。这个概念非常抽象,本文我就以一个有趣实例,用更简洁和通畅的方式来理解它。
元类 ( metaclass )应该是 Python 中最黑魔法、最难懂的概念之一,它提供了创造新类型的能力,为程序设计带来更多可能性。不少功能强大的开发框架,内部实现离不开 metaclass 的魔法。
Class
面向对象编程最重要的概念就是类(Class)和实例(Instance),我们先来创建一个 Lxs 的类,它有两个基本功 sing 和 dance ,lxs 是这个类的实例:
class Lxs(object):
def __init__(self, name, duration):
self.name = name
self.duration = duration
print('%s practiced %s years' % (self.name, self.duration))
def sing(self):
print('%s good at singing' % self.name)
def dance(self):
print('%s good at dancing' % self.name)
lxs = Lxs('laohu',1.5)
lxs.sing()
lxs.dance()
练习时常1年半的老胡擅长唱和跳
恩,针不戳!
laohu practiced 1.5 years
laohu good at singing
laohu good at dancing
再来用__class__
属性或type()看看 Lxs 和 lxs 分别是谁创建的
print(lxs.__class__)
print(Lxs.__class__)
lxs 是 Lxs 的实例,它创建自 Lxs ,这很容易理解。
我们 Lxs 类是 type 创建的?
<class '__main__.Lxs'>
<class 'type'>
一切对象都来自 type
先说结论:type 可以动态创建 类(class) ,对象是类(class)的实例,类(class)也是对象,是 type 的实例。type 为对象的顶点,所有对象都创建自 type 。
当使用 type 创建 class 时,其用法如下:
class = type(classname, superclasses, attributedict)
'''
classname:类名
superclasses:类的继承关系,用元组表示
attributedict:表示各种属性、方法,用字典表示
'''
继续上例,先定义__init__
,sing 和 dance ,然后用 type 可以创建和上面完全一样的类:
Lxs = type('Lxs', (object,), dict( __init__= __init__,sing=sing,dance=dance))
lxs = Lxs('laohu',1)
lxs.sing()
print(lxs.__class__)
print(Lxs.__class__)
这里不得不提一下__call__
这个属性
此方法会在实例作为一个函数被“调用”时被调用
这里等号右边的type(classname, superclasses, attributedict),就是 type 的__call__
运算符重载,它会进一步调用:
type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)
# 这一部分我们以后有空再细品
总结一下:type 实际上是 Python 创建所有 class 的 metaclass。
metaclass
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。
先定义metaclass,就可以创建类,最后创建实例。
一句话:metaclass 是 type 的子类,是类的模板
metaclass 的主要目的是在 class 被创建的时候对生成的 class 进行自动的动态修改。
举个例子:像老胡就只会 sing 和 dance,有人还会rap,有人会说相声,我们定义很多的 class ,有一天,一个男人横空出世,他会打篮球!然后,所有的练习生也都学会了篮球,这可怎么修改?
metaclass 就可以施展魔法了
class LxsMetaclass(type):
def __new__(cls, cls_name, bases, attrs):
def basketball(self):
print('%s good at basketball' % self.name)
attrs['basketball'] = basketball
return super(LxsMetaclass, cls).__new__(cls, cls_name, bases, attrs)
它指示Python解释器在创建 LxsMetaclass 时,要通过LxsMetaclass.new()来创建,在此,我们可以修改类的定义,比如,加上新的方法basketball(),然后,返回修改后的定义。
我们用 LxsMetaclass 这个模板创建类:
class Cxk(object, metaclass=LxsMetaclass):
def __init__(self, name, duration):
self.name = name
self.duration = duration
print('%s practiced %s years' % (self.name, self.duration))
def sing(self):
print('%s good at singing' % self.name)
def dance(self):
print('%s good at dancing' % self.name)
def rap(self):
print('%s good at rap' % self.name)
cxk = Cxk('cxk',2.5)
cxk.basketball()
运行结果如下,秀?
cxk practiced 2.5 years
cxk good at basketball
不过metaclass的作用肯定不限于此,举个例子,也算是个思考题,大家品一品。
比如 laohu 化身 xck 的粉丝,打着学篮球的幌子学 sing、dance和rap :
class Funs(Cxk):
def basketball(self):
print('%s good at singing&dancing&rap' % self.name)
fans = Funs('laohu',0.5)
fans.basketball()
运行结果会是什么呢?
laohu practiced 0.5 years
laohu good at basketball
laohu 真的就只学会了篮球。。。
这是为何呢?且听下回分解。
兄弟们,来个三连可好?转发,在看,点赞
··· END ··· 推荐阅读: 一、Number(数字) Python基础之数字(Number)超级详解 Python随机模块22个函数详解 Python数学math模块55个函数详解 二、String(字符串) Python字符串的45个方法详解 Pandas向量化字符串操作 三、List(列表) 超级详解系列-Python列表全面解析 Python轻量级循环-列表推导式 四、Tuple(元组) Python的元组,没想象的那么简单 五、Set(集合) 全面理解Python集合,17个方法全解,看完就够了 六、Dictionary(字典) Python字典详解-超级完整版 七、内置函数 Python初学者必须吃透这69个内置函数! 八、正则模块 Python正则表达式入门到入魔 笔记 | 史上最全的正则表达式 八、系统操作 Python之shutil模块11个常用函数详解 Python之OS模块39个常用函数详解 九、进阶模块 【万字长文详解】Python库collections,让你击败99%的Pythoner 高手如何在Python中使用collections模块 ↓扫描关注本号↓