coding-style - python有多个构造函数, 干净, pythonic方法是什么?

  显示原文与译文双语对照的内容

我找不到这个问题的确切答案。 在一个 python class, ) 就我所知,你不能有多个 __init__ 等功能。 那么什么是解决这个问题的好方法?

假设我有一个名为 Cheese的类,它带有 number_of_holes 属性。 如何创建cheese-objects的两种方法。。

  • 一个需要一些类似这样的洞: 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的默认值设为 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(random(100))

 @classmethod
 def slightly_holey(cls):
 return cls(random(33))

 @classmethod
 def very_holey(cls):
 return cls(random(66, 100))

现在创建这样的对象:


gouda = Cheese()
emmentaler = Cheese.random()
leerdammer = Cheese.slightly_holey()

为什么你认为你的解决方案是"笨重的"? 我个人倾向于在你的( 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):
 #.. .

在你的Cheese示例中,你可能想要使用Gouda子类。

如果你想使用可选的参数,所有这些答案都是极好的,但是另一个 Pythonic 可能是使用classmethod生成 factory-style pseudo-constructor:


def __init__(self, num_holes):

 # do stuff with the number

@classmethod
def fromRandom(cls):

 return cls( # some-random-number )

这些都是好的想法,但是如果你正在为你的实现模式和一个干酪生产接口给用户。 他们不关心奶酪有多少孔,也不在乎making奶酪的内部。 你的代码的用户只需要"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 。 我这样做的方式更符合duck打字的更多内容。 你只是在请求Gouda对象,而实际上并不关心它是什么类。

最好的答案是上面关于默认参数的答案,但我写的很有趣,它确实适合"多个构造函数"。 在你自己的风险下使用。

新的 方法。

使用"典型的实现将创建该类的一个新实例通过调用超类的 ( ) method. ( cls [,.. .] ) 用适当的参数,然后根据需要修改该newly-created实例之前先对它的。"

所以你可以通过附加有法修改你的类定义相应的构造函数方法。


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,如果是,随机。 这就是我通常看到的。

更完全不同的构造方法可能要求classmethod返回一个 cls 实例。

我将使用继承,尤其是如果有多于数量的孔的差异。 特别是如果Gouda需要有不同的成员集,那么 Parmesan 。


class Gouda(Cheese):
 def __init__(self):
 super(Gouda).__init__(num_holes=10)


class Parmesan(Cheese):
 def __init__(self):
 super(Parmesan).__init__(num_holes=15) 

...