我有一門課可以在二維板上創建塊。我定義了它,所以它這樣做是考慮到它們在板上的高度、長度和位置,但也做了一個替代的建構式,通過在板上傳遞它的坐標來創建塊。
class Block:
""" Create a block of given height and length in a starting location."""
def __init__(self, name: str, h: int, l: int, location: Tuple[int,int]):
self.name = name
self.coords = [tuple_sum(t1=location, t2=(i//l , i%l)) for i in range(h*l)]
# tuple_sum does (a, b) (c, d) -> (a c, b d)
@classmethod
def from_coords(cls, name: str, coords: List[Tuple]):
block = cls.__new__(cls)
block.name = name
block.coords = coords
return block
...
def __str__(self) -> str:
return f'Name: {self.name}\nCoords: {self.coords}'
我正在嘗試為板上的空白創建一個子類。我認為使用 from_coords 建構式可以完成所有作業,但由于某種原因,我不明白創建的元素沒有初始化,即沒有 name 或 coords 屬性。
class SpacesBlock(Block):
""" Make a block of spaces from a list of coordenates """
def __init__(self, coords):
super().from_coords(" ",coords)
...
spaces = SpacesBlock([(0,0),(0,1)])
print(spaces)
AttributeError Traceback (most recent call last)
\block.py in <module>
space = SpacesBlock([(0,0),(0,1)])
----> print(spaces)
AttributeError: 'SpacesBlock' object has no attribute 'name'
我以為是 from_coords 建構式,但它作業正常
a = Block.from_coords("A",[(0,0),(1,0)])
print(a)
Name: A
Coords: [(0, 0), (1, 0)]
我知道我可以在 SpacesBlock 的 init 中定義名稱和坐標,一切都很好,但我很好奇為什么它不能像我想象的那樣作業。我錯過了什么?
uj5u.com熱心網友回復:
from_coords不是建構式。它是一種工廠方法。
__init__實際上初始化一個已經存在的(即分配的)物件。因為它是一個普通的方法,接收一個self引數,它能夠這樣做。Aclassmethod不能初始化一個已經存在的物件,除非你明確地提供一個。
您的代碼中發生的是super().from_coords(" ",coords)創建一個單獨的實體Block,然后將其丟棄。的self實體SpacesBlock沒有得到它的.name集合;這發生在另一個實體上。
@classmethod(相對于)的要點@staticmethod是,因為您收到一個引數,即class,即使您沒有物件實體,它仍然可以表現出多型性。(正如您所發現的,您可以使用它來進行__new__多型作業。)
您的工廠方法已經可以表現出多型性:SpacesBlock.from_coords將呼叫該方法,并SpacesBlock作為傳遞cls,以便cls.__new__創建一個新SpacesBlock實體。但是,__init__不會以這種方式呼叫;并且以您當前的組織結構,尚不清楚您將如何稱呼它或將通過什么。
真正合理的用途__new__很少見。
使用工廠方法的正常方法是__init__通過確定用于呼叫的引數來__init__呼叫它們。在您的情況下,坐標串列可以是任意位置;但是寬度、高度和位置為您提供了一種指定多個坐標的方法。因此,將實際的建構式用于坐標串列的構造方法會更容易。
使用該設定,代碼如下所示:
class Block:
def __init__(self, name: str, coords: List[Tuple]):
self.name = name
self.coords = coords
@classmethod
def from_grid(cls, name: str, h: int, l: int, location: Tuple[int,int]):
coords = [tuple_sum(location, (i//l , i%l)) for i in range(h*l)]
return cls(name, coords)
class SpacesBlock(Block):
pass # thus far, doesn't actually do anything different
這里要注意的重要一點是,它from_grid現在可以用于任一類。Block.from_grid創建一個Block實體,并SpacesBlock.from_grid創建一個SpacesBlock實體 - 在任何一種情況下,都使用 (height, length, location) 方法。要直接從坐標串列創建任一類,只需直接呼叫建構式即可。
uj5u.com熱心網友回復:
當你執行
spaces = SpacesBlock([(0,0),(0,1)])
你創建一個實體SpaceBlock。然后__init__運行以初始化此物件。問題是,__init__函式 inSpaceBlock不會修改您剛剛創建的實體。相反,它創建并實體化另一個物件。這個新物件不用于任何事情,并且您最初創建的物件沒有任何修改,特別是沒有nameandcoords屬性。
__init__您可以通過如下修改來修復它:
def __init__(self, coords):
x = super().from_coords(" ",coords)
self.name = x.name
self.coords = x.coords
但這將是一個不必要的復雜代碼。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/412247.html
標籤:
上一篇:Java中的抽象類和方法
