Python中动态创建类的方法

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):
  • return self.value * val
  • def __div__(self, val):
  • return self.value / val
  • def __mod__(self, val):
  • return self.value % val
  • def __rshift__(self, val):
  • return self.value >> val
  • def __and__(self, val):
  • return self.value & val
  • @property
  • def net_order(self):
  • return self._net_order
  • @net_order.setter
  • def net_order(self, _net_order):
  • self._net_order = _net_order
  • @property
  • def value(self):
  • return self._val
  • @value.setter
  • def value(self, val):
  • if not isinstance(val, int):
  • raise TypeError('类型错误:%s' % type(val))
  • if val < 0: raise ValueError(val)
  • max_val = 256 ** (self.size) - 1
  • if val > max_val: raise ValueError('%d超过最大大小%d' % (val, max_val))
  • self._val = val
  • def unpack(self, buff, net_order=True):
  • '''从buffer中提取出数据
  • '''
  • if len(buff) < self.size: raise ValueError(repr(buff))
  • buff = buff[:self.size]
  • fmt = self.format
  • if not net_order: fmt = '<' + fmt[1]
  • self._val = struct.unpack(fmt, buff)[0]
  • return self._val
  • def pack(self, net_order=True):
  • '''返回内存数据
  • '''
  • fmt = self.format
  • if not net_order: fmt = '<' + fmt[1]
  • return struct.pack(fmt, self._val)
  • @staticmethod
  • def cls_from_size(size):
  • '''从整型大小返回对应的类
  • '''
  • if size == 1:
  • return c_uint8
  • elif size == 2:
  • return c_uint16
  • elif size == 4:
  • return c_uint32
  • elif size == 8:
  • return c_uint64
  • else:
  • raise RuntimeError('不支持的整型数据长度:%d' % size)
  • @classmethod
  • def unpack_from(cls, str, net_order=True):
  • obj = cls()
  • obj.unpack(str, net_order)
  • return int(obj)
  • class ArrayTypeBase(object):
  • '''数组类型基类
  • '''
  • def __init__(self, val=''):
  • init_val = 0
  • if isinstance(val, int):
  • init_val = val
  • else:
  • val = str(val)
  • self._obj_array = [self.obj_type(init_val) for _ in range(self.array_size)] # 初始化
  • self.value = val
  • def __str__(self):
  • return str(self.value)
  • def __repr__(self):
  • return repr(self.value)
  • def __getitem__(self, idx):
  • return self._obj_array[idx].value
  • def __setitem__(self, idx, val):
  • self._obj_array[idx].value = val
  • def __getslice__(self, i, j):
  • result = [obj.value for obj in self._obj_array[i:j]]
  • if self.obj_type == c_ubyte:
  • result = [chr(val) for val in result]
  • result = ''.join(result)
  • return result
  • def __add__(self, oval):
  • if not isinstance(oval, str):
  • raise NotImplementedError('%s还不支持%s类型' % (self.__class__.__name__, type(oval)))
  • return self.value + oval
  • def __radd__(self, oval):
  • return oval + self.value
  • def __iter__(self):
  • '''迭代器
  • '''
  • for i in range(self.length):
  • yield self[i]
  • @property
  • def value(self):
  • result = [obj.value for obj in self._obj_array]
  • if self.obj_type == c_ubyte:
  • result = [chr(val) for val in result]
  • result = ''.join(result)
  • return result
  • @value.setter
  • def value(self, val):
  • if isinstance(val, list):
  • raise NotImplementedError('ArrayType还不支持list')
  • elif isinstance(val, str):
  • self.unpack(val)
  • def unpack(self, buff, net_order=True):
  • '''
  • '''
  • if len(buff) == 0: return
  • if len(buff) < self.size: raise ValueError('unpack数据长度错误:%d %d' % (len(buff), self.size))
  • for i in range(self.array_size):
  • self._obj_array[i].unpack(buff[i * self.obj_type.size:], net_order)
  • def pack(self, net_order=True):
  • '''
  • '''
  • result = ''
  • for i in range(self.array_size):
  • result += self._obj_array[i].pack()
  • return result
  • class c_uint8(IntTypeBase):
  • '''unsigned char
  • '''
  • size = 1
  • format = '!B'
  • class c_ubyte(c_uint8): pass
  • class c_uint16(IntTypeBase):
  • '''unsigned short
  • '''
  • size = 2
  • format = '!H'
  • class c_ushort(c_uint16): pass
  • class c_uint32(IntTypeBase):
  • '''unsigned int32
  • '''
  • size = 4
  • format = '!I'
  • class c_ulong(c_uint32): pass
  • class c_uint64(IntTypeBase):
  • '''unsigned int64
  • '''
  • size = 8
  • format = '!Q'
  • class c_ulonglong(c_uint64): pass
  • cls = c_ubyte * 5
  • print(cls)
  • val = cls(65)
  • print(val)
展开

以上代码在Python2.7中输出结果如下:

  • <class '__main__.c_ubyte_Array_5'>
  • AAAAA

在Python3中,metaclass的定义方法做了修改,变成了:

  • class IntTypeBase(object, metaclass=MetaClass):
  • pass

为了兼容性。可以使用six库中的方法:

  • import six
  • @six.add_metaclass(MetaClass)
  • class IntTypeBase(object):
  • pass

使用元类的优点是可以使用更加优雅的方式创建类,如上面的c_ubyte * 5,提升了代码可读性和技巧性。

0x04 重写__new__方法

每个继承自object的类都有__new__方法,这是个在类实例化时优先调用的方法,时机早于__init__。它返回的类型决定了最终创建出来的对象的类型。

请看以下代码:

  • class A(object):
  • def __new__(self, *args, **kwargs):
  • return B()
  • class B(object):
  • pass
  • a = A()
  • print(a)

输出结果如下:

  • <__main__.B object at 0x023576D0>

可以看到,明明实例化的是A,但是返回的对象类型却是B,这里主要就是__new__在起作用。

下面的例子展示了在__new__中动态创建类的过程:

  • class B(object):
  • def __init__(self, var):
  • self._var = var
  • def test(self):
  • print(self._var)
  • class A(object):
  • def __new__(self, *args, **kwargs):
  • if len(args) == 1 and isinstance(args[0], type):
  • return type('%s_%s' % (self.__name__, args[0].__name__), (self, args[0]), {})
  • else:
  • return object.__new__(self, *args, **kwargs)
  • def output(self):
  • print('output from new class %s' % self.__class__.__name__)
  • obj = A(B)('Hello World')
  • obj.test()
  • obj.output()

结果输出如下:

  • Hello World
  • output from new class A_B

这个例子实现了动态创建两个类的子类,比较适合存在很多类需要排列组合生成N多子类的场景,可以避免要写一堆子类代码的痛苦。

0x05 总结

动态创建类必须要使用type实现,但是,根据不同的使用场景,可以选择不同的使用方法。

这样做对静态分析工具其实是不友好的,因为在运行过程中类型发生了变化。而且,这也会降低代码的可读性,一般情况下也不推荐用户使用这样存在一定技巧性的代码。

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
<<上一篇
下一篇>>