【问题标题】:What is a better way to define a class' __init__ method with optional keyword arguments?使用可选关键字参数定义类的 __init__ 方法的更好方法是什么?
【发布时间】:2015-02-28 05:09:48
【问题描述】:

我希望班级执行以下相同操作:

class Player:
    def __init__(self, **kwargs):
        try:
            self.last_name = kwargs['last_name']
        except:
            pass
        try:
            self.first_name = kwargs['first_name']
        except:
            pass
        try:
            self.score = kwargs['score']
        except:
            pass

但这在我看来真的很草率。有没有更好的方法来定义这个 __init__ 方法?我希望所有关键字参数保持可选。

【问题讨论】:

  • 即使kwargs['score'] 不可用,您通常也希望将self.score 设置为默认值。并且不要使用毯子except(参见Why is "except: pass" a bad programming practice?);最多使用except KeyError,但无论如何您都在硬编码名称,那么为什么要使用**kwargs
  • 只有 3 个关键字参数?然后def __init__(self, last_name = Null, first_name = Null, score = Null):
  • @BhargavRao 您应该将其发布为答案(这是一个非常有效的答案)。
  • @BhargavRao:?您在这里将 Python 与什么语言混淆了?

标签: python python-3.x


【解决方案1】:

如果你只有 3 个参数,那么 Bhargav Rao 的解决方案更合适,但如果你有很多潜在的参数,那么试试:

class Player:
    def __init__(self, **kwargs):
        self.last_name = kwargs.get('last_name')
        # .. etc.

kwargs.get('xxx') 如果存在则返回 xxx 键,如果不存在则返回 None。 .get 采用可选的第二个参数,如果 xxx 不在 kwargs 中(而不是 None),则返回该参数,例如要将属性设置为空字符串,请使用 kwargs.get('xxx', "")

如果您真的希望属性未定义(如果它不在kwargs 中),那么可以这样做:

class Player:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

这将是令人惊讶的行为,所以我建议不要这样做。

【讨论】:

  • 我已经添加了关于适用性的初始句子。
  • 这种做法很危险,在与kwargs中的关键字冲突时可能会覆盖类方法。考虑添加__slots__ 或您要分配的关键字的某种有效性,如下所述@martineau stackoverflow.com/a/27732607/4369617
  • @redlus 这可能是预期的语义,即类变量是可以被__init__ 参数覆盖的默认值。如果我今天写这个,我会把它拼写为 self.__dict__.update(kw) 。我们都是成年人,所以这个方法很好。如果我想引入仅更改现有变量的限制,我将在 for-loop 之前插入一行:if len(self.__dict__) != len(set(kwargs) | set(self.__dict__)): raise ValueError('...')
  • @thebjorn 你是对的,但我的意思是比这更深层次的影响。您可能会意外覆盖类方法并获得异常。这是一个简单的例子:pastebin.com/GqyCycf2
  • @redlus 好吧...不要那样做。 Python 不是一种 bdsm 语言,它可以让你做很多对这种 bdsm'ish 语言的追随者来说似乎很可恶的事情(!)。您可以分配给任何实例变量(如a.method = 'blah')——更有趣的是,您可以将Player 实例化为p = Player(__class__=NotAPlayer),这只是Python 是Python ;-)
【解决方案2】:

如果你只有 3 个关键字参数,那么这样会更好。

class Player:

    def __init__(self, last_name=None, first_name=None, score=None):
        self.last_name = last_name
        self.first_name = first_name
        self.score = score

【讨论】:

    【解决方案3】:

    你可以使用keyword arguments:

    class Player:
    
        def __init__(self, last_name=None, first_name=None, score=None):
            self.last_name = last_name
            self.first_name = first_name
            self.score = score
    
    obj = Player('Max', 'Jhon')
    print obj.first_name, obj.last_name
    


    Jhon Max
    

    带参数**kwargs

    class Player:
        def __init__(self, **args):
    
            self.last_name = args.get('last_name')
    
            self.first_name = args.get('first_name')
    
            self.score = args.get('score', 0) # 0 is the default score.
    
    obj = Player(first_name='Max', last_name='Jhon')
    
    print obj.first_name, obj.last_name, obj.score
    


    Max Jhon 0
    

    【讨论】:

      【解决方案4】:

      这是一种易于更改的方法:

      class Player:
          _VALID_KEYWORDS = {'last_name', 'first_name', 'score'}
      
          def __init__(self, **kwargs):
              for keyword, value in kwargs.items():
                  if keyword in self._VALID_KEYWORDS:
                      setattr(self, keyword, value)
                  else:
                      raise ValueError(
                          "Unknown keyword argument: {!r}".format(keyword))
      

      示例用法:

      Player(last_name="George", attitude="snarky")
      

      结果:

      Traceback (most recent call last):
        File "keyword_checking.py", line 13, in <module>
          Player(last_name="George", attitude="snarky")
        File "keyword_checking.py", line 11, in __init__
          raise ValueError("Unknown keyword argument: {!r}".format(keyword))
      ValueError: Unknown keyword argument: 'attitude'
      

      【讨论】:

      • 如果你要走这条路,为什么不使用__slots__
      • @thebjorn:过早的优化。
      • @thebjorn: ... 因为它还有其他副作用,而且使用它们有几个注意事项。
      【解决方案5】:

      我可以试试这个方法吗:

      #!/usr/bin/python
      
      class Player(object):
          def __init__(self, **kwargs):
              for key, val in kwargs.items():
                  self.__dict__[key] = val
      
      obj = Player(first_name='First', last_name='Last')
      print obj.first_name
      print obj.last_name
      
      newobj = Player(first_name='First')
      print newobj.first_name
      

      输出:

      First
      Last
      First
      

      【讨论】:

        【解决方案6】:

        这取决于您想要的最终结果。如果你想创建一个属性在字典中定义的类,你可以使用 setattr。

        class Player:
            def __init__(self, **kwargs):
                for key, value in kwargs.items():
                    setattr(self, key, value)             
        
        
        In [1]: player = Player(first_name='john', last_name='wayne', score=100)
        
        In [2]: player.first_name
        Out[2]: 'john'
        
        In [3]: player.last_name
        Out[3]: 'wayne'
        
        In [4]: player.score
        Out[4]: 100
        
        In [5]: player.address
        ---------------------------------------------------------------------------
        AttributeError                            Traceback (most recent call last)
        <ipython-input-6-0f7ee474d904> in <module>()
        ----> 1 player.address
        
        AttributeError: 'Player' object has no attribute 'address'
        

        【讨论】:

          猜你喜欢
          • 2021-12-28
          • 1970-01-01
          • 2023-03-28
          • 1970-01-01
          • 2016-03-21
          • 2022-01-20
          • 2013-06-09
          • 1970-01-01
          • 2014-05-03
          相关资源
          最近更新 更多