简单的一个小测试的记录,但因为我习惯记录前因后果,写得有点啰嗦。
起因
我尝试把一些 C/C++ 的库用 Python 重新实现一遍(纯练习 Python 和 熟悉库的原理,实际应用当然是Python直接调用C/C++),先逐句翻译成Python代码,再根据实际情况优化。
struct 当然先是转成 class。然后我发现,一些结构体里反复出现的公共成员,别人会先定义一个宏来减少重复工作,类似这样:
1 2 3 4 5 6 7 8 9 10 11 12
| #define CommonVars type1 var1; type2 var2; type3 var3 struct std_struct{ CommonVars; }; struct struct1{ CommonVars; type4 var4; };
|
对应在Python里的一对一翻译,就是分别定义 CommonVars, StdStruct, Struct1, Struct2… 这么几个类,其中后面的都 继承 CommonVars。当然,很明显CommonVars 和 StdStruct 其实是一回事,但是为了保持跟C++代码的一一对应,我还是保留了两个类。
后来一想,何必继承呢,要不干脆定义完 CommonVars 之后 StdStruct = CommonVars?
实测
Python 一切皆对象,包括 函数 和 类 都是可以作为右值给变量赋值的,这样做相当与给 函数 / 类 起了别名,是可以运行的。
但是别名背后的运作是怎么样的呢,最好的办法就是实际试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def funA(): return 'This is funA.' funB = funA print('run funA:', funA()) print('run funB:', funB()) print('name of funA:', funA.__name__) print('name of funB:', funB.__name__) class ClsA: pass ClsB = ClsA print('new ClsA:', ClsA()) print('new ClsB:', ClsB()) print('name of ClsA:', ClsA.__name__) print('name of ClsB:', ClsB.__name__)
|
输出如下:
1 2 3 4 5 6 7 8 9
| $ ./fun_class_alias.py run funA: This is funA. run funB: This is funA. name of funA: funA name of funB: funA new ClsA: <__main__.ClsA object at 0x7f268e744278> new ClsB: <__main__.ClsA object at 0x7f268e744278> name of ClsA: ClsA name of ClsB: ClsA
|
结果很清晰地说明了:
- 这样是可以正常运行的
- 别名纯粹只是个别名,是对原 函数 / 类 的一个引用,不影响实际的名字。(通过
.__name__
获取实际的名字,仍然是定义时那一个。),也不会改变行为
但有一个问题,为什么两次获得的实例居然是一个地址?!
我把第二个也改为 ClsA()
,结果还是一样,说明跟别名无关。那按理说第二次实例化得到的地址必须不一样才对啊!想了一会,难道是因为内存回收?于是把代码改成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class ClsA: _cnt = 0 def __init__(self): self.__class__._cnt += 1 self.idx = self._cnt ClsB = ClsA insA = ClsA() print('new ClsA:', insA) print('idx of insA:', insA.idx) insB = ClsB() print('new ClsB:', insB) print('idx of insB:', insB.idx)
|
这次地址就不一样了:
1 2 3 4 5
| ./fun_class_alias.py new ClsA: <__main__.ClsA object at 0x7f8f729ccf98> idx of insA: 1 new ClsB: <__main__.ClsA object at 0x7f8f729d2ef0> idx of insB: 2
|
这样看来,之前只是因为匿名引用之后内存被马上回收,然后下次申请的又是同一块内存,产生看起来好像是同一个实例的错觉。但只要给他们都加一个引用,就清楚了。
其他
然后我又查了一下,并没有发现其他人讨论这个内容(当然也可能是关键字选得不对,欢迎留言打脸),到时在官方文档发现类似这样的一句话:在Windows上,WindowsError 是 OSError的别名。
于是我翻了一下Python的源码,然后又在 Windows 上测试了一下这两个Error,作为这个记录的结尾:
1 2 3 4 5 6 7 8
| INIT_ALIAS(EnvironmentError, OSError) INIT_ALIAS(IOError, OSError) #ifdef MS_WINDOWS INIT_ALIAS(WindowsError, OSError) #endif
|
INIT_ALIAS 是一个比较复杂的宏,就不展开了,顾名思义,就是定义前者作为后者的别名。
1 2 3 4 5 6
| print('OSError is:', OSError.__name__) print('EnvironmentError is:', EnvironmentError.__name__) print('IOError is:', IOError.__name__) print('WindowsError is:', WindowsError.__name__)
|
1 2 3 4 5
| > py test_WindowsError.py OSError is: OSError EnvironmentError is: OSError IOError is: OSError WindowsError is: OSError
|
本文为本人原创,采用知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议进行许可。
本作品可自由复制、传播及基于本作品进行演绎创作。如有以上需要,请留言告知,在文章开头明显位置加上署名(Jayce Chant)、原链接及许可协议信息,并明确指出修改(如有),不得用于商业用途。谢谢合作。
详情请点击查看协议具体内容。