Вопрос-Ответ

What is a clean "pythonic" way to implement multiple constructors?

Каков чистый "питонический" способ реализации нескольких конструкторов?

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

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


  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

Что вы скажете? Есть ли другой способ?

Переведено автоматически
Ответ 1

Использование 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()
Ответ 2

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

Ответ 3

Определенно следует предпочесть уже опубликованные решения, но поскольку никто еще не упомянул это решение, я думаю, его стоит упомянуть для полноты картины.

@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__
super(MyClass, obj).__init__() # Don't forget to call any polymorphic base class initializers
obj._value = load_from_somewhere(somename)
return obj
Ответ 4

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

def __init__(self, num_holes):

# do stuff with the number

@classmethod
def fromRandom(cls):

return cls( # some-random-number )
2024-03-10 05:13 python class