[英]Why does a library class break when I try to store and use an instance (delegation) instead of making a subclass (inheritance)?
我正在使用turtle
图形标准库模块制作游戏。 我有创建Turtle
子类的工作代码,如下所示:
import random
class Food(Turtle):
def __init__(self):
super().__init__()
# more code...
但是,我想看看是否可以在不使用 inheritance 的情况下使其工作。 这是我的尝试:
from turtle import Turtle
from random import randint
class Food():
def __init__(self):
self.food = Turtle()
# more code, but now modifying `self.food` instead of `self`
在程序的其他地方,我有一个Food
class 的实例food
,我尝试在food
和另一只乌龟snake.head
之间进行碰撞检测:
if snake.head.distance(food) < 15:
...
在原始代码中,它工作正常,但在新版本中我收到此错误消息:
Traceback (most recent call last):
File "D:\PycharmProjects\Various stuff beginning\Snake_retry\main.py", line 29, in <module>
if snake.head.distance(food) < 15:
File "C:\Users\palliativo\AppData\Local\Programs\Python\Python310\lib\turtle.py", line 1858, in distance
return abs(pos - self._position)
UnboundLocalError: local variable 'pos' referenced before assignment
Process finished with exit code 1
为什么会发生这种情况,我该如何解决?
我想你必须做
if snake.head.distance(food.food) < 15:
food.generate_food()
一般来说:当使用组合而不是 inheritance (第二次尝试所做的)时,有必要委托所有需要使用内部 object 的东西——包括对库代码的任何调用。 使用组合而不是 inheritance 意味着不使用 inheritance ; 因此,class 不再是任何库 class 的子类型; 因此,其他代码不能以同样的方式使用它。
当在snake.head
上调用.distance
方法时,需要给 Turtle class 一些它知道如何找到其 position 的东西。 它具有以下规则(引自来源):
if y is not None:
pos = Vec2D(x, y)
if isinstance(x, Vec2D):
pos = x
elif isinstance(x, tuple):
pos = Vec2D(*x)
elif isinstance(x, TNavigator):
pos = x._position
(由于 Python 版本之间的变化,这与报告的错误消息略有不同。)
换句话说,它知道如何使用:
x
和y
(即直接告诉它位置);Vec2D
(直接告诉它 position,但使用库提供的 class 来表示位置);TNavigator
(实际上,这意味着 Turtle,但还有其他可能性)。 当Food
继承自Turtle
时,它就是一个Turtle
。 它有内置的 position 跟踪,其他Turtle
可以使用:访问隐藏的._position
属性。 (前导下划线意味着其他类不应该知道或使用它;但Python 没有真正的隐私。)
当Food
存储Turtle
时,它不是Turtle
。 虽然逻辑对程序员来说是显而易见的——获取存储在.food
属性中的海龟,然后获取它的 position——已经编写的Turtle
代码无法知道这一点。
为了解决这个问题,我们可以在调用方法的地方提取底层的Turtle
:
if snake.head.distance(food.food) < 15:
...
或者我们可以实现库代码想要使用的接口。 在这种特定情况下,这是不可行的; 它明确地检查类型,所以我们需要 class 是这些类型之一——在这种情况下,我们不妨首先使用 inheritance。
但是考虑另一个例子,其他人已经写了一个距离function (不是方法),它需要两个海龟:
def public_distance(t1, t2):
# this version uses the interface that the Turtle class provides
# for other code to get the position: calling the `pos` method
return abs(t1.pos() - t2.pos())
def private_distance(t1, t2):
# this version directly (naughtily) accesses the "private" attribute
return abs(t1._position - t2._position)
然后我们可以调整 class 来满足该接口。 对于缺少的方法,实现该方法,并让它的逻辑检查包装的 object。对于缺少的属性,使用(只读)属性检查包装的 object 中的相应信息。这是一个显示两者(并使用实现方法的属性):
class Food:
# other stuff as before...
@property
def _position(self):
return self.food._position
def pos(self):
return self._position
(有人可能会问,为什么 Turtle class 使用方法pos
而不是property
?那是因为它是遵循旧设计的旧代码,在property
支持被添加到 Python 之前。更新这样的事情对于 Python 开发团队来说是低优先级的;它有破坏旧代码的风险;它涉及编写新文档——并希望教程作者也能得到提示。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.