self 将导致变量归属于类的实例,而不是类本身。我不知道你是不是这个意思,但这当然值得考虑。
类范围内的变量可以分为两类:类变量和实例变量。类变量在类定义的开头定义,在任何方法之外。如果一个变量对于所有实例都是常量,或者它只用在类/静态方法中,它应该是一个类变量。通常,这些变量是真正的常数,尽管在许多情况下它们不是。实例变量通常在__init__ 中定义,但在许多情况下它们应该在其他地方定义。话虽如此,如果您没有充分的理由不这样做,请在__init__ 中定义实例变量,因为这样可以使您的代码(和类)井井有条。如果您知道变量对实例的状态至关重要,但在调用某个方法之前无法确定其值,则为它们提供占位符值(例如 None)是完全可以接受的。
这是一个很好的例子:
class BaseGame:
"""Base class for all game classes."""
_ORIGINAL_BOARD = {(0,0): 1, (2,0): 1, (4,0): 1, (6,0): 1, (8,0): 1,
(1,2): 1, (3,2): 1, (5,2): 1, (7,2): 1, (2,4): 1,
(4,4): 1, (6,4): 1, (3,6): 1, (5,6): 1, (4,8): 0}
_POSSIBLE_MOVES = {(0,0): ((4,0),(2,4)),
(2,0): ((4,0),(2,4)),
(4,0): ((-4,0),(4,0),(2,4),(-2,4)),
(6,0): ((-4,0),(-2,4)),
(8,0): ((-4,0),(-2,4)),
(1,2): ((4,0),(2,4)),
(3,2): ((4,0),(2,4)),
(5,2): ((-4,0),(-2,4)),
(7,2): ((-4,0),(-2,4)),
(2,4): ((4,0),(2,4),(-2,-4),(2,-4)),
(4,4): ((-2,-4,),(2,-4)),
(6,4): ((-4,0),(-2,4),(-2,-4),(2,-4)),
(3,6): ((-2,-4),(2,-4)),
(5,6): ((-2,-4),(2,-4)),
(4,8): ((-2,-4),(2,-4))}
started = False
def __call__(self):
"""Call self as function."""
self.started = True
self.board = __class__._ORIGINAL_BOARD.copy()
self.peg_count = 14
self.moves = []
@staticmethod
def _endpoint(peg, move):
"""Finds the endpoint of a move vector."""
endpoint = tuple(map(add, peg, move))
return endpoint
@staticmethod
def _midpoint(peg, move):
"""Finds the midpoint of a move vector."""
move = tuple(i//2 for i in move)
midpoint = tuple(map(add, peg, move))
return midpoint
def _is_legal(self, peg, move):
"""Determines if a move is legal or not."""
endpoint = self._endpoint(peg, move)
midpoint = self._midpoint(peg, move)
try:
if not self.board[midpoint] or self.board[endpoint]:
return False
else:
return True
except KeyError:
return False
def find_legal_moves(self):
"""Finds all moves that are currently legal.
Returns a dictionary whose keys are the locations of holes with
pegs in them and whose values are movement vectors that the pegs
can legally move along.
"""
pegs = [peg for peg in self.board if self.board[peg]]
legal_moves = {}
for peg in pegs:
peg_moves = []
for move in __class__._POSSIBLE_MOVES[peg]:
if self._is_legal(peg, move):
peg_moves.append(move)
if len(peg_moves):
legal_moves[peg] = peg_moves
return legal_moves
def move(self, peg, move):
"""Makes a move."""
self.board[peg] = 0
self.board[self._midpoint(peg, move)] = 0
self.board[self._endpoint(peg, move)] = 1
self.peg_count -= 1
self.moves.append((peg, move))
def undo(self):
"""Undoes a move."""
peg, move = self.moves.pop()
self.board[peg] = 1
self.board[self._midpoint(peg, move)] = 1
self.board[self._endpoint(peg, move)] = 0
self.peg_count += 1
def restart(self):
"""Restarts the game."""
self.board = __class__._ORIGINAL_BOARD.copy()
self.peg_count = 14
self.moves.clear()
_ORIGINAL_BOARD 和 _POSSIBLE_MOVES 是真正的常量。虽然started 不是一个常量,因为它的值取决于是否调用了__call__ 方法,它的默认值False 是所有实例的常量,所以我将它声明为一个类变量。请注意,在__call__(不用担心我为什么使用__call__ 而不是__init__)中,我将它重新定义为实例变量,因为__call__ 开始游戏,因此当它被调用时,instance 的 状态已从类默认值“未启动”更改为“已启动”。
还要注意,除了__call__ 之外的其他方法会定期更改实例变量的值,但它们最初并未在所述方法中定义,因为没有令人信服的理由。