也许这更像是一个样式问题而不是技术问题,但我有一个带有几个成员变量的python类,我想让它工作,以便在用户首次创建一个实例时初始化一些成员变量class(即在__init__
函数中),我希望从稍后将调用的成员函数的参数定义其他成员变量。所以我的问题是我应该初始化__init__
函数中的所有成员变量(并将稍后定义的变量设置为虚拟值)或者在__init__
函数中初始化一些变量,在后面的函数中初始化一些变量。我意识到这可能很难理解,所以这里有几个例子。
此示例最初在var3
函数中将__init__
设置为0,然后在my_funct函数中设置为所需的值。
class myClass(object):
def __init__(self,var1,var2):
self.var1=var1
self.var2=var2
self.var3=0
def my_funct(self,var3):
self.var3=var3
并且在此示例中,var3
函数中根本未定义__init__
class myClass(object):
def __init__(self,var1,var2):
self.var1=var1
self.var2=var2
def my_funct(self,var3):
self.var3=var3
我不认为任何一种方式都会产生很大的不同(可能是内存使用量略有不同)。但我想知道其中一个是否因为某些原因而优先于另一个。
答案 0 :(得分:24)
在面向对象的编程中,开发人员需要确保在实例化之后和方法完成后对象始终处于一致状态。除此之外,您可以根据自己的意愿自由开发课程(请记住某些原则,包括子类/覆盖等)。
当您在__init__
之外设置实例变量时,Pylint等工具会发出警告。可以说,在__init__
中设置所有实例变量都比较清晰,但这并不是必须始终遵守的规则。
答案 1 :(得分:5)
我实际上不鼓励将__init__
中并不总是需要的变量初始化为任意默认值。
如果是这种情况,我确实质疑你对OO的使用,但我确信有一个有效且可理解的情况,__init__
不会做任何事情,并且该类将希望通过添加进一步修改自己其他方法的附加属性。
我认为在运行可能要使用它的方法时测试变量是否设置的正确方法是使用hasattr
。这是一种使用该方法的有效方法,测试只是以合理的方式切换行为。
另一种方法是尝试使用它并处理异常,并提供一些用户友好的信息,告诉您类的用户做错了什么。这是在方法需要在运行之前设置属性的情况。
即。嘿,你确实初始化了这个类,但是你需要在运行z
方法之前调用z_init
方法确保z_run
属性存在。
另一种,可以说是更加pythonic的方式,只是记录如何在docstring中使用该方法,然后在异常使用时让异常飞行。这对于第一次实现某些东西已经足够了,然后您可以专注于下一个任务。这与上面的情况相同,该方法需要设置属性。
我不喜欢将变量初始化为任意默认值的原因是这可能令人困惑(因为它是任意的)并且是线噪声。
如果值不任意且只是一个可以更改的默认值,则应该使用__init__
方法中可以覆盖的默认值。它实际上也可以是一个有效的初始状态,也是不任意的,您应该在__init__
方法中设置它。
所以真正的答案是它取决于,如果您通过在其他方法中添加属性或将属性初始化为任意值,您可能应该避免使用OO并质疑您对OO的使用。< / p>
虽然Simeon Visser说要保持你的对象处于一致的状态,但他没有基于你的抽象例子的一致性。虽然Pylint警告这种事情,但是lint程序的警告只是一个高级别的审阅者可以被警告通常指示代码味道的事情。我说高级评论员是因为真正的评论者应该阅读并理解你的所有代码,因此不需要Pylint。
打破经验法则的一个例子:
class Mutant(object):
"""A mutant!"""
def __init__(self):
"""A mutant is born with only 1 eye and 1 mouth"""
self.eyes = 1
self.mouth = 1
self.location = 'Montana'
def roll_to(self, location):
"""If they have limbs, running is less dangerous"""
if hasattr(self, 'limbs'):
print 'Your mutant broke its limbs off!!'
del self.limbs
self.location = location
def run_to(self, location):
"""If they don't have limbs, running is not effective"""
if not hasattr(self, 'limbs'):
print 'Your mutant tries to run but he has no limbs.'
else:
self.location = location
def grow_limbs(self, number_of_limbs):
"""Ah, evolution!"""
assert number_of_limbs > 0, 'Cannot grow 0 or less limbs...'
if hasattr(self, 'limbs'):
self.limbs += number_of_limbs
else:
self.limbs = number_of_limbs
答案 2 :(得分:0)
以下摘自sololearn.com(一个学习python的免费网站)
“属性提供了一种自定义实例属性访问的方法。 它们是通过将属性装饰器放在方法上方来创建的,这意味着当访问与方法同名的实例属性时,将调用该方法。
属性的一个常见用途是将属性设置为只读。“
示例(也来自sololearn.com):
class Pizza:
def __init__(self, toppings):
self.toppings = toppings
@property
def pineapple_allowed(self):
return False
pizza = Pizza(["cheese", "tomato"])
print(pizza.pineapple_allowed)
pizza.pineapple_allowed = True
结果:
>>>
False
AttributeError: can't set attribute
>>>
如果var3取决于var1和var2,你可以做
class myClass:
def __init__(self,var1,var2):
self.var1=var1
self.var2=var2
@property
def var3(self):
return(self.var1+self.var2) #var3 depends on var1 and var2
m1=myClass(1,2)
print(m1.var3) # var3 is 3
var3也可以使用setter函数设置为您想要的任何值。请注意,您可以使用None来避免将var3设置为任意值。
class myClass2(object):
def __init__(self,var1,var2):
self.var1=var1
self.var2=var2
self._var3=None # None or an initial value that makes sense
@property
def var3(self):
return(self._var3)
@var3.setter
def var3(self,value):
self._var3=value
m2=myClass(1,2)
print(m2.var3) # var3 is none
print(m2.var3(10)) # var3 is set to 10