Я пытаюсь добавить гибкость классу python
, чтобы он заметил, что один из аргументов init уже является экземпляром этого класса. Пропустите «Начальная ситуация», если вы не возражаете, как я сюда попал.
Начальная ситуация
У меня есть этот класс:
class Pet:
def __init__(self, animal):
self._animal = animal
@property
def present(self):
return "This pet is a " + self._animal
...
и есть много функций которые принимают экземпляр этого класса в качестве аргумента (def f(pet, ...)
). Все работало как ожидалось.
Затем я хотел добавить некоторую гибкость использованию этих функций: если вызывающая сторона передает экземпляр Pet
, все продолжает работать как прежде. Во всех остальных случаях создается экземпляр Pet
. Один из способов добиться этого, выглядит следующим образом:
def f(pet_or_animal, ...):
if isinstance(pet_or_animal, Pet): #Pet instance was passed
pet = pet_or_animal
else: #animal string was passed
pet = Pet(pet_or_animal)
...
Это также работает, как и ожидалось, но эти строки повторяются в каждой функции. Не DRY, не хорошо.
Цель
Итак, я хотел бы извлечь if
/ else
из каждой функции и интегрировать его в класс Pet сам. Я попытался изменить метод __init__
на
class PetA: #I've changed the name to facilitate discussion here.
def __init__(self, pet_or_animal):
if isinstance(pet_or_animal, PetA):
self = pet_or_animal
else:
self._animal = pet_or_animal
...
и запустить каждую функцию с помощью
def f(pet_or_animal, ...):
pet = PetA(pet_or_animal)
...
Однако это не работает. Если экземпляр Pet передается, все хорошо, но если вызывается строка, экземпляр Pet создается неправильно.
Текущее (безобразное) решение
Что равно работать, это добавить метод класса в класс, например:
class PetB: #I've changed the name to facilitate discussion here.
@classmethod
def init(cls, pet_or_animal):
if isinstance(pet_or_animal, PetB):
return pet_or_animal
else:
return cls(pet_or_animal)
def __init__(self, animal):
self._animal = animal
...
, а также изменить функции на
def f(pet_or_animal, ...):
pet = PetB.init(pet_or_animal) #ugly
...
Вопросы
- Кто-нибудь знает, как изменить класс
PetA
так, чтобы он имел намеченное поведение? Чтобы быть уверенным, вот быстрый тест:
pb1 = PetB.init('dog')
pb2 = PetB.init(pb1) #correctly initialized; points to same instance as pb1 (as desired)
pa1 = PetA('cat')
pa2 = PetA(pa1) #incorrectly initialized; pa1 != pa2
В целом, это правильный путь к go о добавлении этой гибкости? Другим вариантом, который я рассмотрел, было написание отдельной функции, чтобы просто выполнять проверку, но это тоже довольно уродливо и еще одна вещь, которую нужно отслеживать. Я бы предпочел держать все аккуратно и завернутым в самом классе.
И последнее замечание: я понимаю, что некоторые люди могут найти добавленный метод класса (petB
) более элегантным решение. Причина, по которой я предпочитаю добавить метод __init__
(petA
), заключается в том, что в моем реальном использовании я уже допускаю множество различных типов аргументов инициализации. Итак, уже есть список операторов if
/ elif
/ elif
/ ..., которые проверяют, какая из возможностей используется создателем. Я хотел бы расширить это еще на один случай, а именно, если передан инициализированный экземпляр.
Большое спасибо