关于python元类的一些理解

关于python元类的一些理解(后续不断更新)

在看这篇博客之前,可能需要先看一些其他关于元类的博文 。
先写一个函数,如下:

def with_metaclass(meta, *bases):
    """Python 2 and 3 compatible way to add a meta-class."""

    class Metaclass(type):
        def __new__(cls, name, this_bases, d):
            print("packets.py with_metaclass Metaclass __new__ cls: %s"%cls)
            print("packets.py with_metaclass Metaclass __new__ name: %s"%name)
            print("packets.py with_metaclass Metaclass __new__ this_bases: %s"%this_bases)
            print("packets.py with_metaclass Metaclass __new__ d: %s"%d)
            return meta(name, bases, d)

        @classmethod
        def __prepare__(cls, name, _):
            print("packets.py with_metaclass Metaclass __prepare__ cls: %s"%cls)
            print("packets.py with_metaclass Metaclass __prepare__ name: %s"%name)
            return meta.__prepare__(name, bases)
    ##zj_debug
    tmp = type.__new__(Metaclass, "temporary_class", (), {})
    print("packets.py with_metaclass tmp: %s"%tmp)
    ##zj_debug
    return tmp #type.__new__(Metaclass, "temporary_class", (), {})


class Serializable(object):
    """
    This base class for an object than can be serialized. More specifically,
    such objects can be read from and written into a Python dictionary.
    """

    @classmethod
    def new(cls, dct):
        """Creates a new instance of the object."""
        obj = cls.__new__(cls)
        object.__init__(obj)
        obj.parse(dct)
        return obj

class PacketFactory(type):
    """
    A metaclass that is used to register new packet classes as they are being
    defined, and instantiate new packets from their name when necessary.
    """

    _PACKETS = {}

    @staticmethod
    def __new__(mcs, name, bases, attrs):
        """Register a new packet class into the factory."""
        cls = super(PacketFactory, mcs).__new__(mcs, name, bases, attrs)
        print("packets.py PacketFactory __new__ mcs: %s, name: %s, bases: %s, attrs: %s"%(mcs, name, bases, attrs))#zj_debug
        print("packets.py PacketFactory __new__ cls: %s"%cls)#zj_debug
        print("packets.py PacketFactory __new__ type(cls): %s"%type(cls))#zj_debug
        print("packets.py PacketFactory __new__ cls.__type__: %s"%cls.__type__)#zj_debug
        if (
            cls.__type__ is not None
            and cls.__type__ not in PacketFactory._PACKETS
        ):
            PacketFactory._PACKETS[cls.__type__] = cls
            print("packets.py PacketFactory._PACKETS--->"),#zj_debug
            print(PacketFactory._PACKETS)#zj_debug
        return cls


class Packet(with_metaclass(PacketFactory, Serializable)):
    """
    The base class for every packet received. Currently, the packet can
    only be of two kinds: either it is an event or a command.
    """

    __type__ = None

    def __init__(self):
        super(Packet, self).__init__()
        assert self.__type__ is not None, "__type__ not implemented"
    def ..........
    def __repr__(self):
        """
        Return a textual representation of a packet. Currently, it is only
        used to pretty-print the packet contents into the console.
        """
        name = self.__class__.__name__
        if isinstance(self, Query) or isinstance(self, Reply):
            name = self.__parent__.__name__ + "." + name
        attrs = [
            u"{}={}".format(k, v)
            for k, v in Default.attrs(self.__dict__).items()
        ]
        return u"{}({})".format(name, u", ".join(attrs))

我们把这个上面的程序用python解释器运行一下,如下所示:

zhangji16@zhangji16vm:~/c_study/hw_idarling/support_prj$ python3 -i  packets_debug3.py
packets.py with_metaclass tmp: <class '__main__.temporary_class'>
packets.py with_metaclass Metaclass __prepare__ cls: <class '__main__.with_metaclass.<locals>.Metaclass'>
packets.py with_metaclass Metaclass __prepare__ name: Packet
packets.py with_metaclass Metaclass __new__ cls: <class '__main__.with_metaclass.<locals>.Metaclass'>
packets.py with_metaclass Metaclass __new__ name: Packet
packets.py with_metaclass Metaclass __new__ this_bases: <class '__main__.temporary_class'>
packets.py with_metaclass Metaclass __new__ d: {'__type__': None, '__repr__': <function Packet.__repr__ at 0x7f2b477efc80>, 'parse_packet': <staticmethod object at 0x7f2b45afb668>, '__module__': '__main__', '__qualname__': 'Packet', 'build_packet': <function Packet.build_packet at 0x7f2b477efbf8>, '__init__': <function Packet.__init__ at 0x7f2b477ef9d8>, '__doc__': '\n    The base class for every packet received. Currently, the packet can\n    only be of two kinds: either it is an event or a command.\n    '}
packets.py PacketFactory __new__ mcs: <class '__main__.PacketFactory'>, name: Packet, bases: (<class '__main__.Serializable'>,), attrs: {'__type__': None, '__repr__': <function Packet.__repr__ at 0x7f2b477efc80>, 'parse_packet': <staticmethod object at 0x7f2b45afb668>, '__module__': '__main__', '__qualname__': 'Packet', 'build_packet': <function Packet.build_packet at 0x7f2b477efbf8>, '__init__': <function Packet.__init__ at 0x7f2b477ef9d8>, '__doc__': '\n    The base class for every packet received. Currently, the packet can\n    only be of two kinds: either it is an event or a command.\n    '}
packets.py PacketFactory __new__ cls: <class '__main__.Packet'>
packets.py PacketFactory __new__ type(cls): <class '__main__.PacketFactory'>
packets.py PacketFactory __new__ cls.__type__: None

