博客
关于我
元类(metaclass)
阅读量:441 次
发布时间:2019-03-06

本文共 3995 字,大约阅读时间需要 13 分钟。

Python元类入门到放弃:完整教程

引言

元类(Metaclass)是Python面向对象编程的核心机制之一。它决定了类如何创建和行为,99%的开发者无法深入理解,甚至有些自以为了解的人也只是停留在表面。今天,我们将带你深入探索Python元类的核心原理。

元类是什么?

在Python中,所有事物都是对象。用class关键字定义的类本身也是一个对象,而定义这个类的元类就是我们常说的meta class。简单来说,元类是类的类,它决定了类的创建方式。

class Foo:  # Foo的元类是type    pass

为什么用元类?

元类的主要作用是控制类的创建过程。通过自定义元类,我们可以掌控类的产生规则,甚至可以修改对象的行为。这种能力在开发框架和插件系统中非常有用。

内置函数exec的应用

exec函数可以执行字符串代码,适用于动态代码执行。以下是一个示例:

cmd = """x=1print('exec函数运行了')def func(self):    pass"""class_dic = {}exec(cmd, {}, class_dic)

执行后,class_dic会包含xfunc两个键:

print(class_dic)#输出:{'x': 1, 'func': 
}

类的创建过程

类的创建过程实际上是一个实例化的过程,而实例化是通过元类完成的。默认情况下,Python使用type作为元类。

class People:    country = 'China'    def __init__(self, name, age):        self.name = name        self.age = age    def eat(self):        print('%s is eating' % self.name)
print(type(People))  # 输出: 

type元类的实现

type元类负责创建类,接受三个参数:类名、基类和名称空间。

class_name = 'People'class_bases = (object,)class_dic = {}class_body = """country='China'def __init__(self, name, age):    self.name = name    self.age = agedef eat(self):    print('%s is eating' % self.name)"""exec(class_body, {}, class_dic)
print(class_name)  # 输出: Peopleprint(class_bases)  # 输出: (
,)print(class_dic) # 输出: {'country': 'China', '__init__':
, 'eat':
}

自定义元类控制类创建

要使用自定义元类,我们需要继承type

class Mymeta(type):    def __init__(self, class_name, class_bases, class_dic):        print('self:', self)        print('class_name:', class_name)        print('class_bases:', class_bases)        print('class_dic:', class_dic)        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
class People(object, metaclass=Mymeta):    country = 'China'    def __init__(self, name, age):        self.name = name        self.age = age    def eat(self):        print('%s is eating' % self.name)

__call__方法

__call__方法使类成为可调用对象。例如:

class Foo:    def __call__(self, *args, **kwargs):        print(args)        print(kwargs)        print('__call__实现了,实例化对象可以加括号调用了')obj = Foo()obj('nick', age=18)

输出结果:

('nick',){'age': 18}__call__实现了,实例化对象可以加括号调用了

__new__方法

__new__方法是类实例化的关键:

class A:    passclass B(A):    def __new__(cls):        print("__new__方法被执行")        return cls.__new__(cls)    def __init__(self):        print("__init__方法被执行")b = B()

输出结果:

__new__方法被执行__init__方法被执行

自定义元类控制实例化

通过自定义元类,我们可以控制实例化过程:

class Mymeta(type):    def __call__(self, *args, **kwargs):        print(self)        print(args)        print(kwargs)        obj = self.__new__(self)        self.__init__(obj, *args, **kwargs)        return obj
class People(object, metaclass=Mymeta):    country = 'China'    def __init__(self, name, age):        self.name = name        self.age = age    def eat(self):        print('%s is eating' % self.name)
obj = People('nick', 18)print(obj.__dict__)# 输出:{'name': 'nick', 'age': 18}

元类控制属性查找顺序

在自定义元类环境下,属性查找顺序会受到影响。例如:

class Mymeta(type):    passclass Bar(object):    n = 333class Foo(Bar):    n = 222class OldboyTeacher(Foo, metaclass=Mymeta):    n = 111    school = 'oldboy'    def __init__(self, name, age):        self.name = name        self.age = age    def say(self):        print('%s says welcome to the oldboy to learn Python' % self.name)
print(OldboyTeacher.n)  # 输出: 111

查找顺序为:

OldboyTeacher -> Foo -> Bar -> object -> Mymeta -> type

练习:使用元类隐藏属性

需求:通过自定义元类,修改类的属性为隐藏属性。

class Mymeta(type):    def __init__(self, class_name, class_bases, class_dic):        super(Mymeta, self).__init__(class_name, class_bases, class_dic)    def __call__(self, *args, **kwargs):        obj = self.__new__(self)        self.__init__(obj, *args, **kwargs)        obj.__dict__ = {            '_%s__%s' % (self.__name__, k): v             for k, v in obj.__dict__.items()        }        return obj
class Foo(object, metaclass=Mymeta):    def __init__(self, name, age, sex):        self.name = name        self.age = age        self.sex = sex
obj = Foo('nick', 18, 'male')print(obj.__dict__)# 输出:{'_Foo__name': 'nick', '_Foo__age': 18, '_Foo__sex': 'male'}

这就是通过自定义元类隐藏类属性的实现方法。

转载地址:http://pfgyz.baihongyu.com/

你可能感兴趣的文章
Text-to-Image with Diffusion models的巅峰之作:深入解读 DALL·E 2
查看>>
Tensorflow.python.framework.errors_impl.ResourceExhaustedError:无法分配内存[操作:AddV2]
查看>>
TCP基本入门-简单认识一下什么是TCP
查看>>
tableviewcell 中使用autolayout自适应高度
查看>>
Symbolic Aggregate approXimation(SAX,符号聚合近似)介绍-ChatGPT4o作答
查看>>
Orcale表被锁
查看>>
svn访问报错500
查看>>
sum(a.YYSR) over (partition by a.hy_dm) 不需要像group by那样需要分组函数。方便。
查看>>
ORCHARD 是什么?
查看>>
Struts2中使用Session的两种方法
查看>>
STM32工作笔记0032---编写跑马灯实验---寄存器版本
查看>>
order by rand()
查看>>
SSM(Spring+SpringMvc+Mybatis)整合开发笔记
查看>>
Orderer节点启动报错解决方案:Not bootstrapping because of 3 existing channels
查看>>
org.apache.axis2.AxisFault: org.apache.axis2.databinding.ADBException: Unexpected subelement profile
查看>>
sql查询中 查询字段数据类型 int 与 String 出现问题
查看>>
org.apache.commons.beanutils.BasicDynaBean cannot be cast to ...
查看>>
org.apache.dubbo.common.serialize.SerializationException: com.alibaba.fastjson2.JSONException: not s
查看>>
sqlserver学习笔记(三)—— 为数据库添加新的用户
查看>>
org.apache.http.conn.HttpHostConnectException: Connection to refused
查看>>