使用装饰器恢复生成器

2024-04-12

让我们有一个类,它的功能有时会失败,但经过一些操作后它就可以完美地工作。

现实生活中的例子是 Mysql 查询,它会引发_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')但客户端重新连接后,它工作正常。

我尝试为此编写装饰器:

def _auto_reconnect_wrapper(func):
    ''' Tries to reconnects dead connection
    '''

    def inner(self, *args, _retry=True, **kwargs):
        try:
            return func(self, *args, **kwargs)

        except Mysql.My.OperationalError as e:
            # No retry? Rethrow
            if not _retry:
                raise

            # Handle server connection errors only
            # http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html
            if (e.code < 2000) or (e.code > 2055):
                raise

            # Reconnect
            self.connection.reconnect()

        # Retry
        return inner(self, *args, _retry=False, **kwargs)
    return inner

class A(object):
    ...

    @_auto_reconnect_wrapper
    def get_data(self):
        sql = '...'
        return self.connection.fetch_rows(sql)

如果客户端失去连接,它只是默默地重新连接,每个人都很高兴。

但如果我想转型怎么办get_data()到发电机(并使用yield陈述):

    @_auto_reconnect_wrapper
    def get_data(self):
        sql = '...'
        cursor = self.connection.execute(sql)
        for row in cursor:
            yield row

        cursor.close()

好吧,前面的例子不起作用,因为内部函数已经返回生成器,并且它会在第一次调用后中断next().

据我了解,如果 python 看到yield在方法内部它只是立即产生控制(不执行任何一条语句)并等待第一个next().

我已经设法通过替换使其工作:

return func(self, *args, **kwargs)

With:

for row in func(self, *args, **kwargs):
    yield row

但我很好奇是否有更优雅(更Pythonic)的方法来做到这一点。有没有办法让python运行所有代码到第一个yield and then wait?

我知道可以打电话return tuple(func(self, *args, **kwargs))但我想避免一次加载所有记录。


首先,我认为您当前使用的解决方案很好。当你装饰一个生成器时,装饰器至少需要像该生成器上的迭代器一样运行。通过让装饰器成为生成器来做到这一点也是完全可以的。正如 x3al 指出的那样,使用yield from func(...)代替for row in func(...): yield row是一种可能的优化。

如果您想避免实际上使装饰器也成为生成器,您可以通过使用来做到这一点next,它将运行到第一个yield,并返回第一个产生的值。除了生成器生成的其余值之外,您还需要使装饰器以某种方式捕获并返回第一个值。你可以这样做itertools.chain https://docs.python.org/3/library/itertools.html#itertools.chain:

def _auto_reconnect_wrapper(func):
    ''' Tries to reconnects dead connection
    '''

    def inner(self, *args, _retry=True, **kwargs):
        gen = func(self, *args, **kwargs)
        try:
            value = next(gen)
            return itertools.chain([value], gen)
        except StopIteration:
            return gen
        except Mysql.My.OperationalError as e:
            ...
            # Retry
            return inner(self, *args, _retry=False, **kwargs)
    return inner

您还可以使装饰器同时使用生成器和非生成器函数,使用inspect https://docs.python.org/3/library/inspect.html确定您是否正在装饰发电机:

def _auto_reconnect_wrapper(func):
    ''' Tries to reconnects dead connection
    '''

    def inner(self, *args, _retry=True, **kwargs):
        try:
            gen = func(self, *args, **kwargs)
            if inspect.isgenerator(gen):
                value = next(gen)
                return itertools.chain([value], gen)
            else: # Normal function
                return gen
        except StopIteration:
            return gen
        except Mysql.My.OperationalError as e:
            ...
            # Retry
            return inner(self, *args, _retry=False, **kwargs)
    return inner

我赞成yield/yield from基于 的解决方案,除非除了生成器之外您还需要装饰常规函数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用装饰器恢复生成器 的相关文章

