简体   繁体   English

当我尝试存储和使用实例(委托)而不是创建子类(继承)时,为什么库 class 会中断?

[英]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:换句话说,它知道如何使用:

  • two separate integers x and y (ie, directly telling it the position);两个独立的整数xy (即直接告诉它位置);
  • a Vec2D (directly telling it the position, but using the class that the library provides for representing positions);一个Vec2D (直接告诉它 position,但使用库提供的 class 来表示位置);
  • a tuple (directly telling it the position, but using values stored in a tuple);一个元组(直接告诉它 position,但使用存储在元组中的值);
  • a 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.

相关问题 当我尝试使用 corsheaders 时,为什么我的应用程序会中断? - Why does my application break when I try to use corsheaders? Python:我应该在这里使用委托还是继承? - Python: Should I use delegation or inheritance here? 为什么在创建list类的子类时不使用构造函数? - why not using constructor when making subclass of list class? 当我尝试设置中断时,为什么pdb显示“***空白或注释”? - Why is pdb displaying “*** Blank or comment” when I try to set a Break? 实例化时在类外添加子类继承 - Adding subclass inheritance outside a class when instantiating 在fromtimestamp方法中指定参数tz时,为什么datetime的子类不是子类的实例? - Why does subclass of datetime not an instance of subclass when specifying argument tz to method fromtimestamp? 为什么当我从实例调用 class 属性而不是 class 属性时,我的 output 发生了变化? - Why did my output change when I called the class attribute from an instance instead of the class? 为什么使用类方法而不是仅使用实例的类名? - Why Use Class Methods Instead Of Just Using The Class Name For Instance? 具有类变量继承的子类 - Subclass with class variable inheritance 为什么sqlalchemy使用DeclarativeMeta类继承将对象映射到表 - Why does sqlalchemy use DeclarativeMeta class inheritance to map objects to tables
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM