То, что вы хотите, иногда называется виртуальным конструктором , потому что экземпляры подкласса создаются конструктором базового класса.Это часто выполняется с помощью какой-то «фабричной» функции.
Однако, одна вещь, которую я не люблю в большинстве реализаций фабричных функций, состоит в том, что они часто реализуются таким образом, что фабричная функция требует ручного изменения при каждом добавлении другого подкласса.к иерархии классов.В лучших реализациях это может быть сведено к одному вызову некоторой другой «вспомогательной» функции для регистрации каждого подкласса.
В Python такая функция может быть реализована путем переопределения метода по умолчанию __new__()
базового класса (фактически делая егостатическая заводская функция).Затем в этом методе можно использовать метод __subclasses__()
объекта класса, чтобы найти их все, не требуя ручного вызова какого-либо вспомогательного метода «register».Таким образом, добавление подкласса к виртуально сконструированной иерархии классов происходит в основном автоматически.
Вот как применить эти понятия к классам примеров в вашем вопросе.Также обратите внимание, что я также изменил ваш код, чтобы он более точно соответствовал PEP 8 - Руководству по стилю для кода Python .
class Pet:
class UnknownType(Exception): pass # Custom Exception subclass.
def __init__(self, dictionary):
self.name = dictionary['name']
self.type = dictionary['type']
@classmethod
def _get_all_subclasses(cls):
""" Recursive generator of all subclasses of a class. """
for subclass in cls.__subclasses__():
yield subclass
for subclass in subclass._get_all_subclasses():
yield subclass
def __new__(cls, dictionary):
""" Create instance of appropriate subclass using string
value of 'type' in dictionary.
"""
kind = dictionary['type']
for subclass in cls._get_all_subclasses():
if subclass.kind == kind:
# Using "object" base class method avoids recursion here.
return object.__new__(subclass)
else: # no subclass with matching type found.
raise Pet.UnknownType(
'type "{}" is not recognized'.format(kind))
class Dog(Pet):
kind = 'Dog'
def __init__(self, dictionary):
super().__init__(dictionary)
self.weight = dictionary['weight']
class Cat(Pet):
kind = 'Cat'
def __init__(self, dictionary):
super().__init__(dictionary)
self.color = dictionary['color']
if __name__ == '__main__':
pet1 = {'name': 'Harry', 'type': 'Dog', 'weight': 100}
pet2 = {'name': 'Sally', 'type': 'Cat', 'color': 'blue'}
pet3 = {'name': 'Joe', 'type': 'Frog', 'eyecolor': 'brown'}
mypet1 = Pet(pet1)
mypet2 = Pet(pet2)
print(mypet1.__class__.__name__) # -> Dog
print(mypet2.__class__.__name__) # -> Cat
# Example showing use of custom Exception subclass.
try:
mypet3 = Pet(pet3)
except Pet.UnknownType as exc:
print('Error occurred:', exc)
# -> Error occurred: type "Frog" is not recognized
По сути, это всего лишь адаптацияКод в моем ответе на другой вопрос .