我找不到一个明确的答案。据我所知,在Python类中不能有多个__init__函数。那么如何解决这个问题呢?

假设我有一个名为Cheese的类,它具有number_of_holes属性。我怎么能有两种方法来创建奶酪对象…

其中一个需要像这样的洞的数量:帕玛森=奶酪(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

你说呢?还有别的办法吗?


当前回答

我还没有看到一个直截了当的答案。想法很简单:

使用__init__作为“基本”构造函数,因为python只允许一个__init__方法 使用@classmethod创建任何其他构造函数并调用基本构造函数

这是一个新的尝试。

 class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def fromBirthYear(cls, name, birthYear):
        return cls(name, date.today().year - birthYear)

用法:

p = Person('tim', age=18)
p = Person.fromBirthYear('tim', birthYear=2004)

其他回答

使用num_holes=None作为默认值。然后检查num_holes是否为None,如果是,则随机化。这就是我通常看到的情况。

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

实际上,对于“魔幻”价值观来说,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

由于我最初的回答受到了批评,因为我的特殊用途构造函数没有调用(唯一的)默认构造函数,我在这里发布了一个修改版本,以尊重所有构造函数都应该调用默认构造函数的愿望:

class Cheese:
    def __init__(self, *args, _initialiser="_default_init", **kwargs):
        """A multi-initialiser.
        """
        getattr(self, _initialiser)(*args, **kwargs)

    def _default_init(self, ...):
        """A user-friendly smart or general-purpose initialiser.
        """
        ...

    def _init_parmesan(self, ...):
        """A special initialiser for Parmesan cheese.
        """
        ...

    def _init_gouda(self, ...):
        """A special initialiser for Gouda cheese.
        """
        ...

    @classmethod
    def make_parmesan(cls, *args, **kwargs):
        return cls(*args, **kwargs, _initialiser="_init_parmesan")

    @classmethod
    def make_gouda(cls, *args, **kwargs):
        return cls(*args, **kwargs, _initialiser="_init_gouda")

最好的答案是上面关于默认参数的那个,但我很高兴写这个,而且它确实符合“多个构造函数”的要求。使用风险自负。

新方法怎么样?

典型的实现通过使用super(currentclass, cls)调用超类的new()方法来创建类的新实例。使用适当的参数New (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)

这就是我如何解决一个我必须创建的YearQuarter类。我创建了一个__init__,它对各种各样的输入非常宽容。

你可以这样使用它:

>>> from datetime import date
>>> temp1 = YearQuarter(year=2017, month=12)
>>> print temp1
2017-Q4
>>> temp2 = YearQuarter(temp1)
>>> print temp2
2017-Q4
>>> temp3 = YearQuarter((2017, 6))
>>> print temp3
2017-Q2 
>>> temp4 = YearQuarter(date(2017, 1, 18))
>>> print temp4
2017-Q1
>>> temp5 = YearQuarter(year=2017, quarter = 3)
>>> print temp5
2017-Q3

这就是__init__和类的其余部分的样子:

import datetime


class YearQuarter:

    def __init__(self, *args, **kwargs):
        if len(args) == 1:
            [x]     = args

            if isinstance(x, datetime.date):
                self._year      = int(x.year)
                self._quarter   = (int(x.month) + 2) / 3
            elif isinstance(x, tuple):
                year, month     = x

                self._year      = int(year)

                month           = int(month)

                if 1 <= month <= 12:
                    self._quarter   = (month + 2) / 3
                else:
                    raise ValueError

            elif isinstance(x, YearQuarter):
                self._year      = x._year
                self._quarter   = x._quarter

        elif len(args) == 2:
            year, month     = args

            self._year      = int(year)

            month           = int(month)

            if 1 <= month <= 12:
                self._quarter   = (month + 2) / 3
            else:
                raise ValueError

        elif kwargs:

            self._year      = int(kwargs["year"])

            if "quarter" in kwargs:
                quarter     = int(kwargs["quarter"])

                if 1 <= quarter <= 4:
                    self._quarter     = quarter
                else:
                    raise ValueError
            elif "month" in kwargs:
                month   = int(kwargs["month"])

                if 1 <= month <= 12:
                    self._quarter     = (month + 2) / 3
                else:
                    raise ValueError

    def __str__(self):
        return '{0}-Q{1}'.format(self._year, self._quarter)