Множественное наследование Python с абстрактным классом вызывает метод __new__ с неправильным количеством аргументов

У меня следующая ситуация:

class AbstractSpeaker(ABC):
    @abstractmethod
    def say_hello(self)->None:
         pass

    """... other abstract and maybe even concrete methods ..."""

class BasicGreeter:
  """implements a basic say_hello method so that Speakers
  don't need to implement it"""
  def __new__(cls):
      r=object.__new__(cls)
      r._language=Language.English #assume that Language is an appropriate Enum
      return r

 @property
 def language(self)->Language: 
    return self._language

 @language.setter
 def language(self, newLanguage:Language):
    self._language=newLanguage

 def say_hello(self)->None:
    if self.language is Language.English:
       print("Hello")
    elif self.language is Language.German:
       print("Hallo")
    elif self.language is Language.Italian:
      print("Ciao")
    else:
      print("Hi")

class Person(BasicGreeter, AbstractSpeaker):
    def __init__(self, name):
       self.name=name
    """goes on to implement everything AbstractSpeaker speaker commands but should
       use the say_hello method from BasicGreeter, who should also hold the language
       information.
    """

Но когда я хочу создать нового человека, я получаю TypeError:

>>> peter=Person('Peter')
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
peter=Person('Peter')
TypeError: __new__() takes 1 positional argument but 2 were given

Теперь я понимаю, что это как-то связано с тем фактом, что я реализовал метод новый в BasicGreeter, потому что, когда я переписываю BasicGreeter, чтобы использовать метод в этом вместо метода новый, а затем явно вызывать super.в этом() в инициализации Person оно работает. Но я хотел знать, есть ли способ сделать это с помощью метода новый, поскольку необходимость вызывать super.в этом() в каждой инициализации динамиков может быть источником ошибки, если ее когда-либо забудут. Любые идеи, можно ли это (класс, реализующий базовое соответствие в некоторых областях для абстрактного класса, чтобы дочерним элементам этого абстрактного класса не нужно было реализовывать один и тот же код)? Я знаю, что всегда мог бы пойти по пути 3-го поколения, сделав BasicGreeter абстрактным классом, но я думаю, что это более элегантный способ, поскольку он позволяет использовать BasicGreeter для других семейств, например. У меня может быть класс Robot с подклассом FriendlyRobot, который также может использовать наследование от BasicGreeter, но робот не обязательно является говорящим, и наоборот.

Этого не должно быть в __new__. Я действительно не мог понять, почему вы не используете __init__...

Tomerikoo 17.03.2022 13:30

При использовании super необходимо сохранить подпись переопределенных методов совместимый с тем, что они переопределяют. Если ваш класс добавляет является параметром, он должен гарантировать, что аргумент будет передан в super, потому что предок некоторый этого не ожидает.

chepner 17.03.2022 13:34

@Tomerikoo, поскольку в этом нужно вызывать дополнительно, новый вызывается автоматически.

user3284214 17.03.2022 13:41

Но вызов super внутри __init__ — это стандартный идиоматический способ наследования. Использование __new__ только для того, чтобы обойти это, является однозначным. Что плохого в том, чтобы вызвать super? В любом случае, ошибка довольно очевидна: ваш __new__ принимает только класс, но вы передаете ему дополнительные аргументы.

Tomerikoo 17.03.2022 14:43

В этом случае я хотел бы избежать этого в Person, поскольку BasicGreeter не является основным предком Person, а скорее родственником на расстоянии, который оставил полезное наследие, в данном случае функцию say_hello и свойство языка. Но хорошо, я изменил новый метод BasicGreeter, чтобы использовать аргумент * args и супер вместо объекта, и теперь он работает нормально.

user3284214 17.03.2022 17: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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
5
35
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Ошибка дает вам решение, просто позвольте методу новый принимать аргументы, даже если вы их не используете:

from abc import ABC, abstractmethod

class Language():
    English = 'english'

class AbstractSpeaker(ABC):
    @abstractmethod
    def say_hello(self)->None:
         pass

    """... other abstract and maybe even concrete methods ..."""

class BasicGreeter:
    """implements a basic say_hello method so that Speakers don't need to implement it"""
    def __new__(cls, *args, **kwargs):   # Fix to your code
        # print(args,kwargs)
        r=object.__new__(cls)
        r._language=Language.English 
        return r
    @property
    def language(self)->Language: 
        return self._language

    @language.setter
    def language(self, newLanguage:Language):
        self._language=newLanguage

    def say_hello(self)->None:
        if self.language is Language.English:
            print("Hello")
        elif self.language is Language.German:
            print("Hallo")
        elif self.language is Language.Italian:
            print("Ciao")
        else:
            print("Hi")

class Person(BasicGreeter, AbstractSpeaker):
    def __init__(self, name):
        self.name=name
        
    """goes on to implement everything AbstractSpeaker speaker commands but should
       use the say_hello method from BasicGreeter, who should also hold the language
       information.
    """

Этот кусок кода работает, сказал что, я не уверен в том, что вам нужна такая структура внутри новый, это способ сделать синглтон?

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

user3284214 17.03.2022 13:38

Не используйте object; используйте super. Если BasicGreeter появляется непосредственно перед object в MRO, он делает то же самое. Если он делает нет, то object.__new__ вызывается преждевременно, а поскольку object.__new__ не использует super, никакой другой метод __new__ класса вызываться не может.

chepner 17.03.2022 13:40

Да, это технически возможно, вы можете использовать метакласс (предпочтительно) или даже, возможно, декоратор класса, в любом случае, обычно вызов «супер» был бы правильным способом здесь.

Pablo Ruiz 17.03.2022 13:48

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