Многократное наследование Python __new__ и __init__ со строкой и вторым классом

Я пытаюсь создать производный класс, который наследует от str тип и второй класс. Это проблематично, так как str тип не просто вызывает __init__, но __new__ Метод из-за его неизменности. Я знаю что для __init__ и супер, чтобы хорошо работать, вы должны иметь одну и ту же структуру вызова до конца. Однако следующая реализация терпит неудачу:

class base(object):
    def __new__(cls, *args, **kwargs):
        print "NEW  BASE:", cls, args, kwargs
        return super(base, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print "INIT BASE", args, kwargs

class foo(base, str):
    def __new__(cls, *args, **kwargs):
        return super(foo, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        super(foo, self).__init__(*args, **kwargs)

Вот foo('cat') работает с:

>> NEW  BASE: <class '__main__.foo'> ('cat',) {}
>> INIT BASE ('cat',) {}

но с аргументом foo('cat', x=3)не получается:

>> NEW  BASE: <class '__main__.foo'> ('cat',) {'x': 3}
Traceback (most recent call last):
  File "inh.py", line 19, in <module>
    foo('cat', x=3)
  File "inh.py", line 12, in __new__
    return super(foo, cls).__new__(cls, *args, **kwargs)
  File "inh.py", line 4, in __new__
    return super(base, cls).__new__(cls, *args, **kwargs)
TypeError: str() takes at most 1 argument (2 given)

Я могу заставить это работать, изменив base.__new__ метод для:

def __new__(cls, *args, **kwargs):
    return super(base, cls).__new__(cls)

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

Как правильно наследовать от строки и второго класса?

1 ответ

Вы не можете просто сделать

def __new__(cls, *args, **kwargs):
    return super(base, cls).__new__(cls)

потому что это вызовет неправильный вызов для новой строки (вы не передадите разрешенный аргумент

>>> foo('t')
NEW  BASE: <class '__main__.foo'> ('t',) {}
INIT BASE ('t',) {}
''

Вы должны сделать что-то вроде

def __new__(cls, *args, **kwargs):
    return super(base, cls).__new__(cls, *args[:1])

Но это может что-то сломать, если вы будете использовать base класс как миксин для класса, который __new__ метод принимает более одного аргумента.

как вариант, может быть, вы должны иметь класс, унаследованный от str но с переопределенным новым методом:

class CarelessStr(str):
    def __new__(cls, *args, **kwargs):
        return super(CarelessStr, cls).__new__(cls, *args[:1])

class foo(base, CarelessStr):
    def __new__(cls, *args, **kwargs):
        return super(foo, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        super(foo, self).__init__(*args, **kwargs)
Другие вопросы по тегам