Python 3对象构造:哪种方式最符合Python风格/被接受的方式?

8

由于我有Java背景,它非常冗长和严格,所以我发现Python对象的可变性使其具有比构造函数中呈现的字段更多的字段,这真的很“丑陋”。

为了习惯Pythonic的思维方式,我想知道如何允许构建我的对象

我的直觉是要在构建时传递字段,例如:

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对象?有一个公认的惯例吗?


构造时间是在调用 __new__ 时。当调用 __init__ 时,实例已经存在,这是初始化时间 - u0b34a0f6ae
@kaizer.se 对术语的使用表示赞赏。 - Humphrey Bogart
2个回答

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])

有很多变种,我所知道的没有通用标准。


1
注意:如果您盲目更新 self.__dict__,任何人都可以覆盖您的所有实例方法和变量。 - Debilski
@Debilski:你说得很有道理,因为不小心覆盖现有属性很容易,但我也想指出,在Python中,任何人都可以覆盖你的方法和变量,如果他们愿意的话。 - u0b34a0f6ae
@Debilski:如果你使用私有作用域(例如:self.__name),那么你的方法和变量将被隐藏,无法被外部修改。请参阅文档:http://docs.python.org/reference/expressions.html#atom-identifiers 和示例:http://codepad.org/EPH6UZd0 - idbrii

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不是必需的,那么你可以在之后定义它们。
如果参数列表的可读性是个问题:使用关键字参数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接