【Python】实现限时循环的学习笔记

本文探讨了如何在Python中实现一个名为`whileLimited_time`的限时循环,确保在指定时间内启动程序,超时则抛出错误。讨论了不同实现方式,如自定义上下文管理器、生成器函数和单例模式,以及可能遇到的问题和解决方案。
摘要由CSDN通过智能技术生成

1 问题描述

今天我们在实现Emutouch时,有这样一个需求:我们希望在指定的时间内启动一个程序,如果超时就报错,类似于一种 “限时循环”的效果,理想中的示例代码如下,

while Limited_time(10): # if timeout,`Limited_time` would throw an timeout error from inside
    # Here is the logic of checking if the program has started
    if start_success:
       break

2 初步实现 [timeplus]

3 需求描述

3.1 循环内部可能出现“超时问题”,但这并不是 while-max_time 需要负责的

这里我们假设 while-max_time 遵循跟while相同的用户逻辑:也就是 while-max_time 并不负责循环体内部的超时,而只是类似于“检查点”一样的机制,只是确认在循环条件这个代码行执行点上是否超时;

南溪:这也是我们和用户约定和需要遵循的使用规范。

换句话说,while-max_time 无法处理内部出现的“超时问题”,而仅能通过检查点的方式检查循环操作已经是否消耗了过多的时间
更具体来说,对于《天哥CodeReview》中给出的例子(在循环中出现time.sleep(10)),while-max_time 无法进行处理,因为在它在设计上并不负责处理循环体内部逻辑引起的超时情况;

4 常用代码

4.1 可参考的堆栈属性

属性代码
堆栈列表inspect.stack()
当前帧inspect.currentframe()
当前帧的上一帧inspect.currentframe().f_back
调用文件名frame.f_code.co_filename

4.2 从stack中遍历相关信息

for info in stack:
    identifier += [info.filename, info.lineno, info.positions.col_offset]

3 Code Review 学习笔记

3.1 来自天哥的 Code Review [bilibili]

在这里插入图片描述

(1)在循环内部出现“大超时”情况,while-max_time 无法检测

4 学习咨询

4.1 GPT-4-32k

南溪:您好,我们今天在编程时遇到一个小问题,我们希望用循环来检查一个程序是否已经启动,并给定一定的时间限制,如果超时就报错,也就是说,是否可以使用python实现一种“限时循环”的效果,期望效果的示例代码如下,

while Limited_time(10): # if timeout,`Limited_time` would throw an timeout error from inside
    # Here is the logic of checking if the program has started
    if start_success:
       break

GPT-4-32k:你可以通过自定义一个带有超时功能的上下文管理器或者使用 time 模块来实现限时循环的效果。下面是一个简单的实现示例:

import time

class LimitedTime:
    def __init__(self, timeout):
        self.timeout = timeout
        self.start_time = None

    def __enter__(self):
        self.start_time = time.time()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def is_timeout(self):
        return time.time() - self.start_time > self.timeout

# 使用示例
try:
    with LimitedTime(10) as timer:
        while True:
            if timer.is_timeout():
                raise TimeoutError("Timeout waiting for the program to start.")
            
            # 这里是检查程序是否已经启动的逻辑
            start_success = False # 假设这里是检查程序是否启动的结果
            if start_success:
                print("Program started successfully.")
                break
            time.sleep(1)  # 避免CPU过度占用
except TimeoutError as e:
    print(e)

在上面的代码中,LimitedTime 类定义了一个上下文管理器,它记录了开始的时间,并在每次循环中检查是否超出了设定的时间限制。如果超时,它会抛出一个 TimeoutError
请注意,在实际的代码实现中,你需要替换 # 这里是检查程序是否已经启动的逻辑 部分为你具体的逻辑代码,并根据实际情况设置合理的检查间隔(这里使用了 time.sleep(1) 作为示例)。
另外,TimeoutError 是一个内置的异常类型,可用于这种情况。如果你的环境中没有 TimeoutError,可以使用 Exception 或者自定义一个异常类型。

南溪:我感觉你给出的代码还是有点复杂,因为try:with limited_time(10): 会增加两行的缩进,是否可以实现成

while Limited_time(10): # if timeout,`Limited_time` would throw an timeout error from inside
    # Here is the logic of checking if the program has started
    if start_success:
       break

这种简单的形式呢?

GPT-4-32k:要实现类似于这样的简洁语法结构,可以定义一个生成器函数,它会在超时时抛出一个异常。请看下面的示例:

import time

class TimeoutError(Exception):
    pass

def limited_time(timeout):
    start_time = time.time()
    while True:
        if time.time() - start_time > timeout:
            raise TimeoutError("Operation timed out.")
        yield

# 使用示例
try:
    for _ in limited_time(10):
        # 这里是检查程序是否已经启动的逻辑
        start_success = False  # 假设这里是检查程序是否启动的结果
        if start_success:
            print("Program started successfully.")
            break
        time.sleep(1)  # 避免CPU过度占用
except TimeoutError as e:
    print(e)

