时间: 2020-09-03 00:08:26 人气: 2280 评论: 0
在Python中,类也是作为一种对象存在的,因此可以在运行时动态创建类,这也是Python灵活性的一种体现。
本文介绍了如何使用type动态创建类,以及相关的一些使用方法与技巧。
何为类?类是对现实生活中一类具有共同特征的事物的抽象,它描述了所创建的对象共同的属性和方法。在常见的编译型语言(如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实例化后是类,类实例化后是对象。
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里实现的,我们没法控制。因此,使用动态创建类的方法可以很好地解决这个问题。
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 技术沙龙 教程文章 热点综合