чего - подклассы python




Что такое чистый, pythonic способ иметь несколько конструкторов в Python? (8)

Вот как я решил это для класса YearQuarter я должен был создать. Я создал __init__ с единственным параметром, называемым value . Код для __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__ с несколькими сообщениями об ошибках. Я пропустил их для этого примера.

Я не могу найти окончательного ответа на это. AFAIK, вы не можете иметь несколько функций __init__ в классе Python. Итак, как я могу решить эту проблему?

Предположим, у меня есть класс под названием Cheese с свойством number_of_holes . Как я могу создать два способа создания сырных объектов ...

  1. один из которых занимает несколько отверстий, подобных этому: parmesan = Cheese(num_holes = 15)
  2. и тот, который не принимает аргументов и просто рандомизирует свойство 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

Что ты говоришь? Есть ли другой способ?


Все эти ответы превосходны, если вы хотите использовать необязательные параметры, но другая возможность Pythonic - использовать метод класса для генерации псевдоконструктора в стиле фабрики:

def __init__(self, num_holes):

  # do stuff with the number

@classmethod
def fromRandom(cls):

  return cls( # some-random-number )

Лучший ответ - выше, чем аргументы по умолчанию, но мне было весело писать это, и это, безусловно, соответствует законопроекту для «нескольких конструкторов». Используйте на свой риск.

Как насчет new метода.

«Типичные реализации создают новый экземпляр класса, вызывая метод new () суперкласса, используя super (currentclass, cls). 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)

На самом деле 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


Почему вы думаете, что ваше решение «неуклюже»? Лично я предпочел бы один конструктор со значениями по умолчанию над несколькими перегруженными конструкторами в таких ситуациях, как ваш (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 of Cheese, хотя ...


Это хорошие идеи для вашей реализации, но если вы представляете пользователю интерфейс для создания сыра. Им все равно, сколько отверстий у сыра или какие внутренние ингредиенты входят в сыр. Пользователь вашего кода просто хочет «гоуда» или «пармезан» правильно?

Так почему бы не сделать это:

# 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, и вам все равно, какой он класс.


num_holes=None используйте num_holes=None . Затем проверьте, нет ли num_holes is None , и если да, то рандомизируйте. Во всяком случае, это то, что я обычно вижу.

Более радикально разные методы построения могут гарантировать класс, который возвращает экземпляр cls .


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