0x00 前言

在Python中,类也是作为一种对象存在的,因此可以在运行时动态创建类,这也是Python灵活性的一种体现。

本文介绍了如何使用type动态创建类,以及相关的一些使用方法与技巧。

0x01 类的本质

何为类?类是对现实生活中一类具有共同特征的事物的抽象,它描述了所创建的对象共同的属性和方法。在常见的编译型语言(如C++)中,类在编译的时候就已经确定了,运行时是无法动态创建的。那么Python是如何做到的呢?

来看下面这段代码:

class A(object):
    pass

print(A)
print(A.__class__)

在Python2中执行结果如下:

<class '__main__.A'>
<type 'type'>

在Python3中执行结果如下:

<class '__main__.A'>
<class 'type'>

可以看出,类A的类型是type,也就是说:type实例化后是实例化后是对象

0x02 使用type动态创建类

type的参数定义如下:

type(name, bases, dict) name: 生成的类名 bases: 生成的类基类列表,类型为tuple dict: 生成的类中包含的属性或方法

例如:可以使用以下方法创建一个类A

cls = type('A', (object,), {'__doc__': 'class created by type'})

print(cls)
print(cls.__doc__)

输出结果如下:

<class '__main__.A'>
class created by type

可以看出,这样创建的类与静态定义的类基本没有什么差别,使用上还更灵活。

这种方法的使用场景之一是:

有些地方需要传入一个类作为参数,但是类中会用到某些受外界影响的变量;虽然使用全局变量可以解决这个问题,但是比较丑陋。此时,就可以使用这种方法动态创建一个类来使用。

以下是一个使用的示例:

import socket
try:
    import SocketServer
except ImportError:
    # python3
    import socketserver as SocketServer

class PortForwardingRequestHandler(SocketServer.BaseRequestHandler):
    '''处理端口转发请求
    '''

    def handle(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect(self.server) # self.server是在动态创建类时传入的
        # 连接目标服务器,并转发数据
        # 以下代码省略...

def gen_cls(server):
    '''动态创建子类
    '''
    return type('%s_%s' % (ProxyRequestHandler.__name__, server), (PortForwardingRequestHandler, object), {'server': server})


server = SocketServer.ThreadingTCPServer(('127.0.0.1', 8080), gen_cls(('www.qq.com', 80)))
server.serve_forever()

在上面的例子中,由于目标服务器地址是由用户传入的,而PortForwardingRequestHandler类的实例化是在ThreadingTCPServer里实现的,我们没法控制。因此,使用动态创建类的方法可以很好地解决这个问题。

0x03 使用元类(metaclass)

类是实例的模版,而元类是类的模版。通过元类可以创建出类,类的默认元类是type,所有元类必须是type的子类。

下面是元类的一个例子:

import struct

class MetaClass(type):
    def __init__(cls, name, bases, attrd):
        super(MetaClass, cls).__init__(name, bases, attrd)

    def __mul__(self, num):
        return type('%s_Array_%d' % (self.__name__, num), (ArrayTypeBase,), {'obj_type': self, 'array_size': num, 'size': self.size * num})

class IntTypeBase(object):
    '''类型基类
    '''
    __metaclass__ = MetaClass
    size = 0
    format = ''  # strcut格式

    def __init__(self, val=0):
        if isinstance(val, str): val = int(val)
        if not isinstance(val, int):
            raise TypeError('类型错误:%s' % type(val))
        self._net_order = True  # 默认存储的为网络序数据
        self.value = val
        self._num = 1

    def __str__(self):
        return '%d(%s)' % (self._val, self.__class__.__name__)

    def __cmp__(self, val):
        if isinstance(val, IntTypeBase):
            return cmp(self.value, val.value)
        elif isinstance(val, (int, long)):
            return cmp(self.value, val)
        elif isinstance(val, type(None)):
            return cmp(int(self.value), None)
        else:
            raise TypeError('类型错误:%s' % type(val))

    def __int__(self):
        return int(self.value)

    def __hex__(self):
        return hex(self.value)

    def __index__(self):
        return self.value

    def __add__(self, val):
        return int(self.value + val)

    def __radd__(self, val):
        return int(val + self.value)

    def __sub__(self, val):
        return self.value - val

    def __rsub__(self, val):
        return val - self.value

    def __mul__(self, val):