[英]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.