我正在学习 Udemy for Python 上的 Angela Yu 博士的“100 天编程”课程。我在第 22 天使用 Turtle 重新创建 Pong 游戏。
我认为 Screen() 是一个类,因为我一直像 Turtle() 一样对待它并像这样编码:
from turtle import Turtle, Screen
tort = Turtle()
display = Screen()
display.bgcolor("orange")
display.exitonclick()
但是每当我开始在 pycharm 社区中输入 Screen() 时,在弹出的下拉菜单中,“Screen()”旁边有一个小红圈“f”。相反,当我开始输入“Turtle() 或“Turtle”时,它旁边有一个小蓝色圆圈“c”。
这是否意味着 Screen() 绝对不是一个类?如果是这样,为什么我可以使用 Screen() 创建一个对象,就像使用 Turtle() 一样?
并不是我的代码不起作用,我只是不明白为什么它会这样工作。
另外,我还意识到我可以通过这样编写代码来完成同样的事情: 从海龟进口海龟
tort = Turtle()
tort.screen.bgcolor("orange")
tort.screen.exitonclick()
但是 pycharm 黄色强调了“exitonclick()”部分,其中包含“类‘TurtleScreen’的未解析属性引用‘exitonclick’”,这只会让我更加困惑,因为……那么为什么当我运行它时它工作得很好。
序言:turtle 是一团糟,而且对初学者来说并不那么友好。 API 中存在大量不一致之处,它几乎打破了 Zen of Python 的每条规则。可能就是这样,因为它是很久以前写的,并且从那以后就没有太大改变。因此,当海龟 API 或内部结构的某些部分莫名其妙地奇怪或不一致或 IDE 自动完成变得混乱时,请不要太惊讶。
查看
turtle.py
中CPython的源代码:
def Screen():
"""Return the singleton screen object.
If none exists at the moment, create a new one and return it,
else return the existing one."""
if Turtle._screen is None:
Turtle._screen = _Screen()
return Turtle._screen
Screen()
是一个函数,只是命名方式打破了PEP-8 Python风格指南,该指南指定函数应该是lower_snake_case
,类应该是UpperPascalCase
。分歧的基本原理可能是它是一个单例实例,因此它返回现有的 _Screen
或创建一个新实例(如果不存在)。
Turtle
显然是一个类:
class Turtle(RawTurtle):
"""RawTurtle auto-creating (scrolled) canvas.
When a Turtle object is created or a function derived from some
Turtle method is called a TurtleScreen object is automatically created.
"""
_pen = None
_screen = None
def __init__(self,
shape=_CFG["shape"],
undobuffersize=_CFG["undobuffersize"],
visible=_CFG["visible"]):
if Turtle._screen is None:
Turtle._screen = Screen()
RawTurtle.__init__(self, Turtle._screen,
shape=shape,
undobuffersize=undobuffersize,
visible=visible)
# ...
exitonclick
文档字符串中有一条评论:
这是
类的方法,不适用于Screen
实例。TurtleScreen
注释的原因是它位于继承自
_Screen
的类TurtleScreen
中。当您创建 Turtle()
并且不指定屏幕时,将调用上面显示的 def Screen
函数,创建一个 _Screen
实例。评论看起来不正确,实际上应该说 _Screen
类。
因此
exitonclick
方法将在运行时可供您的实例使用,但到达那里的路径存在 IDE 无法静态解析的条件,例如:
class RawTurtle(TPen, TNavigator):
"""Animation part of the RawTurtle.
Puts RawTurtle upon a TurtleScreen and provides tools for
its animation.
"""
screens = []
def __init__(self, canvas=None,
shape=_CFG["shape"],
undobuffersize=_CFG["undobuffersize"],
visible=_CFG["visible"]):
if isinstance(canvas, _Screen): # this branch will be taken in OP's case
self.screen = canvas
elif isinstance(canvas, TurtleScreen):
if canvas not in RawTurtle.screens:
RawTurtle.screens.append(canvas)
self.screen = canvas
elif isinstance(canvas, (ScrolledCanvas, Canvas)):
for screen in RawTurtle.screens:
if screen.cv == canvas:
self.screen = screen
break
else:
self.screen = TurtleScreen(canvas)
RawTurtle.screens.append(self.screen)
else:
raise TurtleGraphicsError("bad canvas argument %s" % canvas)
一般来说,Turtle 使用运行时元编程来动态连接各处的一堆冗余类和属性。如果其中一些自动连接使 IDE 感到困惑,我不会感到惊讶。