Множественное наследование без определения класса

Я не уверен, что это возможно, и мне интересно, правильный ли это путь или существует лучший способ сделать это.

Я создам игрушечный пример, чтобы показать то, что я ищу.

Предположим, у меня есть люди разных национальностей. Я создаю класс для каждой национальности, и все эти классы имеют метод приветствия.

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. Я определяю наследование при создании экземпляра класса.

Вы можете написать функцию для создания классов по требованию, вместо того, чтобы жестко запрограммировать их все: def makeClass(lang, sport): / _class Combo(lang, sport): / __pass / _return Combo (здесь мне пришлось использовать подчеркивания, а не фактические отступы). В идеале функция кэшировала бы уже созданные комбинации, а не пересоздавала бы классы.

jasonharper 01.07.2024 00:35

я думаю, что то, что вы здесь описываете, ближе к миксину, чем к "обычному" наследованию

folen gateis 01.07.2024 01:25

Взгляните на en.wikipedia.org/wiki/Prototype-based_programming.

chepner 01.07.2024 04:15
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вы используете оператор 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

Другие вопросы по теме