new - python__




在Python中有多個構造函數的干淨,pythonic方法是什麼? (8)

num_holes=None使用num_holes=None作為默認值。 然後檢查num_holes is None是否num_holes is None ,如果是,則隨機化。 無論如何,這就是我通常所看到的。

更完全不同的構造方法可能需要返回cls實例的類方法。

我無法為此找到明確的答案。 AFAIK,在Python類中不能有多個__init__函數。 那麼我該如何解決這個問題呢?

假設我有一個名為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

你說什麼? 有另一種方法嗎?


一個人應該更喜歡已經發布的解決方案,但由於沒有人提到這個解決方案,我認為這是值得一提的完整性。

可以修改@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

其實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


如果你只有__init__使用num_holes=None作為默認是好的。

如果你想要多個獨立的“構造函數”,你可以將它們作為類方法提供。 這些通常稱為工廠方法。 在這種情況下,您可以將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(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()

我會使用繼承。 特別是如果將會有更多的差異比洞的數量。 特別是如果高達將需要有不同的成員,然後巴馬。

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


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

最好的答案是關於默認參數的上面的答案,但我很樂意寫這個,它確實符合“多個構造函數”的法案。 使用風險自負。

那麼new方法呢?

“典型的實現通過使用super(currentclass,cls).new(cls [,...])調用超類的new ()方法創建新類的實例,然後根據需要修改新創建的實例返回它。“

因此,您可以通過附加適當的構造函數方法來讓方法修改您的類定義。

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課程而解決的問題。 我用一個叫做value參數創建了一個__init____init__的代碼只是決定value參數的類型,並相應地處理數據。 如果你想要多個輸入參數,你只需將它們打包成一個元組並測試value是一個元組。

你這樣使用它:

>>> temp = YearQuarter(datetime.date(2017, 1, 18))
>>> print temp
2017-Q1
>>> temp = YearQuarter((2017, 1))
>>> print temp
2017-Q1

這就是__init__和類的其他部分的外觀:

import datetime


class YearQuarter:

    def __init__(self, value):
        if type(value) is datetime.date:
            self._year = value.year
            self._quarter = (value.month + 2) / 3
        elif type(value) is tuple:               
            self._year = int(value[0])
            self._quarter = int(value[1])           

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

當然可以用多個錯誤信息來擴展__init__ 。 我省略了這個例子。


class Cheese:
    def __init__(self, *args, **kwargs):
        """A user-friendly initialiser for the general-purpose constructor.
        """
        ...

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

    def _init_gauda(self, *args, **kwargs):
        """A special initialiser for Gauda cheese.
        """
        ...

    @classmethod
    def make_parmesan(cls, *args, **kwargs):
        new = cls.__new__(cls)
        new._init_parmesan(*args, **kwargs)
        return new

    @classmethod
    def make_gauda(cls, *args, **kwargs):
        new = cls.__new__(cls)
        new._init_gauda(*args, **kwargs)
        return new




constructor