初始化对象变量 - Java 方法,Python 方法?

13 投票
5 回答
36105 浏览
提问于 2025-04-16 20:39

我有一个对象,需要传入4到5个值。举个例子:

class Swoosh():
    spam = ''
    eggs = ''
    swallow = ''
    coconut = ''

    [... methods ...]

现在,使用 Swoosh 的方式是:

swoosh = Swoosh()
swoosh.set_spam('Spam!')
swoosh.set_eggs('Eggs!')
swoosh.set_swallow('Swallow!')
swoosh.set_coconut('Migrated.')

我在想这样做是不是符合Python的风格,还是说受到了Java的影响?或者说这样做是必须的?另外,如果你在想我为什么要用设置方法而不是直接访问对象的变量——因为在那里面需要做一些基本的验证,所以才用 set_ 方法。

我觉得我可以提供另一种初始化 Swoosh 的方式——提供一个 __init__() 方法,让它接受一个 dict 或者 list

总之,我只是想找一些更有经验的人来帮我或者给我建议。

提前感谢你们的任何意见。

5 个回答

6

基本的验证可以通过重写类中的相应获取和设置方法来完成(如果你希望在所有情况下都进行验证),或者通过使用property这个内置功能来修改特定的属性。记住,Python 不是 Java(当然,Java 也不是 Python)。

关于你想如何创建类,你有几种选择:

1) 使用 __init__ 方法和关键字参数,让用户可以根据需要提供任意数量的参数给你的构造函数:

class Swoosh(object): # Get new-object goodness
    def __init__(self, spam='', eggs='', swallow='', coconut=''):
        # We are going to be using properties in this example
        self._spam = spam
        self._eggs = eggs
        self._swallow = swallow
        self._coconut = coconut

2) 继续按照你现在的方式做,但把类属性更新为实例属性:

class Swoosh(object): # Get new-object goodness
    def __init__(self):
        self.spam = ''
        self.eggs = ''
        self.swallow = ''
        self.coconut = ''

你现在的做法是,每个 Swoosh 的实例共享这些变量的相同值,改变 Swoosh 上的 spam 值会影响到所有没有设置同名实例属性的 Swoosh 实例(这可能不是你想要的结果)。

无论你选择哪种方式,你都应该考虑去掉获取器和设置器,改用属性(当然,何时这样做取决于这个 API 的公开程度和使用广泛性):

# Inside class Swoosh's definition
@property
def spam(self):
    return self._spam
@spam.setter
def spam(self, value):
    # Validate that it's really SPAM, not WHAM!
    # And only then replace the underlying value
    self._spam = value
@spam.deleter
def spam(self):
    del self._spam
# Rinse and repeat for other properties that need access control
11

首先,这段代码:

class Swoosh():
    spam = ''
    eggs = ''
    swallow = ''
    coconut = ''

设置了类的属性,比如 spameggs 等等。你的 set_spam 方法可能会创建同名的对象属性,这样就会遮盖掉类的属性。换句话说,定义这些属性其实没有什么效果,只会让事情变得更复杂。

如果所有的变量都是可选的,我会这样做:

class Swoosh():
    def __init__(self, spam="", eggs="", swallow="", coconut=""):
        self.spam = spam
        self.eggs = eggs
        self.swallow = swallow
        self.coconut = coconut

然后你可以像这样创建对象:

o1 = Swoosh(eggs="Eggs!", coconut="Carried by the husk")

如果说 spameggs 是必须的,那就用下面的方式替代:

    def __init__(self, spam, eggs, swallow="", coconut=""):

而且根本不需要用到设置器和获取器。如果你以后需要的话,可以通过在你的类中添加这样的代码,轻松地把普通属性替换成属性:

    def get_spam(self):
        return self._spam
    def set_spam(self, value):
        self._spam = " ".join([str(value)] * 5)
    spam = property(get_spam, set_spam)

通过上面的修改,这段代码:

o2 = Swoosh(eggs="yes", spam="spam")
print o2.spam

会输出

spam spam spam spam spam

注意:正如 Dave Webb 在他的回答中指出的,你需要为属性创建一个子类 object,除非你使用的是 Python 3.0 及以上版本,在这种情况下,类会自动继承 object。哦,还有,一定要去看看 Sean Vieira 提到的 Python is not Java 文章。这是必读的。

16

首先,你正在使用旧式类。你真的,真的应该使用新式类,这种类是从object继承而来的

class Swoosh(object):

定义一个带参数的__init__方法绝对是符合Python风格的做法:

def __init__(self,spam,eggs,swallow,coconut):
    self.spam = spam
    self.eggs = eggs
    self.swallow = swallow
    self.coconut = coconut

这样你就可以做到:

s = Swoosh('Spam!','Eggs','Swallow','Migrated')

和其他Python函数一样,__init__方法也可以为参数设置默认值,比如:

def __init__(self,spam,eggs,swallow,coconut='Migrated.'):
        self.spam = spam
        self.eggs = eggs
        self.swallow = swallow
        self.coconut = coconut

如果你想在设置属性时进行验证,应该使用标准的property属性,而不是自己创建设置方法。这样做的话,你可以像使用普通属性一样在代码中使用myobject.spam,但你的设置方法仍然会被调用。

例如:

@property
def spam(self):
    """I'm the 'spam' property."""
    return self._spam

@spam.setter
def spam(self, value):
    if not value.endswith("!"):
        raise ValueError("spam must end with !")
    # Store the value in "private" _spam attribute
    self._spam = value

@spam.deleter
def spam(self):
    del self._spam

注意:property在你使用新式类之前是无法工作的

在你的例子中,你有:

class Swoosh():
    spam = ''
    eggs = ''
    swallow = ''
    coconut = ''

这通常被认为是为对象的属性快速设置默认值的一种方法。这样做没问题,但你需要理解实际发生了什么。实际上,你是在为设置属性。如果一个对象没有某个属性,Python会查看这个属性是否在类中定义,并从那里返回值(因为这正是你想要的方法)。

如果你想为对象属性设置默认值,最好是在__init__中为参数设置默认值,如上面所述,而不是使用class属性。

撰写回答