改进__init__,直接将参数赋值给成员

6 投票
5 回答
1897 浏览
提问于 2025-04-16 03:56

我发现自己写了很多这样的类和构造函数:

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)

撰写回答