共计 1426 个字符,预计需要花费 4 分钟才能阅读完成。
前言
最近调试一段简单代码的时候遇到一个问题,我在某处打了断点,并认为依照预期应该会运行到指定的断点,但遗憾的是并没有。几经排查,发现了一处暗藏的“坑”。
用简略的代码复现
简略起见,用上面这段代码来复现遇到的问题:
class Person(object):
def __init__(self, id):
self.id = id
self._person = None
self._name = None
@property
def name(self):
if self._person is None:
self._person = db_get_person(self.id)
return self._person['name']
def __repr__(self):
return '<Person: {}>'.format(self.name)
def db_get_person(id):
return {'name': 'Jack'}
if __name__ == '__main__':
p = Person('Jack')
print(p.name)
这段代码定义了一个 Person
类型,构造函数接管 id
参数。它有一个 name
属性,在首次拜访时会调用 db_get_person()
(示例就间接返回了)函数从数据库依据 id
加载这个人的信息,并将之缓存,后续再拜访时就返回缓存值。另外,还定义了 __repr__
魔法办法,为了在打印 Person
对象时可能更加敌对。
让咱们在第 18
、24
行打上断点并开始调试,首先会运行到第 24
行。当点击持续运行下一个断点时,会发现程序间接运行完结了,而没有如预期进行。
为什么 PyCharm 没有运行到指定断点?
认真看下运行到第 24
行的调试界面,在 Variables 面板中会主动显示出以后作用域的所有变量。其中就有变量 p
,而它显示为了 <Person: Jack>
,这阐明此时 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 行时,只管 Variables 面板显示了变量 p
,并不会通过 Person.__repr__
调用 p.name
。这样,也可能确保运行第 24 行的 p.name
是首次运行,天然就能运行到第 18 行断点。
总结
应用 PyCharm 调试具备缓存逻辑的代码时,要留神 Variables 和 Watches 面板中的变量是否已被主动执行对应的逻辑,从而导致没有进入到预期的断点。