需求
给一个类,生成该类对象时会传一些参数,若这些满足参数的对象已经存在,则直接返回已有对象,否则构造新对象。
解决
import copy
import time
class ObjID():
insn_node_details = {}
class OriInsnNode(object):
def __init__(self, ea, mnem, byte_data, prev_insn_node, flags, layer=0):
print '__init'
self.ea = ea
self.insn_data = byte_data
self.layer = layer
self.mnem = mnem
self.len = len(byte_data)
self.prev_insn_node = prev_insn_node
self.next_insn_node = None
self.flags = flags
self.is_call = False
self.xref = []
self.hash = 0
self.is_useless = False
if ea not in ObjID.insn_node_details:
ObjID.insn_node_details[ea] = []
ObjID.insn_node_details[ea].append(self)
def set_type(self,type1):
self.type = type1
class InsnNode(OriInsnNode):
flag = False
def __new__(cls, ea, mnem, byte_data, prev_insn_node, flags, layer=0):
print '__new___'
if ea in ObjID.insn_node_details:
for insn_node in ObjID.insn_node_details[ea]:
if insn_node.ea== ea and insn_node.mnem==mnem and insn_node.flags==flags and insn_node.prev_insn_node==prev_insn_node and insn_node.insn_data==byte_data:
print ('use existing insn_node %x' % insn_node.ea)
return insn_node
else:
InsnNode.flag = True
return super(InsnNode, cls).__new__(cls, ea, mnem, byte_data, prev_insn_node, flags, layer)
def __init__(self, ea, mnem, byte_data, prev_insn_node, flags, layer=0):
print '__init___'
if InsnNode.flag:
InsnNode.flag = False
super(InsnNode, self).__init__(ea, mnem, byte_data, prev_insn_node, flags, layer)
def __copy__(self):
return self
def __deepcopy__(self, memo):
return self
x = InsnNode(0x401091, 'jmp' , '435434', None, 0, 0)
y = InsnNode(0x401091, 'jmp' , '435434', None, 0, 0)
print y
print '-------------'
time.sleep(1)
copy.deepcopy(x)
首先,需要有一个存已知对象的地方,这里用ObjID的类成员insn_node_details保存。
之后,需要在构造对象前检查是否有已知对象存在。这里需要了解python的内置函数__new__和__init__。
构造新对象时,首先要调用__new__,之后才会调用__init__。类似于在new是分配空间的,init是把内容填到空间中,当然这个理解并不准确、
由于多继承条件下,并不知道new’里面的super怎么写,因为继承了好几个啊,这个super到底是哪个呢,我觉得python解释器应该也不知道,并没有做实验测试,直接定义了一个子类,就是代码里的InsnNode,在__new__里判断目标对象是不是已经存在了,dict的value是list的原因是一个地址上可能存在多条指令,所以有了判断逻辑,如果存在直接返回insn_node,如果不存在,则构造新对象,调用OriInsnNode的__new__方法。因为InsnNode的__new__之后还会调用__init__,所以需要一个变量来控制到底init还要不要执行,所以用了flag,并保证flag一直是False。当需要构造新对象时,调用OriInsnNode的__init__方法。
本以为就结束了,然而发现使用copy.deepcopy来copy InsnNode类的对象时会出错,为啥?因为deepcopy不知道该给new哪些参数。于是做了一个特别简陋的copy和deepcopy,其实就是重载了内建方法,特别是deepcopy,这种实现在大多数场景下是错的,实际应该return一个新构造的对象(因为我们知道要传哪些参数),但是我的需求这样就够了。so much。
下面是上述代码的运行结果。
__new___
__init___
__init
__new___
use existing insn_node 401091
__init___
<__main__.InsnNode object at 0x025D35D0>
-------------