改进__init__,直接将参数赋值给成员
我发现自己写了很多这样的类和构造函数:
class MyClass(object):
def __init__(self, foo, bar, foobar=1, anotherfoo=None):
self.foo = foo
self.bar = bar
self.foobar = foobar
self.anotherfoo = anotherfoo
这样写是不是有点问题?Python有没有更优雅的处理方式?
我的类和一些构造函数其实比我展示的要复杂得多,但我通常会有一堆参数传给构造函数,结果这些参数就被分配给了同样名字的成员变量。我把一些参数设为可选的,想要指出这样做的问题:
class MyClass(object):
def __init__(self, arg_dict):
self.__dict__ = arg_dict
5 个回答
1
你可以这样做:
def Struct(name):
def __init__(self, **fields):
self.__dict__.update(fields)
cls = type(name, (object, ), {'__init__', __init__})
return cls
使用的时候像这样:
MyClass = Struct('MyClass')
t = MyClass(a=1, b=2)
如果你还想要位置参数的话,可以用这个:
def Struct(name, fields):
fields = fields.split()
def __init__(self, *args, **kwargs):
for field, value in zip(fields, args):
self.__dict__[field] = value
self.__dict__.update(kwargs)
cls = type(name, (object, ), {'__init__': __init__})
return cls
然后使用起来像这样:
MyClass = Struct('MyClass', 'foo bar foobar anotherfoo')
a = MyClass(1, 2, foobar=3, anotherfoo=4)
这和 collections
里的 namedtuple
有点像。这样做可以省去很多重复的输入,因为你不需要一次又一次地定义基本上是相同的 __init__
方法,也不需要为了得到那个方法而让你的继承结构变得复杂。
如果你需要添加额外的方法,可以直接创建一个基础的
MyClassBase = Struct('MyClassBase', 'foo bar')
class MyClass(MyClassBase):
def other_method(self):
pass
2
我个人觉得你现在的做法更好,因为这样更稳定。
看看下面这段代码,里面有个拼写错误:
myobject = MyClass(foo=1,bar=2,fobar=3)
如果你用你原来的方法来创建这个对象,你会得到以下理想的结果:
TypeError: __init__() got an unexpected keyword argument 'fobar'
但是如果用kwargs
的方法,就会出现这样的情况:
>>> myobject.fobar
3
我觉得这就是那种很难找到的错误的根源。
你可以检查kwargs
列表,确保里面只有预期的值,但等你做完这些检查和添加默认值的工作后,我觉得这会比你原来的方法复杂得多。
8
如果它们是关键字参数(kwargs),你可以这样做:
def __init__(self, **kwargs):
for kw,arg in kwargs.iteritems():
setattr(self, kw, arg)
位置参数(posargs)就有点复杂,因为你不能很方便地获取到参数的名称。
如果你想提供默认值,可以这样做:
def __init__(self, **kwargs):
arg_vals = {
'param1': 'default1',
# ...
}
arg_vals.update(kwargs)
for kw,arg in arg_vals.iteritems():
setattr(self, kw, arg)