我有一个在2d板中创建块的类。我定义了它,这样它就可以在给定它们在板上的高度、长度和位置的情况下这样做,但也制作了一个替代构造函数,通过传递其在板中的坐标来创建块。
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_words构造函数可以完成所有操作,但由于某种原因,我不理解创建的元素没有初始化,即没有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_words构造函数,但它运行良好
a = Block.from_coords("A",[(0,0),(1,0)])
print(a)
Name: A
Coords: [(0, 0), (1, 0)]
我知道我可以在SpacesBlock的init中定义名称和坐标,一切都很好,但我很好奇为什么它不能像我想象的那样工作。我错过了什么?
from_coords
不是构造函数。这是一种工厂方法。
__init__
实际上初始化了一个已经存在(即已分配(的对象。因为它是一个普通的方法,接收一个self
参数,所以它能够做到这一点。classmethod
不能初始化一个已经存在的对象,除非你明确地为它提供一个。
代码中发生的情况是,super().from_coords(" ",coords)
创建了Block
的单独实例,然后将其丢弃。SpacesBlock
的self
实例没有得到其.name
集合;另一个例子就是这样。
@classmethod
(与@staticmethod
相反(的要点是,因为您收到的参数是类的,所以即使您没有对象实例,它仍然可以表现为多态性。(正如您所发现的,您可以使用它使__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
实例——无论哪种情况,都使用(高度、长度、位置(方法。要直接从坐标列表中创建任一类,只需直接调用构造函数即可。
spaces = SpacesBlock([(0,0),(0,1)])
您创建了一个实例SpaceBlock
。然后运行__init__
来初始化这个对象。问题是,SpaceBlock
中的__init__
函数不会修改您刚刚创建的实例。相反,它创建并实例化另一个对象。这个新对象没有用于任何用途,您最初创建的对象没有进行任何修改,特别是没有name
和coords
属性。
您可以通过修改__init__
来修复它,例如:
def __init__(self, coords):
x = super().from_coords(" ",coords)
self.name = x.name
self.coords = x.coords
但这将是一个不必要的复杂代码。