在这个例子中,limited_time 函数是一个生成器,它会一直运行,直到超出指定的超时时间。在每次迭代时,它会检查是否已超时,一旦超时,则抛出TimeoutError异常。使用这个生成器的for循环就能够实现类似于你想要的“限时循环”的效果。

这种方式减少了代码的缩进层级,使得结构更为简洁。此外,limited_time生成器函数的设计使得它在其他地方也能复用,增加了代码的可维护性。

南溪:可以看到这里的代码跟我们期望的还是不一样,这里是用for循环实现的。

南溪:我感觉你给出的代码还是有点复杂,我期望将这样的逻辑封装成一个Limited_time()的结构来直接调用,这里的“for _ in limited_time(10):”是否可以直接简化成“while Limited_time(10):”这样呢?

GPT-4-32k:为了实现 while Limited_time(10): 这样的简洁结构,我们可以定义一个函数,它返回一个可以在 while 循环中使用的对象。这个对象需要有一个方法来不断检查是否超时。这里,我们可以使用一个迭代器来实现这个效果。

以下是一个可能的实现:

import time

class TimeoutError(Exception):
    pass

class Limited_time:
    def __init__(self, timeout):
        self.timeout = timeout
        self.start_time = time.time()

    def __iter__(self):
        return self

    def __next__(self):
        if time.time() - self.start_time > self.timeout:
            raise TimeoutError("Operation timed out.")
        else:
            return True

# 使用示例
try:
    while next(Limited_time(10)):
        # 这里是检查程序是否已经启动的逻辑
        start_success = False  # 假设这里是检查程序是否启动的结果
        if start_success:
            print("Program started successfully.")
            break
        time.sleep(1)  # 避免CPU过度占用
except TimeoutError as e:
    print(e)

在这个实现当中,Limited_time 类实例化时传入超时时间,它实现了 __iter____next__ 方法,使得它可以在 while 循环中使用。每次循环时,__next__ 方法会被调用,如果超时则抛出 TimeoutError 异常,否则继续循环。

尽管这种方法实现了 while Limited_time(timeout): 这样的语法,它仍然需要使用 try/except 块来捕获异常,因为这是处理超时错误的推荐方式。如果你不想捕获异常,你可以修改 __next__ 方法来返回一个布尔值而不是抛出异常,但这将改变你最初的需求(即在超时时抛出一个错误)。

南溪:
这里的代码明显是错误的,循环体while next(Limited_time(10)):每次都会生成一个新的对象,导致程序陷入死循环。

接下来我们咨询一下Claude教授。

2.2 Claude

南溪:您好,我们今天在编程时遇到一个小问题,我们希望用循环来检查一个程序是否已经启动,并给定一定的时间限制,如果超时就报错,也就是说,是否可以使用python实现一种“限时循环”的效果,期望效果的示例代码如下,

while Limited_time(10): # if timeout,`Limited_time` would throw an timeout error from inside
    # Here is the logic of checking if the program has started
    if start_success:
       break

Claude:您好,您提到的"限时循环"可以使用Python的time模块来实现。具体做法是:

import time

timeout = 10 # seconds
starttime = time.time()
while True:
  if time.time() - starttime > timeout:
    raise TimeoutError("Timeout!")

  # 检查程序是否启动的逻辑
  if start_success: 
    break

  time.sleep(0.1)

主要思路是:

  1. 设置一个超时时间timeout
  2. 在循环开始时记录当前时间starttime
  3. 在循环内部判断当前时间与starttime的时间差是否大于超时时间
  4. 如果超时就抛出TimeoutError异常
  5. 否则就执行正常的检查逻辑,如果程序启动就break退出循环

这里time.sleep可以添加一点延时,避免循环太快消耗CPU。

这个基本思路可以实现Python中的限时循环效果,请您参考是否符合需求。如果还有其他问题,欢迎继续讨论。

南溪:你在代码中使用了“while True:”,我感觉这种写法不是很优雅,能不能写成

while Limited_time(10): # if timeout,`Limited_time` would throw an timeout error from inside
    # Here is the logic of checking if the program has started
    if start_success:
       break

我们期望的这种形式呢

Claude:您的意思是希望实现一个Limited_time的函数,来封装这个超时逻辑对吧?可以这样实现:

import time

def Limited_time(timeout):
  starttime = time.time()  
  while True:
    if time.time() - starttime > timeout:
      raise TimeoutError("Timeout!")
    yield

# 使用
try:
  while Limited_time(10):
    # 检查程序启动逻辑
    if start_success:
      break
except TimeoutError as e:
  print(e) # 打印超时错误信息

我们实现一个Limited_time生成器函数,内部实现和之前类似,增加超时判断逻辑。

在外部通过while循环调用它,这样可以通过except捕获超时错误。

yield语句让该函数成为一个生成器,遍历它时内部的while True逻辑会执行。

这样就实现了您期望的形式,封装了超时逻辑,外部用while循环更简洁。请您检查下是否符合要求,如果还有其他问题欢迎继续讨论。

南溪:我感觉Claude给出的代码似乎有问题,我再询问一下;

南溪:你给出的def Limited_time(timeout)是一个“迭代器函数”吗?

