[英]Why does a library class break when I try to store and use an instance (delegation) instead of making a subclass (inheritance)?
I'm making a game using the turtle
standard library module for graphics.我正在使用
turtle
图形标准库模块制作游戏。 I have working code that creates a subclass of Turtle
, like so:我有创建
Turtle
子类的工作代码,如下所示:
import random
class Food(Turtle):
def __init__(self):
super().__init__()
# more code...
However, I wanted to see if I could make it work without using inheritance instead.但是,我想看看是否可以在不使用 inheritance 的情况下使其工作。 This is my attempt:
这是我的尝试:
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`
Elsewhere in the program, I have an instance food
of the Food
class, an I try to do collision detection between the food
and another Turtle, snake.head
:在程序的其他地方,我有一个
Food
class 的实例food
,我尝试在food
和另一只乌龟snake.head
之间进行碰撞检测:
if snake.head.distance(food) < 15:
...
In the original code, it works fine, but with the new version I get this error message:在原始代码中,它工作正常,但在新版本中我收到此错误消息:
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
Why does this occur, and how can I fix it?为什么会发生这种情况,我该如何解决?
I think you'll have to do我想你必须做
if snake.head.distance(food.food) < 15:
food.generate_food()
In general: when using composition instead of inheritance (what the second attempt does), it is necessary to delegate everything that needs to use the internal object - including any calls to library code.一般来说:当使用组合而不是 inheritance (第二次尝试所做的)时,有必要委托所有需要使用内部 object 的东西——包括对库代码的任何调用。 Using composition instead of inheritance entails not using inheritance ;
使用组合而不是 inheritance 意味着不使用 inheritance ; therefore, the class is no longer a subtype of whatever library class;
因此,class 不再是任何库 class 的子类型; therefore, other code can't use it the same way .
因此,其他代码不能以同样的方式使用它。
When the .distance
method is called on the snake.head
, the Turtle class needs to be given something whose position it knows how to find.当在
snake.head
上调用.distance
方法时,需要给 Turtle class 一些它知道如何找到其 position 的东西。 It has the following rules (quoting from the source ):它具有以下规则(引自来源):
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
(This is slightly offset from the error message reported, because of changes between Python versions.) (由于 Python 版本之间的变化,这与报告的错误消息略有不同。)
In other words, it knows how to use:换句话说,它知道如何使用:
x
and y
(ie, directly telling it the position);x
和y
(即直接告诉它位置);Vec2D
(directly telling it the position, but using the class that the library provides for representing positions);Vec2D
(直接告诉它 position,但使用库提供的 class 来表示位置);TNavigator
(in practice, this means a Turtle, but there are other possibilities). TNavigator
(实际上,这意味着 Turtle,但还有其他可能性)。 When Food
inherits from Turtle
, it's a Turtle
.当
Food
继承自Turtle
时,它就是一个Turtle
。 It has built-in position tracking, which other Turtle
s can use: accessing the hidden ._position
attribute.它有内置的 position 跟踪,其他
Turtle
可以使用:访问隐藏的._position
属性。 (The leading underscore means that other classes aren't supposed to know about it or use it; but Python does not have true privacy .) (前导下划线意味着其他类不应该知道或使用它;但Python 没有真正的隐私。)
When Food
stores a Turtle
, it isn't a Turtle
.当
Food
存储Turtle
时,它不是Turtle
。 While the logic is obvious to the programmer - fetch the turtle stored in the .food
attribute, and then get the position of that - the already-written Turtle
code has no way to know that.虽然逻辑对程序员来说是显而易见的——获取存储在
.food
属性中的海龟,然后获取它的 position——已经编写的Turtle
代码无法知道这一点。
To fix the problem, we can extract the underlying Turtle
at the point where the method is called:为了解决这个问题,我们可以在调用方法的地方提取底层的
Turtle
:
if snake.head.distance(food.food) < 15:
...
Or we can implement the interface that the library code wants to use.或者我们可以实现库代码想要使用的接口。 In this specific instance, that won't be feasible;
在这种特定情况下,这是不可行的; it's explicitly checking for types, so we'd need the class to be one of those types - in which case, we might as well just use inheritance in the first place.
它明确地检查类型,所以我们需要 class 是这些类型之一——在这种情况下,我们不妨首先使用 inheritance。
But consider another example, where someone else has written a distance function (not method) that expects two Turtles:但是考虑另一个例子,其他人已经写了一个距离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)
Then we could adapt the class to meet that interface.然后我们可以调整 class 来满足该接口。 For a missing method, implement the method, and have its logic check the wrapped object. For a missing attribute, use a (read-only) property that checks for the corresponding information in the wrapped object. Here's an example showing both (and using the property to implement the method):
对于缺少的方法,实现该方法,并让它的逻辑检查包装的 object。对于缺少的属性,使用(只读)属性检查包装的 object 中的相应信息。这是一个显示两者(并使用实现方法的属性):
class Food:
# other stuff as before...
@property
def _position(self):
return self.food._position
def pos(self):
return self._position
(One might ask, why does the Turtle class use a method pos
, instead of a property
? That's because it's old code following an old design, before property
support was added to Python. Updating things like this is low priority for the Python dev team; it risks breaking old code; and it involves writing new documentation - and hoping that tutorial authors get the hint as well.) (有人可能会问,为什么 Turtle class 使用方法
pos
而不是property
?那是因为它是遵循旧设计的旧代码,在property
支持被添加到 Python 之前。更新这样的事情对于 Python 开发团队来说是低优先级的;它有破坏旧代码的风险;它涉及编写新文档——并希望教程作者也能得到提示。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.