我有一个对象需要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
,或者是配置实例的实例方法。
答案 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__
。
就其他选择而言:
__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