目录
1、引发turtle.Terminator的现象
2、现象总结
3、解决turtle.Terminator
3.1 一种自定义的解决方法
3.2 Terminator异常的深入研究
4、turtle.Terminator总结
在用turtle绘图的时候,遇到一个窗口隐藏与显示的问题:
>>> import turtle
>>> turtle.fd(90) #将自动创建窗口
>>> turtle.bye() #用语句关闭窗口
>>> turtle.home() #此时有异常
Traceback (most recent call last):
File "
turtle.home()
File "
turtle.Terminator
>>> turtle.home() #这个时候是可以正常显示新窗口的
>>>#关闭窗口
还有几种类似的情形(接着上面输入):
>>> p=turtle.Turtle() #有异常产生,但此时将创建窗口
Traceback (most recent call last):
…
turtle.Terminator
>>> p.fd(90)
Traceback (most recent call last):
File "
p.fd(90)
NameError: name 'p' is not defined
>>> turtle.fd(90) #正常绘制
#######################################
>>>s=turtle.Screen() #关闭后再新建窗口,正常
>>> turtle.fd(90) #抛出异常,但再次调用时正常,类似上面的home
>>> s=turtle.Screen() #关闭后再新建窗口,正常
>>> p=turtle.Turtle() #抛出异常,但再次调用时正常,类似上面的home
>>> s=turtle.setup(600,400,0,0) #关闭后再新建窗口,抛出异常,再次调用时正常,类似上面的home
Traceback (most recent call last):
File "
s=turtle.setup(600,400,0,0)
File "
turtle.Terminator
通过上面一系列的探索,我们可以对产生turtle.Terminator异常的现象做一个总结:
首先,创建/出现窗口的方式有三种:导入turtle后直接绘制图形时自动创建;用Screen创建;用setup创建;
其次,区分画笔turtle与窗口TurtleScreen/Screen。
在窗口存在/运行期间显式或隐式创建的画笔turtle,将在窗口关闭后失效,直接使用会抛出turtle.Terminator异常,即便已重新创建窗口。但是,当接着再次使用(隐式或显示创建的)时可以正常绘制。
当窗口关闭后,Screen可以直接创建窗口,但是会抛出异常;而setup只抛出异常。
我在二叉搜索树中呈现隐藏/显示窗口时:通过turtle.bye()这个方法关闭窗口,并通过二次调用turtle.home()创建窗口:
try:
turtle.home()
except:
turtle.home()
有没有更好的解决办法?如果能找到抛出turtle.Terminator异常的根本原因不是更好吗?在请教大神如何查看某个方法的源码后,我决定看看源代码。
这是通过帮助文档的模块索引找到turtle模块源代码存放位置,另外一种方法是:
>>> turtle.__file__
'C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Anaconda3_64\\lib\\turtle.py'
>>>
打开turtle.py后,查找“Terminator”,结果:
class Terminator (Exception):
"""Will be raised in TurtleScreen.update, if _RUNNING becomes False.
This stops execution of a turtle graphics script.
Main purpose: use in the Demo-Viewer turtle.Demo.py.
"""
pass
这和想象的并不一样,竟然只是个“异常分类标记”,一个空的异常类。不过这和前面的异常提示是一致的,如:
Traceback (most recent call last):
File "
s=turtle.setup(600,400,0,0)
File "
turtle.Terminator #这里只有一个异常类型
那么,是什么原因会导致抛出Terminator异常呢?“Will be raised in TurtleScreen.update, if _RUNNING becomes False”这里已经说的很明显:当TurtleScreen.update方法中检测到_RUNNING为False的时候将抛出Terminator异常。不妨先查找“raise Terminator”。
图__func_body
图update
turtle.py中包含“raise Terminator”的地方就只有这两个。
3.2.1 __func_body和_make_global_funcs
图__func_body给出的是一个函数构造模板,主要用于将对象方法变为普通函数,通用处理过程为:
首先判断对象obj是否为空,如果为空,则判断当前是否有窗口运行,若没有则将_RUNNING置为True,并抛出Terminator异常;若有窗口在运行,则对obj初始化,并返回obj的同名同参数方法。返回时若出现异常,则判断当前是否有窗口运行,若没有则将_RUNNING置为True,并抛出Terminator异常;
_func_body只出现在下面这个地方:
从中可以看出,_Screen和Turtle这两个类的方法(两个列表中列出的,如果有参数的话)都经过__func_body和_make_global_funcs的调用后构造出相应的全局空间中的函数。但是这个“全局空间”只是turtle.py的全局空间。
综合在一起,也就是与_Screen类Turtle._screen对象、Turtle类Turtle._pen对象相关的那些方法在以全局函数形式调用的时候都会遵循构造模板__func_body的处理过程。
因此,在本文开始的异常现象中,很多方法第一次调用时异常,接着再调用一次又正常的原因就很好理解了。
而如果不是以全局函数形式调用呢?以对象方法调用的呢?turtle还有另一种机制。
3.2.2 update和_incrementudc
这两个方法都是TurtleScreen的方法。update用于执行窗口更新,会将当前窗口的画笔更新,也会调用其基类TurtleScreenBase的_update。
而_incrementudc则进行更新计数,它的处理过程和之前的__func_body相似。基本上只要是对窗口进行任何的绘制都会涉及到它,这可以考查哪些方法会间接/直接调用到_incrementudc。今天只分析到这里。
如果没有出现窗口是因为对前一个窗口的关闭,那么在后面接着执行第一个窗口或画笔相关方法可能会抛出turtle.Terminator异常,再接着执行便不抛出。可以从turtle.py中看到相关处理过程。此外,从探究异常本质的过程中也学到非常多的高级技术,比如__func_body和_make_global_funcs的运用。
今晚时间有限,只对turtle.py进行部分剖析,还有很多问题有待疏理。以后有空有机会的时候再和大家分享。