Pandas1.x
弃用了Pandas 0.25.x
中的某些函数。随着库的升级,这些代码都要修改。但是公司的代码有上百万行!怎么应付?
ast
在某些场合中可以是开发的得力工具,就比如在代码移植及代码质量评估中。在python2.x 到 python3.x的代码转换中,亦或是Pandas 0.25.x
到 Pandas 1.x
的代码转化。Pandas1.x
弃用了 Pandas 0.25.x
中的某些函数,因此在代码转换过程中需要修改这部分的函数调用方式。ast
使得我们可以忽视代码中的注释、空格等信息,而直接关注于代码本身。
AST基础
Python 有库ast
支持生成代码的AST:
1 | import ast |
ast
库提供了一个dump
方法返回以节点为根的整颗树(格式化之后)
1 | print(ast.dump(head)) |
可以看到head
节点是Module
类型,它有一个属性body
,其值为一个包含两个节点的list
。一个代表a = 2
,另一个代表 print(a)
。第一个节点有一个targets
属性表示左侧(LHS)的a
以及一个value
属性代表右侧(RHS)的1。
试试将右侧的value的n属性替换为2
1 | head.body[0].value.n = 2 |
可以看到其值被更改为了2,现在将AST转换回代码
1 | import astunparse |
可以看到代码中的1被替换为了2.
一个案例
在Pandas1.0.0
中,多维索引MultiIndex
的 name
属性不再支持以=
的方式更新,改为使用index.set_names()
进行更新。
1 | #pandas=0.25.x |
上面的第7行代码在Pandas=1.0.0
中应该改为
1 | mi = mi.set_names("new name", level=0) |
该怎么使用程序完成mi.levels[0].name = "new name"
到mi = mi.set_names("new name", level=0)
的转换呢?
借助AST可以达到目的。转换算法如下
- 构造源代码的AST,并且遍历树
- 识别该节点是否代表如下形式的代码:
<var>.levels[<idx>].name=<val>
. - 如果代表,则对该节点进行替换,替换为如下形式:
<var>=<var>.set_names(<val>, level=<idx>)
.
转换代码
首先来看看源代码和目标代码的AST形式
1 | # src code |
经过前述分析,结合转换算法,有如下的转换代码
1 | import ast |
结果
1 | print(f"previous code: {prev_code}") |
可以看到,借由AST我们可以成功地将之前版本的代码
mi.levels[0].name = "new name"
转换到了最新版本的代码
mi = mi.set_names('new name', level=0)
。