Python 3 对象构造:最符合 Python 风格的方式是什么?

8 投票
2 回答
6085 浏览
提问于 2025-04-15 19:50

我之前学过Java,那个语言比较啰嗦和严格,所以我觉得在Python中可以随意修改对象的属性,让它们有构造函数里没有的字段,这种做法真的让我觉得很“丑”。

我正在努力适应Python的思维方式,我在想我应该怎么构造我的对象

我的直觉是应该在构造的时候传入字段,比如:

def __init__(self, foo, bar, baz=None):
    self.foo = foo
    self.bar = bar
    self.baz = baz

不过,如果要传很多字段,这样就会变得特别啰嗦和混乱。为了解决这个问题,我觉得最好的方法是传一个字典给构造函数,从中提取字段

def __init__(self, field_map):
    self.foo = field_map["foo"]
    self.bar = field_map["bar"]
    self.baz = field_map["baz"] if baz in field_map else None

我想到的另一种方法是在其他地方添加字段,比如:

class Blah(object):

    def __init__(self):
        pass

...

blah = Blah()
blah.foo = var1

但这样对我来说感觉太随意了。

(我想我脑子里困扰我的问题是如何在Python中处理接口……)

所以,简单来说,我想问:我应该如何在Python中构造我的对象?有没有什么公认的规范?

2 个回答

3

我在现实中没见过你们的 field_map,我觉得只有在代码的其他地方也用到 field_map 时,这样做才有意义。

关于你的第三个例子:虽然你不需要给它们赋值(除了 None),但在 __init__ 方法中明确声明属性是个常见的做法,这样你就能很清楚地看到你的对象有哪些属性。

所以,下面这种写法比单纯写一个空的 __init__ 方法要好(这样你还会得到更高的 pylint 分数):

class Blah(object):
    def __init__(self):
        self.foo = None
        self.bar = None

blah = Blah()
blah.foo = var1

这种方法的问题在于,初始化后你的对象可能处于一个不太明确的状态,因为你还没有定义所有的属性。这取决于你的对象逻辑(代码逻辑和含义)以及对象的工作方式。如果确实如此,我建议你不要这样做。如果你的对象依赖于 foobar 被合理地定义,你应该把它们放在 __init__ 方法里。

不过,如果属性 foobar 不是必须的,你可以在之后再定义它们。

如果你觉得参数列表的可读性有问题,可以使用关键字参数。

9

你描述的第一种方法是非常常见的。有些人会使用更简短的方式:

class Foo:
   def __init__(self, foo, bar):
       self.foo, self.bar = foo, bar

你提到的第二种方法不太常见,但有一个类似的版本是:

class Thing:
   def __init__(self, **kwargs):
       self.something = kwargs['something']
       #..

这个方法可以用来创建像这样的对象:

t = Thing(something=1)

这个方法还可以进一步修改成:

class Thing:
   def __init__(self, **kwargs):
       self.__dict__.update(kwargs)

这样就可以实现:

t = Thing(a=1, b=2, c=3)
print t.a, t.b, t.c # prints 1, 2, 3

正如Debilski在评论中提到的,最后一种方法有点不安全,你可以像这样添加一个被接受的参数列表:

class Thing:
    keywords = 'foo', 'bar', 'snafu', 'fnord'
    def __init__(self, **kwargs):
        for kw in self.keywords:
            setattr(self, kw, kwargs[kw])

其实有很多不同的变种,我所知道的并没有一个通用的标准。

撰写回答