Claude:对,我实现的Limited_time是一个生成器函数,它是一个迭代器。

生成器函数有以下几个特点:

  1. 包含yield语句
  2. 当调用时不会执行函数体代码,而是返回一个生成器对象
  3. 在遍历生成器对象时才会执行函数体内的代码
  4. 每次执行到yield时会暂停,并将yield后的值作为本次迭代的值
  5. 可以通过for循环遍历,也可以通过next()函数获取下一个迭代值

所以在这个例子中,Limited_time是一个生成器函数,包含yield语句。

在外部调用它时,不会执行while True循环,而是返回一个生成器对象。

在for或while循环遍历这个生成器对象时,才会进入Limited_time函数体,并执行while True循环。

每次执行到yield语句就会暂停并返回,实现了迭代的效果。

这就是生成器函数的关键特点,它实现了延迟执行和迭代效果。

所以您认识正确,Limited_time是一个迭代器函数/生成器函数,利用其迭代效果来实现了限时循环逻辑。

南溪:你给出的代码可能是错误的,因为“while Limited_time(10):”在每次while的循环检查时,都会调用新的函数,并没有发挥“迭代器”的作用,于是会导致程序陷入死循环

Claude:您的观点非常正确,我给出的代码的确有问题,会导致重复调用Limited_time而不是利用其迭代器特性。

我重新考虑了一下,应该这样改进:

import time

def Limited_time(timeout):
  # 代码同上

timer = Limited_time(10) # 提前创建迭代器

try:
  while True: 
    start_time = next(timer) # 触发迭代器进到函数内部执行
    
    # 检查程序启动逻辑
    if start_success:
      break
      
except StopIteration:
  print("Timeout!") # 迭代器完成时触发StopIteration异常

关键点是:

  1. 提前创建迭代器对象timer,而不是在while内调用
  2. 在while内使用next(timer)触发迭代器进入函数内部执行
  3. 最后用except StopIteration捕获迭代完成的异常

这样就可以正确利用Limited_time的迭代器特性,避免重复调用产生死循环。

非常感谢您指出我示例代码的错误,帮我改进了解决方案。这确实是一个迭代器应用需要注意的地方。如有其他问题,欢迎继续讨论!

南溪:
给出的代码还是使用的while True:,并没有很好地实现我们的要求。

接下来我们咨询一下讯飞星火教授。

2.3 讯飞星火

南溪:您好,我们今天在编程时遇到一个小问题,我们希望用循环来检查一个程序是否已经启动,并给定一定的时间限制,如果超时就报错,也就是说,是否可以使用python实现一种“限时循环”的效果,期望效果的示例代码如下,

while Limited_time(10): # if timeout,`Limited_time` would throw an timeout error from inside
    # Here is the logic of checking if the program has started
    if start_success:
       break

讯飞星火:可以使用Python的time模块实现这个功能。首先,我们需要记录循环开始的时间,然后在循环中检查当前时间与开始时间的差值是否超过了给定的时间限制。如果超过了时间限制,就抛出一个异常。示例代码如下:

import time

class TimeoutError(Exception):
    pass

def Limited_time(timeout):
    start_time = time.time()
    while True:
        current_time = time.time()
        if current_time - start_time > timeout:
            raise TimeoutError("Timeout")
        # 这里是检查程序是否启动的逻辑
        if start_success():
            break

try:
    Limited_time(10)
except TimeoutError as e:
    print(e)

南溪:我感觉你给出的代码似乎跟我们期望的示例不太一样,在你给出的代码,你将“检查程序是否启动的逻辑”放在了“Limited_time”里面,而我们期望的用法中,“检查程序启动的逻辑”应该是在同一个上下文中

讯飞星火:对不起,我理解错了你的需求。你可以将检查程序是否启动的逻辑放在循环外,然后在循环中只进行超时判断。示例代码如下:

import time

class TimeoutError(Exception):
    pass

def Limited_time(timeout):
    start_time = time.time()
    while True:
        current_time = time.time()
        if current_time - start_time > timeout:
            raise TimeoutError("Timeout")
        # 这里是检查程序是否启动的逻辑
        if not start_success():
            break

try:
    Limited_time(10)
except TimeoutError as e:
    print(e)

南溪:
给出的代码没有实现我们的要求,星火将“检查程序启动的逻辑”放在了Limited_time内部。

南溪:你这次给出的代码似乎就是仅仅多加了一个“not”,是吗?

讯飞星火:是的,我添加了“not”关键字来反转start_success()的结果。这样,当start_success()返回False时,循环将继续执行。

南溪:
我感觉星火教授的回答很奇怪,跟我的问题似乎没有什么关联。

3 后记

我们感觉这个简单的API应该是可以实现的,可以想到的是:使用单例模式(将LimitedTime实现成一个单例class)加上记录函数的调用栈信息,来有效地区分和判别当前循环,来判断当前循环的执行是否超时,以实现上述这种简单的调用形式;

4 实验笔记

  1. 如果仅使用 thread_id 作为标识符,在同一上下文中使用多个 while-max-time 循环时,可能会出现问题
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值