Я не уверен, что это возможно, и мне интересно, правильный ли это путь или существует лучший способ сделать это.
Я создам игрушечный пример, чтобы показать то, что я ищу.
Предположим, у меня есть люди разных национальностей. Я создаю класс для каждой национальности, и все эти классы имеют метод приветствия.
class French:
def greeting(self):
print("Bonjour")
class English:
def greeting(self):
print("Hello")
class Spanish:
def greeting(self):
print("Hola")
Конечно, это можно сделать, имея один класс Person и используя условные выражения внутри метода Greeting, но мы можем представить, что функция Greeting будет более сложной и оправдывает наличие класса для каждой национальности.
Вы также можете представить, что все эти Nationalities на самом деле являются дочерним классом класса Person и переопределяют метод greeting
для вывода своего собственного языка.
Теперь представьте себе еще одну функцию, например «Спорт», с помощью которой я также создаю разные классы для разных видов спорта.
class Football:
def sport(self):
print("Football")
class Tennis:
def sport(self):
print("Tennis")
class Golf:
def sport(self):
print("Golf")
Опять же, вместо одного класса «Спорт» представьте, что функция «Спорт» более сложна, и оправданы разные классы. Или мы можем представить, что все эти классы являются дочерними элементами общего класса Sport и переопределяют метод sport.
Теперь мне нужен класс, который наследуется как от национальности, так и от вида спорта. Этот класс не будет добавлять новые методы или атрибуты, он просто наследуется от двух классов, поэтому у него есть собственное приветствие и спорт.
Я могу создать класс FrenchFootball с наследниками от French и Football.
class FrenchFootball(French, Football):
pass
Теперь я могу создать экземпляр этого класса, и у него будет как метод greeting
, так и метод sport
.
foo = FrenchFootball()
foo.greeting() # Bonjour
foo.sport() # Football
Теперь вот вопрос:
Дочернему классу не нужно добавлять какие-либо атрибуты или методы, он просто связывает национальность со видом спорта, поэтому полученный экземпляр говорит «Bonjour» и любит «Футбол».
Однако, чтобы создать такой экземпляр, мне нужно определить класс FrenchFootball, который просто наследует оба класса.
Если мне нужны все комбинации этого примера игрушки, мне нужно определить 9 дочерних классов, чтобы я мог создать их экземпляры по их имени.
Вопрос:
Есть ли способ создать экземпляр foo как English Tennis без необходимости кодирования класса EnglishTennis?
Вы можете себе представить, что наличие 10 видов спорта и 20 национальностей превращает создание дочерних классов в кошмар.
Я хотел бы что-то вроде (псевдокод)
foo = new Class(English, Tennis)
По сути, это говорит Python: мне нужен экземпляр пустого класса, который наследует английский и теннис, но на самом деле у меня нет кода класса EnglishTennis. Я определяю наследование при создании экземпляра класса.
я думаю, что то, что вы здесь описываете, ближе к миксину, чем к "обычному" наследованию
Взгляните на en.wikipedia.org/wiki/Prototype-based_programming.
Если вы используете оператор class
, то, как вы заметили, классы должны быть жестко запрограммированы в исходном файле.
Однако Python допускает динамическое создание классов. Достаточно просто вызвать встроенный type
с именем класса в качестве первого параметра, базами в качестве второго и (возможно, пустым) пространством имен в качестве третьего:
new_class = type("FrenchFootball", (French, Football), {})
Итак, в этом случае вы можете просто использовать что-то вроде itertools.Products
, чтобы создать все возможные базовые комбинации для ваших динамических классов, а также использовать их для создания имен классов и поместить их в словарь!
from itertools import product
class French:
...
...
class Football:
...
...
all_classes = {}
for bases in product([French, English, ...], [Football, Basketball, Golf, ...]):
name = bases[0].__name__ + bases[1].__name__
all_classes[name] = type(name, bases, {})
И вот оно. Поймите, что может не иметь смысла добавлять динамически созданные классы в глобальную область модуля, поскольку любой код, использующий этот класс, в любом случае должен будет жестко запрограммировать имя класса: получение их из словаря all_classes
допускает динамическое использование. Но если вы хотите, это просто вопрос обновления dict, возвращаемого вызовом globals()
:
globals().update(all_classes)
def create_combined_class(nationality_class, sport_class):
class_name = nationality_class.__name__ + sport_class.__name__
return type(class_name, (nationality_class, sport_class), {})
FrenchFootball = create_combined_class(French, Football)
EnglishTennis = create_combined_class(English, Tennis)
foo = FrenchFootball()
foo.greeting() # Bonjour
foo.sport() # Football
bar = EnglishTennis()
bar.greeting() # Hello
bar.sport() # Tennis
Вы можете написать функцию для создания классов по требованию, вместо того, чтобы жестко запрограммировать их все:
def makeClass(lang, sport):
/_class Combo(lang, sport):
/__pass
/_return Combo
(здесь мне пришлось использовать подчеркивания, а не фактические отступы). В идеале функция кэшировала бы уже созданные комбинации, а не пересоздавала бы классы.