前两天有个一万多行的C++项目需要转PYTHON,逻辑非常复杂,要想看懂逻辑再用PYTHON写一遍预期时间比直接转要长,所以大家决定转PYTHON,遇到一些坑,这里简记一下。
最大的坑是数据结构,PYTHON没有数组,只能用LIST,C++的函数里数组直接传地址进去,可以进行各种操作,尤其是二维数组。PYTHON只能用LIST,二维数组只能嵌套LIST。C++可以传一个类对象的引用进一个函数。PYTHON得把这个类对象传进去,改变以后再传出来外面还得再=一下。其实PYTHON也可以传引用。传引用进一个函数注意事项:
1、不能在函数内部使用param = []或者param = list_other
2、不能在调用处传param + 3之类的
出现第2种情况时,可以使用一个零时arg_param = param[3:]使用arg_param传入然后函数调用出来以后再循环把值赋到原来的parm里。当然如果是const最好了,返回以后外面就不用再赋值了。
小技巧:第一步:先把C++代码翻译成“类似PYTHON的C++"中间代码。第二步:把这种中间代码翻译成PYTHON。第一步走完以后可以进行一次逻辑回测,看是否还能正常工作。
小技巧:第一步中尽量把传的数组能CONST的统统CONST掉,这其实是C++的编辑习惯问题,尽量养成能CONST的(无论传指针还是引用)都CONST。这样PYTHON就可以非常放心的直接传引用进去,里面不改变即可。
小技巧:CopyMemory拷贝数组,统统改成:
template<class T>
static void CopyMemory_py(T dest[], const T from[], int count, int offset1 = 0, int offset2 = 0)
{
for (int nIdx = 0;nIdx < count;++nIdx)
{
dest[offset1 + nIdx] = from[offset2 + nIdx];
}
}
def CopyMemory_py(dest, src, count, offset1 = 0, offset2 = 0):
for nIdx in range(count):
dest[offset1 + nIdx] = src[offset2 + nIdx]
当然了,这里严格要求两个类型是一样的,如果不一样可以加个模板参数。
小技巧:所有的数组,在定义的时候,直接把它初始化成固定长度。
def constructArray1(array, count):
for i in range(count):
array.append(0)
def constructArray2(array, count1, count2):
for i in range(count1):
tmparray = []
for j in range(count2):
tmparray.append(0)
array.append(tmparray)
小技巧:C++的POD调用memset置空的,统统在里面封一个clear()函数,把里面的东西置空。完了C++ PYTHON分别实现一下clear()
小技巧:封掉c++ POD 的 copy construtor、assign construtor,直接private掉。或者检查一下看有没有用到的
private:
tagStackHandCardInfo& operator=(const tagStackHandCardInfo& rhs);
tagStackHandCardInfo(const tagStackHandCardInfo& rhs);
小技巧:拷贝数组,封成函数
// 兼容python,传两个引用进来,一个赋值给另一个
static void tagOutCardTypeResult_copy(tagOutCardTypeResult& lhs, const tagOutCardTypeResult& rhs)
{
lhs.cbCardType = rhs.cbCardType;
lhs.cbCardTypeCount = rhs.cbCardTypeCount;
for (int nIdx = 0;nIdx < MAX_TYPE_COUNT;++nIdx)
{
lhs.cbEachHandCardCount[nIdx] = rhs.cbEachHandCardCount[nIdx];
}
for (int i = 0;i < MAX_TYPE_COUNT;++i)
{
for (int j = 0;j < MAX_COUNT;++j)
{
lhs.cbCardData[i][j] = rhs.cbCardData[i][j];
}
}
}
def tagOutCardTypeResult_copy(lhs, rhs):
lhs.cbCardType = rhs.cbCardType
lhs.cbCardTypeCount = rhs.cbCardTypeCount
for nIdx in range(MAX_TYPE_COUNT):
lhs.cbEachHandCardCount[nIdx] = rhs.cbEachHandCardCount[nIdx]
for i in range(MAX_TYPE_COUNT):
for j in range(MAX_COUNT):
lhs.cbCardData[i][j] = rhs.cbCardData[i][j]
小技巧:python中的类中的函数调用,统统要加上self.,可以三步走:step1,文本替换"FuncName"=>"self.FuncName"。step2,文本替换"self.self.FuncName"=>"self.FuncName"(这一步其实是走到中间我才想到的,前面有些函数已经写成了self.FuncName,经过第一步后会变百self.self.FuncName,所以加上这一步容错)。step3,文本替换"def self.FuncName"=>"def FuncName"
小技巧:在全部工作开始之前,把所有的函数调用关系理一下。这里我想到一个比较笨的办法,用OFFICE PROJECT把每一个函数定义为一个任务,把该函数调用的其他函数设置为它的前置任务,这样就可以顺利的得到一个翻译的顺序图:
再根据这个图,得到函数的翻译顺序:
GetCardType
SortCardList
RandCardList
RemoveCard
IsValidCard
GetCardLogicValue
CompareCard
MakeCardData
AnalysebCardData
AnalysebDistributing
SearchOutCardA
SearchOutCardB
GetAllBomCard
GetAllLineCard
GetAllThreeCard
GetAllDoubleCard
GetAllSingleCard
AnalyseOutCardTypeA
AnalyseOutCardTypeB
Combination
Permutation
AnalyseSinleCardCount
SetUserCard
SetBackCard
SetLandScoreCardData
RemoveUserCardData
BankerOutCardA
BankerOutCardB
UpsideOfBankerOutCardA
UpsideOfBankerOutCardB
UndersideOfBankerOutCardA
UndersideOfBankerOutCardB
LandScore
L2C
_TestOutAllCard
AnalyseFourCardType
TestOutAllCard
IsLargestCard
VerifyOutCard
从左到右,就是翻译顺序。
小技巧:C++程序员有时候喜欢界定局部变量,或者只是为了逻辑清晰,使用两个花括号括起来写一段代码,python直接使用 if True:来保证对齐
小技巧:如果你准备翻译的C++代码不是非常整洁,可以使用VISUAL STUDIO格式化一下 CTRL A全选,CTRK K F即可。这一步可以省掉很多事情,因为PYTHON对齐是需要非常小心的。典型的就是类似这样的c++代码:
for(int nIdx = 0;nIdx < nCount;++nIdx)
do_something(some_param1, some_param2);
do_someotherthing(some_param3, some_param4);
如果只有这两行相信正常人一眼就能看见,但是如果下面的拖的非常长,又或者
do_something
函数前面后面跟了花括号,里面东西又非常长,这时候后面的代码是极不容易被发现对齐问题的。
小技巧:在递归函数或者是需要注意性能的函数中,数组可以加上偏移来实现c++ 中类似array + 3这样的用法的python版:
//原函数:
void Func(int array[])
{
if (...)
Func(array + 3);
......
}
//新函数
void Func(int array[], int offset = 0)
{
if (...)
Func(array, offset + 3);
}