Pythonic方式指定对象配置

时间:2015-10-08 07:27:43

标签: python constructor

我有一个对象需要5个配置参数,如此;

class A(object):

    def __init__(self, a=1, b=2, c=3, d=4, e=4):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.e = e

但是,我想提供几套默认配置而不只是一套,例如也(1, 2, 1, 2, 1)(5, 4, 3, 2, 1),最好给他们理智的名字。什么是最Pythonic方法?我考虑过的一些选项是生成实例的@classmethod,或者是配置实例的实例方法。

3 个答案:

答案 0 :(得分:4)

我会有一个单独的类方法映射到预设配置的字典:

class A(object):

    CONFIGS = {
        'default': {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 4},
        ...
    }

    def __init__(self, a, b, c, d, e):
        ...

    @classmethod
    def from_config(cls, config='default'):
        if config not in cls.CONFIGS:
            raise ValueError('invalid config: {!r}'.format(config))
        return cls(**cls.CONFIGS[config])

然后可以轻松扩展到任意数量的配置,并且可以使用以下命令创建实例:

a = A.from_config(config_name)

您仍然可以为__init__提供默认参数值,但如果他们尝试使用A()而不是A.from_config(),则将其删除意味着用户会收到错误消息。这是件好事取决于你,但我认为让用户始终使用from_config或显式自定义参数是有好处的。

子类可以简单地定义自己的CONFIGS字典,并使用继承的from_config甚至__init__

就其他选择而言:

  • 每个预设的单独类方法可以很好地与IDE一起使用,但不能很好地扩展或继承(例如,如果一个孩子不支持其中一个父预设,你会怎么做?);
  • 带有参数或配置名称的__init__会过于复杂且容易出错;和
  • 如果您需要重新配置它们,配置实例的实例方法可能很有用,并且可以与我的类方法类似地实现(接口可以是例如a = A().configure(config_name),其中{ {1}} configure},但是类方法更清楚,配置应该在创建时设置而不是更改。

功能非常简单,您可以将其提取到混合类中,以便在其他地方重复使用:

return self

请注意,为了尽可能广泛地重复使用,我现在已单独提供位置class Configurable(object): """Mix-in for creating instances from preset configs.""" CONFIGS = {} @classmethod def from_config(cls, config): if config not in cls.CONFIGS: raise ValueError('invalid config: {!r}'.format(config)) args, kwargs = cls.CONFIGS[config] return cls(*args, **kwargs) class A(Configurable, ...): # can still include any other inheritance CONFIGS = { 'default': ((), {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 4}), ... } def __init__(self, a, b, c, d, e): ... @classmethod def from_config(cls, config='default'): return super(A, cls).from_config(config) 并命名为args,并删除了默认kwargs

答案 1 :(得分:0)

为构造函数提供可选参数,可以将其作为

完成
class A(object):
    par_names = ('a', 'b', 'c', 'd', 'e')
    defaults = {'default': (1, 2, 3, 4, 4),
                'second':  (1, 2, 1, 2, 1),
                'third':   (5, 4, 3, 2, 1)}

    def __init__(self, cfg='default', *args, **kwargs):
        if cfg not in self.defaults:
            raise ValueError('invalid config: {!r}'.format(cfg))
        if len(args) > len(self.defaults[cfg]):
            raise TypeError('invalid number of arguments: max {!r}'.format(
                                                    len(self.defaults[cfg])))
        for n, v in zip(self.par_names, args):
            setattr(self, n, v)

        cfg_defs = dict(zip(self.par_names, self.defaults[cfg]))
        for k, v in kwargs.items():
            if k not in cfg_defs:
                raise TypeError('{!r} is an invalid keyword argument for this configuration'.format(k)) 
            setattr(self, k, v)
        for k, v in cfg_defs.items():
            setattr(self, k, self.__dict__.get(k, v))

它允许您正常实例化对象,在指定时从适当的配置获取默认值,否则从默认配置中获取默认值(即default)。

样品使用:

>>> a=A()
>>> a.a
1
>>> a.b
2
>>> a.c
3
>>> b=A('third',a=8,c=6)
>>> b.a
8
>>> b.c
6
>>> c=A('third')
>>> c.a
5

以及使用位置和/或关键字参数:

>>> a=A('second',8,9)
>>> a.a
8
>>> a.b
9
>>> a.c
1
>>> a.d
2
>>> b=A('second',8,9,c=10,d=11)
>>> b.a
8
>>> b.b
9
>>> b.c
10
>>> b.d
11
>>> b.e
1

答案 2 :(得分:0)

我还在构造函数中使用可选的config参数。最通用的解决方案(假设None不是有效的配置程序)是获取显式位置或命名初始值设定项的列表以及配置指定的初始值设定项列表,合并它们,并检查没有None值。类似的东西:

DEFAULTS = { 'name': (1,2,3,4,5),
             'another': (2,3,4,5,6),
             'zero_de': (None,None,None,0,0),
             # etc
           }

def __init__( self, a=None,b=None,c=None,d=None,e=None, config=None):

   explicit = [ a,b,c,d,e ]

   if config is None:
      config = (None,None,None,None,None)
   else:
      if config in DEFAULTS:
         config = DEFAULTS[config]
      else
         raise ValueError, 'config name {} not valid'.format( config)

   values = [ ee if ee is not None else ii for ee,ii in zip( explicit, config) ]

   if any( [v is None for v in values ] ):
       raise ValueError, 'object not fully specified: {}'.format(values)

   self.a,self.b,self.c,self.d,self.e = values