python—类对象和实例对象的区别
最近在对RF的通讯层的模块进行封装,需要将之前放在类似main里面的一个方法,如下所示:这段代码是开发提供,用于接口测试,模拟底层通讯,具体的通讯是在dll内,python这边只是做了个封装让RF进行调用。这段通讯层的代码实质上做了五件事:
第一:加载dll;
第二:初始化dll内的通讯参数;
第三:与服务器进行连接,创建session
第四:把数据senbuffer通过sessionManger发送给服务器
第五:取得的数据返回recibuffer
def testlogin(ip,port,buf): dllName = "SessionConnector.dll" dllABSPath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + dllName dll = cdll.LoadLibrary(dllABSPath) dll.Init.restype = c_bool; ret = dll.Init(); dll.CreateSession.argtypes=[c_char_p,c_char_p] dll.CreateSession.restype = c_int; session_id = dll.CreateSession(ip,port); time.sleep(2); dll.SendSessionMsg.argtypes=[c_int,c_char_p,c_uint] dll.CreateSession.restype = c_bool; send_msg = buf ret = dll.SendSessionMsg(session_id, send_msg, len(send_msg) + 1); dll.RecvSessionMsg.argtypes=[c_int,c_char_p,c_uint,c_int] dll.RecvSessionMsg.restype = c_bool; recv_buf = create_string_buffer(1024); ret = dll.RecvSessionMsg(session_id, recv_buf, 1024, 3000); return recv_buf.value
很明显存在很多的问题。最明显的就是这个模块中,第一和第二,第三的前3个步骤,不是每个接口都必须做的事情,所有接口在测试之前干完这些事就可以了。所有这块代码必须得弄成一个类模块,再打成一个包,然后做为关键字提供给RF工具在写case的时候使用,于是就开始重点的关注了下python的类和对象,粗看的时候改写代码是这样的:
class Main_class_dll(): def __init__(self): dllName = "SessionConnector.dll" dllABSPath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + dllName self.dll = cdll.LoadLibrary(dllABSPath) self.session_id='' def int_create_(self): self.dll.Init.restype = c_bool sign = self.dll.Init() def Create_Session(self,ip,port): self.dll.CreateSession.argtypes=[c_char_p,c_char_p] #输入参数的格式 self.dll.CreateSession.restype = c_int; #输出参数的格式 self.session_id = self.dll.CreateSession(ip,port); def send_recv(self,buf): time.sleep(2) self.dll.SendSessionMsg.restype = c_bool; self.dll.SendSessionMsg.argtypes=[c_int,c_char_p,c_uint] ret = self.dll.SendSessionMsg(self.session_id, buf, len(buf) + 1); self.dll.RecvSessionMsg.argtypes=[c_int,c_char_p,c_uint,c_int] self.dll.RecvSessionMsg.restype = c_bool; recv_buf = create_string_buffer(1024); ret = self.dll.RecvSessionMsg(self.session_id, recv_buf, 1024, 3000); self.dll.DestroySession.restype = c_bool; ret = self.dll.DestroySession(self.session_id); return recv_buf.value
然后在RF里调用,老是报错,调试后是发现初始化都有问题,dll也都没加载起来。后面仔细想想,确实觉得这样写是有问题的,__init__方法,表示创建的实例本身时调用的函数进行初始化用的,但是在RF内,始终就没有去创建一个类的实例,直接导入后,使用里面的函数做关键字就可以了,那就是能提供类对象,直接操作类的函数。那类对象和实例对象有什么区别呢?
类对象就是可以用类名字直接使用表示的对象,它支持两种操作,直接属性使用和实例化。对于类属性的使用,直接使用类名.属性即可。对于类方法的使用,需要实例化一个对象后,将对象名赋值给self使用,如下所示:
class test: data = 1 def __init__(self): self.property=0 def test2(self): print 'hello' if __name__=='__main__': t = test() print test.data print t.data print test.test2 print t.test2() print test.test2(t)
运行结果如下:
1
1
<unbound method test.test2>
hello
hello
那实例对象和类对象分别修改数据成员变量,会是怎么样的呢?
class test: data = 1 def __init__(self): self.property=0 def test2(self): return 'hello' if __name__=='__main__': test1= test() test2=test() print 'test.data = ',test.data print 'id of test.data', id(test.data) print '*'*10 print 'test1.data = ',test1.data print 'id of test1.data', id(test1.data) print '*' * 10 print 'test2.data = ',test2.data print 'id of test2.data', id(test2.data) print '*' * 10 test1.data = 2 print 'test1.data = ', test1.data print 'id of test1.data', id(test1.data) print 'test.data = ', test.data print 'id of test.data', id(test.data) print '*' * 10 print 'test2.data = ', test2.data print 'id of test2.data', id(test2.data) print '*' * 10 test1.__class__.data= 2 print 'test1.data = ', test1.data print 'id of test1.data', id(test1.data) print '*' * 10 print 'test2.data = ', test2.data print 'id of test2.data', id(test2.data) print '*' * 10 print 'test.data = ', test.data print 'id of test.data', id(test.data) print '*' * 10
运行结果如下:
test.data = 1 id of test.data 37285680 ********** test1.data = 1 id of test1.data 37285680 ********** test2.data = 1 id of test2.data 37285680 ********** test1.data = 2 id of test1.data 37285668 test.data = 1 id of test.data 37285680 ********** test2.data = 1 id of test2.data 37285680 ********** test1.data = 2 id of test1.data 37285668 ********** test2.data = 2 id of test2.data 37285668 ********** test.data = 2 id of test.data 37285668 **********
从这个例子得出一个结论:
总结 : 第一:作为test的类对象的变量 (data),每次创建一个新的实例对象,类对象变量就多一个引用指向它,通过实例对象来修改类对象的变量的取值,实际上是让实例对象 的data指向了另外一块内存变量。实例对象是类对象的一个拷贝。 第二:可以通过实例对象.__class_.data 来获取类对象的data值,改变类对象的变量的值后,相应实例的值也会发生变化。 类对象的变量在实例中实际上是只读的,任何实例都无法修改类对象变量的值(test1.data=2 实际上是让实例的变量指向了另一块内存,当再生成一个新的对象时, 值仍然还是1),通过实例对象.__class_.data可以修改类对象的属性值
然后又想到一个问题,类成员的变量,实例对象生成的时候就有么?实例成员变量,类对象直接就有么?这就需要用到vars()来测试一下:dir(object)和vars(object)是用来查看模块的attribute和properties的;其中通过help()得到官方的解释如下:
dir使用: dir([object]) -> list of strings
for a module object: the module's attributes.
for a class object: its attributes, and recursively the attributes of its bases.
for any other object: its attributes, its class's attributes, and recursively the attributes of its class's base classes.
vars使用:vars([object]) -> dictionary
Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.
class test3: __value1=0 value2=0 def __init__(self): self.__value3=0 def testit(self): pass if __name__=='__main__': test=test3() print 'vars(test)=',vars(test) print 'vars(test3)=',vars(test3) print 'dir(test)=', dir(test) print 'dir(test3)=', dir(test3)
运行结果:
vars(test)= {'_test3__value3': 0} vars(test3)= {'__module__': '__main__', 'testit': <function testit at 0x027831B0>, '_test3__value1': 0, 'value2': 0, '__doc__': None, '__init__': <function __init__ at 0x027831F0>} dir(test)= ['__doc__', '__init__', '__module__', '_test3__value1', '_test3__value3', 'testit', 'value2'] dir(test3)= ['__doc__', '__init__', '__module__', '_test3__value1', 'testit', 'value2']
dir:如果是一个模块对象,就是一个模块的属性;如果是一个类对象,就是类及其父类的属性;如果是其他对象,就是它本身的属性和它的类对象以及其父类的对象。只显示其属性字段,不显示其值。
vars:vars(object)等价于 object.__dict__,显示当前命名空间内的属性和值。
私有成员分:类对象的变量私有和实例对象的变量私有,对于类对象的私有成员,在实例对象生成初期,实例对象的命名空间内是不存在类对象的任何变量(不管私有或公有),实例对象是无法直接修改的,可以通过实例对象.类名.类对象的私有成员来进行访问和修改,之后命名空间内就有了该数据的引用。
明白了类对象和实例对象的不同之后,修改脚本如下所示,测试终于能跑通了~~~
class Main_class_dll(): dllName = "SessionConnector.dll" # 仅仅是定义了一个string 字符串而已 dllABSPath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + dllName dll = cdll.LoadLibrary(dllABSPath) session_id='' def __init__(self): pass def int_create_(self): self.dll.Init.restype = c_bool sign = self.dll.Init() def Do_work_connector(self,ip,port): self.dll.CreateSession.argtypes=[c_char_p,c_char_p] #输入参数的格式 self.dll.CreateSession.restype = c_int; #输出参数的格式 self.session_id = self.dll.CreateSession(ip,port); def Do_work_send_recv(self,buf): time.sleep(2) self.dll.SendSessionMsg.restype = c_bool; self.dll.SendSessionMsg.argtypes=[c_int,c_char_p,c_uint] ret = self.dll.SendSessionMsg(self.session_id, buf, len(buf) + 1); self.dll.RecvSessionMsg.argtypes=[c_int,c_char_p,c_uint,c_int] self.dll.RecvSessionMsg.restype = c_bool; recv_buf = create_string_buffer(1024); ret = self.dll.RecvSessionMsg(self.session_id, recv_buf, 1024, 3000); self.dll.DestroySession.restype = c_bool; ret = self.dll.DestroySession(self.session_id); return recv_buf.value def Close_Session(self): self.dll.MainSessionClosed.argtypes = [c_int] self.dll.MainSessionClosed.restype = None self.dll.MainSessionClosed(self.session_id)