如何在Python中自动将for替换为while循环?

pjngdqdw  于 2023-10-14  发布在  Python
关注(0)|答案(3)|浏览(109)

我想创建应用转换到Python代码。
例如,我希望能够转换这个:

for i in range(10):
   print(i)

变成这样:

i = 0
while i < 10:
   print(i)
   i += 1

有没有什么工具可以做到这一点?
我试着使用Python ast库。

8yparm6h

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例子中这样做,我们会得到一个无限循环:

i = 0 
while i < 10: 
    i = 0
    print(i) 
    i += 1

如你所见,你所做的并不是一个真正的等价物。
那么如何从for(for-each)循环中执行while循环呢?你必须理解Python中的迭代器和迭代器。For-each通过创建一个迭代器并对其调用next来从可迭代对象中获取每个元素。引发StopIteration时循环停止。
简单示例:

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循环如何工作的解释。

tez616oj

tez616oj2#

你可以使用迭代器和next()函数来转换它。
举例来说:

iLoop = iter(range(10))
while True:
   i = next(iLoop,iLoop) 
   if i is iLoop: break
   ...

这将涵盖大多数情况,但仍然会有一个问题的else:部分的for循环。如果迭代没有被break中断,带有else:组件的for循环将只执行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循环的所有特性和行为,包括处理continuebreaki变量的(重新)赋值,并且不吸收任何异常。
产出:

iLoop = ForLoop(range(10))
while iLoop():
    i = iLoop.value
    print(i)

0
1
2
3
4
5
6
7
8
9

虽然这是一个有趣的思想实验,但将一种结构改变为另一种结构似乎有点徒劳,效率较低。也许你可以在你的问题中提出你的目标。

wvmv3b1j

wvmv3b1j3#

  • blah blah你可能不应该把for循环改为while循环blah blah更容易出错blah blah可读性更差blah blah关于同样的性能blah blah。*

有没有什么工具可以做到这一点?

是的,我知道至少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)

输出量:

i = 0
while i < 10:
    print(i)
    i += 1

相关问题