本文同步发布于字节谈云公众号。前言我最近在调试一段复杂的代码时遇到了一个问题。我在某处打了一个断点,以为它应该按预期运行到指定的断点,但不幸的是它没有。经过几番排查,发现了一个隐藏的“坑”。用简单代码重现为简单起见,使用以下代码重现遇到的问题:classPerson(object):def__init__(self,id):self.id=idself._person=Noneself._name=None@propertydefname(self):如果self._person为None:self._person=db_get_person(self.id)returnself._person['name']def__repr__(self):return''.format(self.name)defdb_get_person(id):return{'name':'Jack'}if__name__=='__main__':p=Person('Jack')print(p.name)这段代码定义了一个Person类型,构造函数接收一个id参数。它有一个name属性,第一次访问时会调用db_get_person()(例子直接返回)函数根据id从数据库中加载person的信息,并缓存起来,当访问时返回缓存的值稍后再次访问它。另外定义了__repr__魔术方法,打印Person对象时更加友好。我们在第18行和24行打断点开始调试,首先我们会运行到第24行。当你点击继续运行到下一个断点时,你会发现程序直接结束了,但是并没有按预期进行。为什么PyCharm没有运行到指定的断点?仔细观察运行到第24行的调试界面,Variables面板中会自动显示当前范围内的所有变量。其中有变量p,显示为,表示此时PyCharm已经自动运行了Person.__repr__,这个魔术方法调用了self.name。在第24行运行p.name时,它??不是第一次运行。看到这里就明白了,PyCharm的Variables面板中的变量会自动运行Person.__repr__。在这个阶段,如果有断点,这个方法涉及的逻辑不会被挂起,也就是不会运行到指定的断点处。在PyCharm调试界面中,除了Variables面板可以查看变量,Watches面板也可以执行任意表达式进行观察。一不小心你也可能会遇到同样的问题。例如:当断点运行到第24行时,如何通过在Watches面板中添加变量p.name来解决?明白原因后,解决办法就是防止PyCharm自动运行到特定代码。方法一:取消第24行的断点由于取消了第24行的断点,调试逻辑不会停在第24行,所以Variables面板中不会显示变量p,Person.__repr__也不会被调用。在第24行运行p.name的时候是第一次运行,自然会跑到第18行的断点。方法二:暂时注释掉Person.repr由于Person.__repr__被注释掉了,调试时停留在第24行,虽然变量p显示在Variables面板中,但是p.name不会通过Person.__repr__调用。这样也可以保证第24行运行的p.name是第一个运行,自然可以运行到第18行的断点。Variables和Watches面板中的变量已经自动执行了相应的逻辑,导致没有进入预期的断点。