通常一般来讲如果把一个类实例化,我们知道会运行构造函数 __init__,但是在这个构造函数之前,我们知道__new__函数会先__init__执行。上面的例子告诉我们,其实还有比__new__先执行的函数,那就是__prepare__函数 。关于这些函数的真正作用,就是那个函数返回类,返回实例的内部生成和运行原理在后续理解后再进行补充,这里暂时只告诉他们执行的先后顺序 。

运行脚本产生的一堆输出,是因为我们定义了类class Packet(with_metaclass(PacketFactory, Serializable)),也就是说这个类只要定义完成,解释器加载脚本,就意味着我们生成这个类,记住不是实例,也就是说不需要实例化在控制台终端就能输出上面的这些内容。下面在交互界面,我们通过生成一个类来说明这个原理 。

>>> class ZJ(object):
...     pass
... 
>>> diy_zj = with_metaclass(PacketFactory, ZJ)
packets.py with_metaclass tmp: <class '__main__.temporary_class'>
>>> with_metaclass(PacketFactory, ZJ)
packets.py with_metaclass tmp: <class '__main__.temporary_class'>
<class '__main__.temporary_class'>
>>> diy_zj
<class '__main__.temporary_class'>
>>> class DIY(diy_zj):
...     pass
... 
packets.py with_metaclass Metaclass __prepare__ cls: <class '__main__.with_metaclass.<locals>.Metaclass'>
packets.py with_metaclass Metaclass __prepare__ name: DIY
packets.py with_metaclass Metaclass __new__ cls: <class '__main__.with_metaclass.<locals>.Metaclass'>
packets.py with_metaclass Metaclass __new__ name: DIY
packets.py with_metaclass Metaclass __new__ this_bases: <class '__main__.temporary_class'>
packets.py with_metaclass Metaclass __new__ d: {'__qualname__': 'DIY', '__module__': '__main__'}
packets.py PacketFactory __new__ mcs: <class '__main__.PacketFactory'>, name: DIY, bases: (<class '__main__.ZJ'>,), attrs: {'__qualname__': 'DIY', '__module__': '__main__'}
packets.py PacketFactory __new__ cls: <class '__main__.DIY'>
packets.py PacketFactory __new__ type(cls): <class '__main__.PacketFactory'>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "packets_debug3.py", line 12, in __new__
    return meta(name, bases, d)
  File "packets_debug3.py", line 62, in __new__
    print("packets.py PacketFactory __new__ cls.__type__: %s"%cls.__type__)#zj_debug
AttributeError: type object 'DIY' has no attribute '__type__'
>>> DIY
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'DIY' is not defined

故意不给 DIY 添加属性__type__ = xxx, 所以这个类最终生产失败了,也就是说DIY会显示没有定义 。但是终端输出的内容正好展示了生成这个的类的过程 。下面我们增加 __type__属性来产生这个类。

>>> class DIY(diy_zj):
...     __type__ = "event"
...     pass
... 
packets.py with_metaclass Metaclass __prepare__ cls: <class '__main__.with_metaclass.<locals>.Metaclass'>
packets.py with_metaclass Metaclass __prepare__ name: DIY
packets.py with_metaclass Metaclass __new__ cls: <class '__main__.with_metaclass.<locals>.Metaclass'>
packets.py with_metaclass Metaclass __new__ name: DIY
packets.py with_metaclass Metaclass __new__ this_bases: <class '__main__.temporary_class'>
packets.py with_metaclass Metaclass __new__ d: {'__qualname__': 'DIY', '__type__': 'event', '__module__': '__main__'}
packets.py PacketFactory __new__ mcs: <class '__main__.PacketFactory'>, name: DIY, bases: (<class '__main__.ZJ'>,), attrs: {'__qualname__': 'DIY', '__type__': 'event', '__module__': '__main__'}
packets.py PacketFactory __new__ cls: <class '__main__.DIY'>
packets.py PacketFactory __new__ type(cls): <class '__main__.PacketFactory'>
packets.py PacketFactory __new__ cls.__type__: event
packets.py PacketFactory._PACKETS--->
{'event': <class '__main__.DIY'>}
>>> DIY
<class '__main__.DIY'>

