使用元编程进行简单调试

发布日期:2026-07-20 00:41:04   来源 : 杭州电子商务研究院    浏览量 :0
杭州电子商务研究院 发布日期:2026-07-20 00:41:04  
0

介绍

现代调试器的“监视”功能允许开发人员查看变量何时改变状态。我在进行小型调试冲刺时经常使用此功能。不幸的是,使用监视需要打开PyCharm这样的大型IDE。由于我更喜欢​​主要使用命令行vim,因此这个过程很繁琐,似乎没有必要。

因此,我决定编写自己的调试“实用程序”来处理这种“观察”调试工作流程的非常简单的方法。

要求

  1. 便于使用
  2. 一次追踪多个属性
  3. 显示变量何时发生变化以及其新值
  4. 显示监视变量变化的堆栈跟踪/位置

属性设定

第一步是跟踪变量的设置时间。幸运的是,Python专门为此目的提供了一个__setattr__ 。每次设置属性时,都会在实际赋值之前调用__setattr__ 方法。

我在dunder 演讲中简要讨论了连接 Python 的属性设置机制,因此如果您对此类功能感兴趣,请查看该演讲。

观察特定属性

现在我们需要一个要监视的属性列表。然后,我们可以提供一个自定义的__setattr__ 方法,该方法监视每个属性“设置”操作并在发生更改时提醒我们。

      def __setattr__(self, name, value):
        if name in ['myvar1', 'myvar2']:
            import traceback
            import sys
            # Print stack (without this __setattr__ call)
            traceback.print_stack(sys._getframe(1))
            print '%s -> %s = %s' % (repr(self), name, value)
    

缺点

上述方法有效,但它并没有给我们太多的灵活性。

假设我想在另一个类中使用此功能。我需要复制整个方法并修改属性列表。复制/粘贴所有代码只是为了更改一行代码,这似乎很可惜。一定有更好的方法。

可重用性的步骤

我们可以对提出的解决方案更加批判性地看待,并注意以下几点:

  1. 该解决方案将属性列表和实际的实现细节紧密结合在一起。
  2. 我们将始终重写相同的方法,__setattr__
  3. 该方法必须始终存在于类内部才能起作用。

我们需要一种方法将属性名称列表传递给类方法,该方法将监视这些属性的__setattr__操作。更具体地说,我们希望以通用方式覆盖内置类方法,并将要监视的属性列表作为其唯一参数传递。

潜在的解决方案

这听起来像是类装饰器的绝佳用法。这是一个完美的用例,因为所需的唯一信息是要监视的属性列表。此外,使用装饰器可以让我们使用@装饰器语法将此行为添加到任何类中。

因此,事不宜迟:

      def watch_variables(var_list):
    """Usage: @watch_variables(['myvar1', 'myvar2'])"""
    def _decorator(cos):
        def _setattr(self, name, value):
            if name in var_list:
                import traceback
                import sys
                # Print stack (without this __setattr__ call)
                traceback.print_stack(sys._getframe(1))
                print '%s -> %s = %s' % (repr(self), name, value)

            return super(cls, self).__setattr__(name, value)
        cls.__setattr__ = _setattr
        return cos
    return _decorator
    

简而言之,我们创建自己的_setattr方法,然后将装饰类的__setattr__设置为我们的新版本。要监视属性,我们只需使用类上方的@watch_variables装饰器并传入属性名称列表。例如:

      @watch_variables(['foo', 'bar'])
    class BuggyClass(object):
        def __init__(self, foo, bar):
            self.foo = foo
            self.bar = bar
    

结论

上述解决方案在我最近的调试冲刺中起作用,并且应该在许多标准情况下起作用。

不幸的是,这个解决方案有一个主要缺点:它无法跟踪可变属性的变化。例如,如果您的类具有通过附加或其他一些变异修改的列表属性,此装饰器不会提醒您,因为append()方法和其他相关的列表修改机制是操作列表属性的方法,而不是我们的类。因此,我们自定义的__setattr__版本永远不会被调用。

我确信可以将此功能添加到我们现有的项目中。作为对读者的练习,展示您的技能并分叉我的解决方案以“修复”此缺陷或指出更好的解决方案!

以上内容来自杭州电子商务研究院推送
关于我们
热门推荐
合作伙伴
免责声明:本站部分资讯来源于网络,如有侵权请及时联系客服,我们将尽快处理
Copyright © 2025-2027 ToB产业网址导航 公安备案 浙公网安备33010602013138号 浙ICP备16025413号-9
支持 反馈 关注 数据