博客
关于我
元类(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会包含x和func两个键:

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/

你可能感兴趣的文章
PYTHON-返回给定x的多边形路径的y坐标
查看>>
python-进程池与线程池,协程
查看>>
Python-通过函数传递变量
查看>>
Python内置函数详解——总结篇
查看>>
Python.对象集合中的标识.和散列
查看>>
Python.硒.拖放错误“AttributeError:move_to 需要 WebElement“
查看>>
Python内置函数详解
查看>>
Python/Kivy:从一个类调用函数到另一个类并在 Python 中显示小部件
查看>>
Python/Kivy:我可以在双击标签时调用函数吗
查看>>
python内置函数5-frozenset()
查看>>
Python/Plotly:如何使用要显示的信息自定义悬停模板?
查看>>
Python/Selenium - 无法单击 www.instagram.com 上的“接受 cookie“按钮
查看>>
Python内置函数(35)——next
查看>>
python写入mysql
查看>>
python写入csv老是乱码或者各种不好
查看>>
Python内置函数
查看>>
Python2 安装MySQLdb库
查看>>
Python2 已终结,入手Python 3,你需要这30个技巧
查看>>
python写下拉列表在excel,无法使用python获取Excel下拉列表(组合框)值
查看>>
python2.7安装
查看>>