簡體   English   中英

當我嘗試存儲和使用實例(委托)而不是創建子類(繼承)時,為什么庫 class 會中斷?

[英]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 版本之間的變化,這與報告的錯誤消息略有不同。)

換句話說,它知道如何使用:

  • 兩個獨立的整數xy (即直接告訴它位置);
  • 一個Vec2D (直接告訴它 position,但使用庫提供的 class 來表示位置);
  • 一個元組(直接告訴它 position,但使用存儲在元組中的值);
  • 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM