Python基础教程

第一章: 环境搭建,安装Python

第二章: 挑选一款趁手的IDE

第三章: 计算机基础知识

第四章: 命令行基础知识

第五章: 从全局把握Python

第六章: Python语言基础

第七章: Python流程控制

第八章: Python数据类型与运算

第九章: Python字符串类型

第十章: Python列表类型

第十一章: Python元组类型

第十二章: Python字典类型

第十三章: Python集合类型

第十四章: Python函数处理

第十五章: Python文件处理

第十六章: Python面向对象

第十七章: Python异常处理

第十八章: Python模块处理

第十九章: Python高级编程

第二十章: Python项目实战

首页 > Python基础教程 > 第十六章: Python面向对象 > 16.10节: Python中的元类

16.10节: Python中的元类

薯条老师 2022-11-30 15:41:16 27261 0

编辑 收藏

广州番禺Python, Java小班周末班培训

薯条老师在广州做Python和Java的小班培训,一个班最多10人,学员的平均就业薪资有11K。不在广州的同学可以报名线上直播班,跟线下小班的同学们同步学习。培训的课程有Python爬虫,Python后端开发,Python办公自动化,Python大数据分析,Python量化投资,Python机器学习,Java中高级后端开发。授课详情请点击:http://chipscoco.com/?cate=6

16.10.1 一切皆对象

在Python中一切皆对象,迄今学过的简单数据类型,复合数据类型,函数等都是对象,甚至连类本身也是对象。Python中的类是用来创建对象的对象。在前面的内容中,我们使用type来输出对象的类型。对于类,同样可以使用type来输出其类型。现在进入Python的交互模式,在交互模式中进行验证:
>>> class Test:
...          pass
...
>>> type(Test)
<class 'type'>

在交互模式中,我们定义了一个Test类,在执行type函数时,输出的结果为 <class 'type'>,type正是类的类型。在Python中,type类型是类类型的metaclass,metaclass的中文释义即为元类。使用元类可以动态地创建类。

16.10.2 type元类

关于type元类的定义和用法,可以在交互模式中执行help('type')来进行查找:
>>> help(type)
Help on class type in module builtins:
class type(object)
|  type(object_or_name, bases, dict)
|  type(object) -> the object's type
|  type(name, bases, dict) -> a new type
|

在help的输出中可知,type本身也是一个类,它的父类是object。

在16.7节中讲到了MRO,在MRO的输出中,我们可以分析出object是所有类类型的顶层基类。

在type类中,可以通过函数type(name, bases, dict)来创建一个新的类类型。其中参数name表示类类型的名称,必须是字符串类型。bases用来定义该类型的基类,是一个元祖类型。参数dict是一个字典类型,对应的是该类型的命名空间,用来定义类类型的类属性和方法,在这个字典中,键名是属性名或方法名,键值对应的是具体的属性值或函数。

代码实例:

# __desc__ = 使用type来动态地创建类
 
# 1.执行type来创建一个Cat类,并定义了一个类属性species
Cat = type("Cat", (), {"species":"cat"})
 
# 通过type来创建Cat类,通过Cat类来实例化一个对象
kitty = Cat()
print(kitty.species)
 
"""
程序的输出为:
cat
"""
 
# 2.执行type来创建一个Babby类,Babby类的基类是Cat类
Babby = type("Babby", (Cat,),{})
# 通过type来创建Babby类,Babby类继承于Cat类
# 现在通过Babby类来实例化一个对象
lisa = Babby()
print(lisa.species)
 
"""
程序的输出为:
cat
"""
在上面的代码实例中,为类类型添加的是类属性,由于type中的dict参数是一个字典类型,所以在添加类方法,静态方法,对象方法时,方法需提前进行定义,然后将其作为键值。类方法,静态方法须遵循相应的规范,比如定义类方法时,须通过@classmethod进行修饰。

代码实例

# __desc__ = 使用type来动态地创建类
 
# 定义一个catch_mice函数,参数为cls
@classmethod
def catch_mice(cls):
    print("a cat that doesn't catch a mouse is not a good cat") 
 
# 1.执行type来创建一个Cat类,并将catch_mice作为类方法
Cat = type("Cat", (), {"catch_mice": catch_mice})
 
Cat.catch_mice()
"""
程序的输出为:
a cat that doesn't catch a mouse is not a good cat
"""

16.10.3 自定义元类

在16.9.2节中,系直接通过type类型来创建一个类类型,但这样的做法有一个很大的弊端,不能控制类类型的创建过程。继承是面向对象编程的重要特征之一,通过面向对象中的继承,我们可以从type中派生出一个新的元类。

在对type进行继承之前,需要先理解并掌握type中的四个魔术方法:

(1) type.__new__(mcs, name, bases, dict)

__new__方法是一个静态方法,用来构造一个类类型, 并返回该类实例。元类中的__new__方法用来构造一个类,类中的__new__方法用来构造一个对象。__new__方法中的参数与type方法中的参数基本一致,但多了一个mcs, 参数mcs表示元类的类型。由于__new__方法是静态方法,所以必须显式的传递元类类型 。

(2) type.__prepare__(mcs, name, bases)

__prepare__方法是一个类方法,默认返回一个空的dict对象。该方法的参数同__new__方法中的参数。__prepare__方法用来创建类的命名空间,即type中的dict参数所对应的命名空间。在__new__方法中接收一个dict参数,而__new__方法是自动被Python调用的,由此可推断Python解释器会先调用__prepare__方法,然后将已构造好的命名空间对象传递给__new__方法。

(3) type.__init__(self, name, bases, dict)

__init__方法用来对类实例进行初始化,参数与__new__方法基本一致,参数self表示元类的实例对象。__init__方法在__new__方法之后被调用。

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

在之前的内容中有介绍过__call__方法,__call__方法使得对象变成可调用对象。在type元类的__call__方法中,默认返回类的实例。现在我们来对type进行继承:

# __desc__ = 对type类进行继承
 
#定义一个CatType,该元类负责创建Cat类
class CatType(type):
    @classmethod
    def __prepare__(mcs, name, bases):
        print("Cat type is creating namespace")
        # 调用父类type的__prepare__方法,返回一个命名空间
        return super().__prepare__(name, bases)
 
    @staticmethod
        def __new__(mcs, name, bases, dict)
        print("Cat type is creating Cat Class")
        # 调用父类type的__new__方法
        return super().__new__(mcs, name, bases, dict)
 
    def __init__(self, name, bases, dict)
        print("Cat type is initializing Cat Class")
        # 调用父类type的__init__方法
        super().__init__(name, bases, dict)
        print("Cat Class initialized")
对type进行继承以后,就得到了一个自定义的元类,我们可以用这个自定义的元类来控制类的创建过程。在Python3中定义一个类时,通过关键字参数metaclass来指定元类。指定元类的基本语法:

class ClassName(metaclass=type):
    pass
metaclass的默认值是type,即默认使用内置的type元类来创建类类型。现在我们通过自定义的元类CatType来控制Cat类的创建过程。那么具体该如何控制呢?很简单,把控制逻辑写入到元类的这几个魔术方法当中。同学们须理解这几个魔术方法的作用,以改写对应的魔术方法。现在通过自定义的元类来控制每次对类实例化时,获取的都是同一个对象:

# __desc__ = 对type类进行继承
# 定义一个CatType,该元类负责创建Cat类
 
 
class  CatType(type):
    @classmethod
    def __prepare__(mcs, name, bases):
        return super().__prepare__(name, bases)
 
    @staticmethod
    def __new__(mcs, name, bases, dict):
        print("__new__")
        return super().__new__(mcs, name, bases, dict)
 
    def __init__(self, name, bases, dict):
        print("__init__")
        # 在元类中为类创建一个类属性__instance
        # 用__instance来保存类的实例对象
        self.__instance = None
        super().__init__(name, bases, dict)
 
    def __call__(self, *args, **kwargs):
        print("__call__")
        # 判断__instance属性是否为空
        if self.__instance is None:
            # 如果为空,则调用父类type的__call__方法来返回类的实例对象
            self.__instance = super().__call__(*args, **kwargs)
 
        # 由于__instance已经非空,所以直接返回__instance   
        return self.__instance
 
 
class Cat(metaclass=CatType):
    pass
 
kitty = Cat()
 
# 第二次调用Cat()时,由于Cat类已经构造并且初始化,所以会先调用__call__方法
# 在__call__方法中直接返回已实例化的对象,所以每次获取到的都是同一个对象
lisa = Cat()
 
# 使用is操作符来判断是否为同一个对象
print(lisa is kitty)
 
"""
程序的输出为:
__new__
__init__
__call__
__call__
True
"""

16.10.4 知识要点

(1) 在Python中一切皆对象,迄今学过的简单数据类型,复合数据类型,函数等都是对象,甚至连类本身也是对象。Python中的类是用来创建对象的对象。
(2) type是类的元类,通过类来创建对象,而通过元类来创建类。
(3) 自定义元类时,须通过关键字参数metaclass来指定元类

16.10.5 最具实力的小班培训

薯条老师在广州做Python和Java的小班培训,一个班最多10人。不在广州的同学可以报名线上直播班,跟线下小班的同学们同步学习打算参加小班培训的同学,必须遵守薯条老师的学习安排,认真做作业和项目。把知识学好,学扎实,那么找到一份高薪的工作就是很简单的一件事。

(1) Python后端工程师高薪就业班,月薪11K-18K,免费领取课程大纲
(2) Python爬虫工程师高薪就业班,年薪十五万,免费领取课程大纲
(3) Java后端开发工程师高薪就业班,月薪11K-20K, 免费领取课程大纲
(4) Python大数据分析,量化投资就业班,月薪12K-25K,免费领取课程大纲

扫码免费领取Python学习资料:

关注微信公众号.jpg














欢迎 发表评论: