初始化对象变量 - Java 方法,Python 方法?
我有一个对象,需要传入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 个回答
基本的验证可以通过重写类中的相应获取和设置方法来完成(如果你希望在所有情况下都进行验证),或者通过使用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
首先,这段代码:
class Swoosh():
spam = ''
eggs = ''
swallow = ''
coconut = ''
设置了类的属性,比如 spam、eggs
等等。你的 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")
如果说 spam 和 eggs
是必须的,那就用下面的方式替代:
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 文章。这是必读的。
首先,你正在使用旧式类。你真的,真的应该使用新式类,这种类是从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
属性。