随机推荐

  • NSXMLParser 可以用来解析 HTML [重复]

    这个问题在这里已经有答案了 可能的重复 使用 NSXMLParser 解析 HTML https stackoverflow com questions 8866376 using an nsxmlparser to parse html
  • Xcode:复制标头:公共、私有、项目?

    我正在构建一个 Cocoa Touch 静态库 我应该如何决定是否将头文件复制为公共 私有或项目 Public 该界面已最终确定 可供产品的客户使用 公共标头作为可读源代码包含在产品中 不受限制 Private 该界面不适用于您的客户 或者
  • std::mutex 会创建栅栏吗?

    如果我锁定一个std mutex我总会得到一个记忆栅栏吗 我不确定这是否暗示或强迫你获得栅栏 Update 根据 RMF 的评论找到了这个参考资料 多线程编程和内存可见性 http en wikipedia org wiki Memory
  • 如何深度复制混有特征的类

    这是一些示例 scala 代码 abstract class A val x Any abstract def copy A class b i Int extends A i override def copy new B x class
  • python中,什么时候可以省略self?

    下面定义的代码Duck类由以下组合而成Bill类和Tail班级 我的问题是至于方法about inside Duck类定义 为什么可以写bill description and tail length Is self这里省略了 如果是 我什
  • neo4j cypher节点之间的多重关系

    例如 a r gt b 两个节点之间存在多个r 每个r userId都是唯一的 例如 a r R userId user1 gt b a r R userId user2 gt b 对于 a r gt c 也是如此 而情况是a r gt b
  • Mojolicious REST 调用中错误的 HTTP 响应

    我使用的mojolicious应用程序是基于JSON的 即客户端和服务器之间的交互更多的是JSON结构化数据的交换 我正在尝试实现一种标准方法 当在 REST 调用期间发生错误时 使用正确的 HTTP 响应代码来处理错误 实施此类标准的最佳
  • 插入符使用插入符训练对象返回的预测与使用提取的最终模型返回的预测不同

    在拟合模型时我更喜欢使用插入符号 因为它的相对速度和预处理功能 然而 我对它如何做出预测有点困惑 当比较直接从训练对象做出的预测和从提取的最终模型做出的预测时 我看到了非常不同的数字 来自火车对象的预测似乎更准确 library caret
  • 如何执行 CompletableFuture 数组并组合其结果

    我正在研究 Java 8CompletableFutures并阅读 并看到 我应该采用thenCompose代替thenApply 我已将我的代码转换为使用thenCompose但我有一种不正确的感觉 这是我的控制代码 final Comp
  • Spark 和 AWS S3 连接错误:无法通过 Spark-shell 从 S3 位置读取文件

    在下面的 Spark shell 中 我尝试连接到 S3 并加载文件以创建数据帧 spark shell packages com databricks spark csv 2 10 1 5 0 scala gt val sqlContex
  • 哪个 ORM 支持这个

    我有一个可选的查询部分 需要在特定条件下执行 这是示例代码 int cat 1 int UserID 12 string qry select from articles if cat gt 0 qry where categoryID c
  • 如何仅在特定 API 级别上执行代码

    例如 这段代码 if Build VERSION SDK INT gt Build VERSION CODES GINGERBREAD myCalendarView setOnDateChangeListener new OnDateCha
  • 如何向 CMFCPopupMenu 添加图标?

    我想用CMFCPopupMenu用于右键单击期间的弹出菜单 如何添加图标CMFCPopupMenu 这是我在基本 MFC 应用程序中尝试的示例代码 CMFCPopupMenu TestCMFCPopMenu new CMFCPopupMen
  • str在 data.frame 中按行分割并按列分配结果

    所以我有数据框 dat data frame x c Sir Lancelot the Brave King Arthur The Black Knight The Rabbit stringsAsFactors F gt dat x 1
  • Java 8 Streams:将对象列表转换为一组对象

    我正在尝试将对象列表转换为一组对象 以确保集合中是否不存在重复项 我正在尝试使用 Streams 我有一个类产品如下 class Product int id String name float price public Product i
  • 模拟通过实例使用的类方法

    我正在尝试使用模拟修补类方法 如所述在文档中 http www voidspace org uk python mock patch html patch Mock 对象本身工作正常 但它的方法却不能 例如 它们的属性如下call coun
  • 从 Delphi 将列表导出到 OpenOffice Calc

    我正在使用 Delphi 7 我想使用自动化而不是使用文件将列表内容从我的程序导出到 OpenOffice Calc 任务很简单 创建新文档 迭代行 列并更改单元格数据 我找到了一些代码 但它并不完整 我希望有人有一些示例代码可以完成这个非
  • 将 Python 安装到自托管 Windows 构建代理

    我已经安装了 Windows 代理 并且需要能够运行 Python 脚本 我知道我需要安装Python 但我不知道如何安装 我将标准安装中的 Python 文件添加到 AGENT TOOLSDIRECTORY Python 3 8 2 x6
  • Swift 的 Facebook 登录按钮

    在 Xcode 中 如果我创建一个UIView然后将自定义类添加为FBSDKLoginButton 当我单击时 它会引导我完成 Facebook 登录 然后返回到与FBSDKLoginButton但不是说登录按钮 而是说现在注销 当单击登录
  • 使用装饰器恢复生成器

    让我们有一个类 它的功能有时会失败 但经过一些操作后它就可以完美地工作 现实生活中的例子是 Mysql 查询 它会引发 mysql exceptions OperationalError 2006 MySQL server has gone