2017-06-22 22 views
1

我刚刚在Python中约定了self约定,并开始制作更复杂的代码。然而,一位经验丰富的程序员和我的朋友告诉我,对类方法中的每个变量使用self都是浪费。何时不使用Python中的自我约定?

据我所知,self将导致该变量归因于该类。因此,除非有必要,否则避免使用self是好习惯吗?

下面是一些代码,从API获取英雄联盟信息,并将每个变量存储在self.var_name中,以说明我是如何(可能不必要地)使用self

async def getChampInfo(self, *args): 
    """ Return play, ban, and win rate for a champ """ 
    self.uri = "http://api.champion.gg/v2/champions/{}?api_key={}" 
    self.champ = " ".join(args) 
    self.champID = lu.getChampID(self.champ) 
    self.res = requests.get(self.uri.format(
     self.champID, League.champion_gg_api_key)).json() 
    self.role = self.res[0]["role"] 
    self.role_rate = self.res[0]["percentRolePlayed"] 
    self.play_rate = self.res[0]["playRate"] 
    self.win_rate = self.res[0]["winRate"] 
    self.ban_rate = self.res[0]["banRate"] 
+2

那你有没有在外面用那种方法?在猜测中,我会说前四个应该是局部变量(uri模板可能是一个类属性),最后五个属性。 – jonrsharpe

+0

我想我错过了'self.train',因为我真的没有在方法之外引用这些变量。我应该能够修剪代码,让现在更加合理。 – James

回答

1

有些情况下不需要使用self

了我的头顶部:

  • 当变量在1个功能仅使用,或者一个功能/方法内创建并仅在该功能中使用/方法
  • 当变量没有按“T需要被方法
  • 之间共享时该变量不需要被暴露于其它类/范围/上下文

另一部分的答案是,创建元类/工厂时/组成这样的事情可能会更有意义,从使用self喜欢的约定移开:

class Factory(object): 
    def __init__(cls, *args, **kwargs): 
     thing = cls(args, kwargs) 

我会在这里失去了一些东西,但那些是什么,我可以在此刻想到的。

相关:

+1

伟大的帮助!所以在上面的例子中,如果我不需要访问实例或方法以外的那些变量,那么它基本上是浪费的。我会修补自我,并看看它从那里去。干杯! – James

+0

@James添加了另一个链接,以深入探讨相关链接下“self”的用法。 :) np。 – jmunsch

1

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__开始游戏,因此当它被调用时,实例的状态已从类默认,“未开始”,“开始”。

另请注意,除__call__之外的其他方法会定期更改实例变量的值,但它们最初并未在所述方法中定义,因为它们没有令人信服的理由。

+0

我绝对是想引用实例,所以你是对的。我的措辞有点笨拙,因为在这一点上我没有完全掌握面向对象(或任何编程)。就你而言,如果我有一个将保持不变的URI,那么把它放在任何函数之外是合理的,因为它将是一个常量? – James

+0

如果类和/或实例需要访问它,那么是的。如果只有一个方法需要访问它,那么只需在该方法中声明它。 –

+0

我会给你一个很好的例子。我用它来表示游戏本身(而不是图形和游戏所用的非必要方法),以实现来自Cracker Barrel的Triangle Peg Game。 –

相关问题