我想创建应用转换到Python代码。例如,我希望能够转换这个:
for i in range(10): print(i)
变成这样:
i = 0 while i < 10: print(i) i += 1
有没有什么工具可以做到这一点?我试着使用Python ast库。
8yparm6h1#
你展示的那些循环并不完全等同。Python的for循环实际上是一个for-each循环,range不像其他语言那样是真正的for-i循环。让我们以您为例:
for i in range(10): print(i) i = 0 while i < 10: print(i) i += 1
看看当我们实际添加一些修改i的东西时,在for和while中会发生什么:
for i in range(10): i = 0 print(i)
它只会把0打印十次。因为我们每次都只是从range对象中获取下一个元素,所以我们不关心在循环中是否覆盖了i但是如果我们在while例子中这样做,我们会得到一个无限循环:
while
i = 0 while i < 10: i = 0 print(i) i += 1
如你所见,你所做的并不是一个真正的等价物。那么如何从for(for-each)循环中执行while循环呢?你必须理解Python中的迭代器和迭代器。For-each通过创建一个迭代器并对其调用next来从可迭代对象中获取每个元素。引发StopIteration时循环停止。简单示例:
for
next
for elem in iterable: ... # is something along the lines of my_iter = iter(iterable) try: while elem := next(my_iter): ... except StopIteration: pass # no more elements
如果任何嵌套的循环也有自己的except(这样你就不会过早地从外部循环退出),这应该是可行的!).在真实的Python网站-https://realpython.com/python-for-loop/上有一些关于迭代器、可迭代对象以及Python的for循环如何工作的解释。
tez616oj2#
你可以使用迭代器和next()函数来转换它。举例来说:
iLoop = iter(range(10)) while True: i = next(iLoop,iLoop) if i is iLoop: break ...
这将涵盖大多数情况,但仍然会有一个问题的else:部分的for循环。如果迭代没有被break中断,带有else:组件的for循环将只执行else部分。在这种情况下,循环将始终被break中断。或者,您可以编写一个小型类来管理迭代和当前值:
else:
break
class ForLoop: def __init__(self,data): self.idata = iter(data) self._value = None @property def value(self): return self._value def __call__(self): self._value = next(self.idata,self) return self._value is not self
这将具有for循环的所有特性和行为,包括处理continue,break,i变量的(重新)赋值,并且不吸收任何异常。产出:
continue
i
iLoop = ForLoop(range(10)) while iLoop(): i = iLoop.value print(i) 0 1 2 3 4 5 6 7 8 9
虽然这是一个有趣的思想实验,但将一种结构改变为另一种结构似乎有点徒劳,效率较低。也许你可以在你的问题中提出你的目标。
wvmv3b1j3#
是的,我知道至少libcst可以做到这一点,它对于大规模代码重构非常有效:
libcst
下面是一个简单的例子,硬编码:你需要改变一些东西来使它更通用,但它给出了很好的画面。
from libcst import ( IndentedBlock, SimpleStatementLine, Name, FlattenSentinel, RemovalSentinel, BaseStatement, For, While, Comparison, ComparisonTarget, LessThan, Integer, AugAssign, AddAssign, CSTTransformer, parse_module, Assign, AssignTarget) class LoopUpdater(CSTTransformer): def leave_For( self, original_node: For, updated_node: For ) -> BaseStatement | FlattenSentinel[BaseStatement] | RemovalSentinel: """Transform a for-loop into a hardcoded while-loop""" # Hardcoded iteration init (i = 0) init_i = Assign(targets=[AssignTarget(target=Name(value='i'))], value=Integer(value='0')) # Hardcoded while condition (i < 10) i_under_10 = Comparison(left=Name(value='i'), comparisons=[ComparisonTarget( operator=LessThan(), comparator=Integer(value='10'))]) # Hardcoded iteration block (i += 1) increment_i = SimpleStatementLine(body=[ AugAssign(target=Name(value='i'), operator=AddAssign(), value=Integer(value='1'))]) return FlattenSentinel([ SimpleStatementLine(body=[init_i]), While(test=i_under_10, body=IndentedBlock( body=list(updated_node.body.body) + [increment_i])) ]) test = r''' for i in range(10): print(i) ''' cst = parse_module(test) updated_cst = cst.visit(LoopUpdater()) print(updated_cst.code)
输出量:
3条答案
按热度按时间8yparm6h1#
你展示的那些循环并不完全等同。Python的for循环实际上是一个for-each循环,range不像其他语言那样是真正的for-i循环。
让我们以您为例:
看看当我们实际添加一些修改i的东西时,在for和while中会发生什么:
它只会把0打印十次。因为我们每次都只是从range对象中获取下一个元素,所以我们不关心在循环中是否覆盖了i
但是如果我们在
while
例子中这样做,我们会得到一个无限循环:如你所见,你所做的并不是一个真正的等价物。
那么如何从
for
(for-each)循环中执行while
循环呢?你必须理解Python中的迭代器和迭代器。For-each通过创建一个迭代器并对其调用next
来从可迭代对象中获取每个元素。引发StopIteration时循环停止。简单示例:
如果任何嵌套的循环也有自己的except(这样你就不会过早地从外部循环退出),这应该是可行的!).
在真实的Python网站-https://realpython.com/python-for-loop/上有一些关于迭代器、可迭代对象以及Python的for循环如何工作的解释。
tez616oj2#
你可以使用迭代器和next()函数来转换它。
举例来说:
这将涵盖大多数情况,但仍然会有一个问题的
else:
部分的for循环。如果迭代没有被break
中断,带有else:
组件的for循环将只执行else部分。在这种情况下,循环将始终被break中断。或者,您可以编写一个小型类来管理迭代和当前值:
这将具有for循环的所有特性和行为,包括处理
continue
,break
,i
变量的(重新)赋值,并且不吸收任何异常。产出:
虽然这是一个有趣的思想实验,但将一种结构改变为另一种结构似乎有点徒劳,效率较低。也许你可以在你的问题中提出你的目标。
wvmv3b1j3#
有没有什么工具可以做到这一点?
是的,我知道至少
libcst
可以做到这一点,它对于大规模代码重构非常有效:代码示例
下面是一个简单的例子,硬编码:你需要改变一些东西来使它更通用,但它给出了很好的画面。
输出量: