非常好的问题。我能够重现这种行为:如果你只养一只乌龟,print(len(turtle.turtles()))
按预期给出 1,但克隆一次后,突然出现 3。这是一个最小的示例:
import turtle
t = turtle.Turtle()
print(len(turtle.turtles())) # => 1, no problem
c = turtle.clone()
print(len(turtle.turtles())) # => 3 !!?
问题是调用.clone()
on turtle
(模块)而不是您想要克隆的海龟实例:
import turtle
t = turtle.Turtle()
c = t.clone()
print(len(turtle.turtles())) # => 2 as expected
这是经典海龟陷阱 https://gist.github.com/ggorlen/f46857bd0057ca919b97d5569f6ec641:将函数式接口误认为是面向对象的接口。你打电话时turtle.clone()
,这是一个函数调用,因此该模块创建其单例非 OOP 海龟,然后克隆它并返回您存储在其中的克隆c
.
无与伦比的cdlane https://stackoverflow.com/users/5771269/cdlane 倡导者 https://stackoverflow.com/a/62296811/6243352对于以下导入:
from turtle import Screen, Turtle
这使得事情很难搞砸。
如果您对导致该行为发生的 CPython 海龟内部结构感到好奇,这里是代码(来自库/turtle.py#L3956 https://github.com/python/cpython/blob/81bf10e4f20a0f6d36b67085eefafdf7ebb97c33/Lib/turtle.py#L3956):
## The following mechanism makes all methods of RawTurtle and Turtle available
## as functions. So we can enhance, change, add, delete methods to these
## classes and do not need to change anything here.
__func_body = """\
def {name}{paramslist}:
if {obj} is None:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
{obj} = {init}
try:
return {obj}.{name}{argslist}
except TK.TclError:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
raise
"""
def _make_global_funcs(functions, cls, obj, init, docrevise):
for methodname in functions:
method = getattr(cls, methodname)
pl1, pl2 = getmethparlist(method)
if pl1 == "":
print(">>>>>>", pl1, pl2)
continue
defstr = __func_body.format(obj=obj, init=init, name=methodname,
paramslist=pl1, argslist=pl2)
exec(defstr, globals())
globals()[methodname].__doc__ = docrevise(method.__doc__)
_make_global_funcs(_tg_screen_functions, _Screen,
'Turtle._screen', 'Screen()', _screen_docrevise)
_make_global_funcs(_tg_turtle_functions, Turtle,
'Turtle._pen', 'Turtle()', _turtle_docrevise)
这需要所有的乌龟Screen()
and Turtle()
方法并将它们连接到模块的全局变量中,所以turtle.Turtle().clone
被设定为turtle.clone
。作为此次重新布线的一部分,__func_body
向每个调用添加一些样板,检查是否Turtle._pen
or Turtle._screen
已经存在,如果不存在则创建它们。
无关,但你需要打电话pendown()
如果你想让它做任何事情,请用括号括起来。另外,最好在循环之前而不是在循环内部设置事件侦听器,或者完全删除它,因为它与问题无关。