我无法找到明确的答案.AFAIK,你不能__init__
在Python类中拥有多个函数.那么我该如何解决这个问题呢?
假设我有一个Cheese
使用该number_of_holes
属性调用的类.我怎样才能有两种创建奶酪对象的方法......
一个像这样的洞: parmesan = Cheese(num_holes = 15)
并且不带参数并且只是随机化number_of_holes
属性:gouda = Cheese()
我只想到一种方法来做到这一点,但这似乎有点笨重:
class Cheese(): def __init__(self, num_holes = 0): if (num_holes == 0): # randomize number_of_holes else: number_of_holes = num_holes
你说什么?还有另外一种方法吗?
实际上None
对于"神奇"的价值要好得多:
class Cheese(): def __init__(self, num_holes = None): if num_holes is None: ...
现在,如果您想完全自由地添加更多参数:
class Cheese(): def __init__(self, *args, **kwargs): #args -- tuple of anonymous arguments #kwargs -- dictionary of named arguments self.num_holes = kwargs.get('num_holes',random_holes())
为了更好地解释*args
和**kwargs
(你可以实际更改这些名称)的概念:
def f(*args, **kwargs): print 'args: ', args, ' kwargs: ', kwargs >>> f('a') args: ('a',) kwargs: {} >>> f(ar='a') args: () kwargs: {'ar': 'a'} >>> f(1,2,param=3) args: (1, 2) kwargs: {'param': 3}
http://docs.python.org/reference/expressions.html#calls
num_holes=None
如果你想要的话,使用默认是很好的__init__
.
如果您需要多个独立的"构造函数",则可以将它们作为类方法提供.这些通常称为工厂方法.在这种情况下,你可以有默认num_holes
BE 0
.
class Cheese(object): def __init__(self, num_holes=0): "defaults to a solid cheese" self.number_of_holes = num_holes @classmethod def random(cls): return cls(randint(0, 100)) @classmethod def slightly_holey(cls): return cls(randint(0, 33)) @classmethod def very_holey(cls): return cls(randint(66, 100))
现在创建这样的对象:
gouda = Cheese() emmentaler = Cheese.random() leerdammer = Cheese.slightly_holey()
如果你想使用可选参数,所有这些答案都非常好,但另一种Pythonic可能是使用classmethod来生成工厂式伪构造函数:
def __init__(self, num_holes): # do stuff with the number @classmethod def fromRandom(cls): return cls( # some-random-number )
为什么你认为你的解决方案"笨重"?就个人而言,我个人更喜欢一个默认值超过多个重载构造函数的构造函数(Python不支持方法重载):
def __init__(self, num_holes=None): if num_holes is None: # Construct a gouda else: # custom cheese # common initialization
对于具有许多不同构造函数的非常复杂的情况,使用不同的工厂函数可能更简洁:
@classmethod def create_gouda(cls): c = Cheese() # ... return c @classmethod def create_cheddar(cls): # ...
在您的奶酪示例中,您可能想要使用奶酪的Gouda子类...
这些是您实施的好主意,但如果您要向用户展示奶酪制作界面.他们不关心奶酪有多少孔或者是什么内部制作奶酪.你的代码的用户只想要"gouda"或"parmesean"吗?
那么为什么不这样做:
# cheese_user.py from cheeses import make_gouda, make_parmesean gouda = make_gouda() paremesean = make_parmesean()
然后你可以使用上面的任何方法来实际实现这些功能:
# cheeses.py class Cheese(object): def __init__(self, *args, **kwargs): #args -- tuple of anonymous arguments #kwargs -- dictionary of named arguments self.num_holes = kwargs.get('num_holes',random_holes()) def make_gouda(): return Cheese() def make_paremesean(): return Cheese(num_holes=15)
这是一个很好的封装技术,我认为它更像Pythonic.对我来说,这种做事方式更适合鸭子打字.你只是要求一个gouda对象而你并不关心它是什么类.
人们肯定更喜欢已发布的解决方案,但由于还没有人提到这个解决方案,我认为值得一提的是完整性.
@classmethod
可以修改该方法以提供不调用默认构造函数(__init__
)的替代构造函数.相反,使用创建实例__new__
.
如果无法根据构造函数参数的类型选择初始化类型,并且构造函数不共享代码,则可以使用此方法.
例:
class MyClass(set): def __init__(self, filename): self._value = load_from_file(filename) @classmethod def from_somewhere(cls, somename): obj = cls.__new__(cls) # Does not call __init__ obj._value = load_from_somewhere(somename) return obj
最好的答案就是上面关于默认参数的答案,但我写这篇文章很有意思,它确实符合"多个构造函数"的要求.使用风险由您自己承担.
新方法怎么样?
"典型的实现通过调用超类的创建类的新实例新使用超级(currentclass,CLS)()方法.新(CLS [,...])用适当的参数,然后修改之前根据需要新创建的实例归还它."
因此,您可以通过附加适当的构造函数方法让新方法修改类定义.
class Cheese(object): def __new__(cls, *args, **kwargs): obj = super(Cheese, cls).__new__(cls) num_holes = kwargs.get('num_holes', random_holes()) if num_holes == 0: cls.__init__ = cls.foomethod else: cls.__init__ = cls.barmethod return obj def foomethod(self, *args, **kwargs): print "foomethod called as __init__ for Cheese" def barmethod(self, *args, **kwargs): print "barmethod called as __init__ for Cheese" if __name__ == "__main__": parm = Cheese(num_holes=5)
num_holes=None
而是默认使用.然后检查是否num_holes is None
,如果是,随机化.无论如何,这就是我通常看到的.
更完全不同的构造方法可能需要一种返回实例的类方法cls
.