现在我们对 DIY类的产生,进行一一解读, __prepare__ cls: <class '__main__.with_metaclass.<locals>.Metaclass'> 这是一个局部变量,可以认为是一个类, (ps:是元类吗,以后研究), __prepare__ name: DIY 这是我们即将要产生的类的名字 DIY
__new__ this_bases: <class '__main__.temporary_class'> 临时的基类;
__new__ d: {'__qualname__': 'DIY', '__type__': 'event', '__module__': '__main__'} 要产生的类的属性;

接下来是工厂类PacketFactory(type)部分:
packets.py PacketFactory __new__ mcs: <class '__main__.PacketFactory'>, name: DIY, bases: (<class '__main__.ZJ'>,), attrs: {'__qualname__': 'DIY', '__type__': 'event', '__module__': '__main__'}指出了要产生类的元类
<class '__main__.PacketFactory'>,后面紧跟名字,DIY类的基类就<class '__main__.ZJ'>,属性attr;

重点是这条代码__new__ type(cls): <class '__main__.PacketFactory'>, 它说明了一个问题,是谁把cls生出来的,没错就是这个工厂类,这个工厂是一个元类 。
再看一些例子:

>>> class AA(PacketFactory):
...     pass
... 
>>> AA
<class '__main__.AA'>
>>> AA.mro()    #元类没爹??暂时先这样理解,至少这里没父类
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor 'mro' of 'type' object needs an argument
>>> class BB(type):
...     pass
... 
>>> BB
<class '__main__.BB'>
>>> BB.mro()   #元类没爹??暂时先这样理解,至少这里没父类
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor 'mro' of 'type' object needs an argument
>>> DIY.mro()  #父类有自己,还有ZJ,object
[<class '__main__.DIY'>, <class '__main__.ZJ'>, <class 'object'>]
>>> diy_zj  #with_metaclass 函数返回的是一个名字叫临时类的类
<class '__main__.temporary_class'>
>>> diy_zj.mro()  #它的父类自然包括自身
[<class '__main__.temporary_class'>, <class 'object'>]

其实这个函数with_metaclass可以先根据其参数去理解其功能,第一个参数是一个 元类, 而第二个参数是一个元胞 *base 其意义为父类
进而我们加深对这个类Packet(with_metaclass(PacketFactory, Serializable ))的理解,元类是数据包工厂类,后面的序列化类是正儿八经的类,是Packet类的父类 。

现在我们在增加一个例子,如下:

#!/usr/bin/env python
# coding=utf-8

#from models import Database, Project
from packets_debug2 import (
    Command,
    Container,
    DefaultCommand,
    ParentCommand,
    Query as IQuery,
    Reply as IReply,
    ZJ_DIY,
)

class DownloadFile(ParentCommand):
    __command__ = "download_file"

    class Query(IQuery, DefaultCommand):
        def __init__(self, project, database):
            super(DownloadFile.Query, self).__init__()
            self.project = project
            self.database = database

    class Reply(IReply, Container, Command, ZJ_DIY):
        pass

备注:

# from packets_debug2.py
##zj_debug
class ZJ_DIY(object):
    @staticmethod
    def __new__(cls, *args, **kwargs):
        print("ZJ_DIY cls: %s"%cls)
        self = super(ZJ_DIY, cls).__new__(cls)
        return self
##zj_debug

class Container(Command):
    """
    Containers are a special kind of commands that will contain some raw data.
    This is useful for exchanging files as they don't have to be encoded.
    """

    @staticmethod
    def __new__(cls, *args, **kwargs):
        print("packets.py Container(Command) __new__ cls: %s"%cls)#zj_debug !!!!!
        self = super(Container, cls).__new__(cls)
        self._upback = None
        self._downback = None
        return self

如果我们加载脚本,自然会把packets_debug2.py里面生成类的过程信息打印出来,但是这里我们主要想看一下这个嵌套类的生成过程,因为嵌套类是定义在DownloadFile类中,所以他们的生成过程没有被打印出来,怎么打印出来呢?如下:

zhangji16@zhangji16vm:~/c_study/hw_idarling/support_prj$ python -i command_debug.py 
>>> DownloadFile.Reply(IQuery) #IQuery 实例也行
packets.py Container(Command) zj_debug __new__ cls: <class '__main__.DownloadFile.Reply'>
ZJ_DIY cls: <class '__main__.DownloadFile.Reply'>
DownloadFile.Reply()

在上面显示的调用嵌套类后,就输出了创建这个嵌套类的过程 。
(未完待续。。。。。。。。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值