Использование метаклассов для переопределения методов сложных встроенных функций

В качестве учебного упражнения я пытаюсь реализовать класс, который будет эмулировать поведение встроенного комплекса Python, но с другим поведение методов __str__и __repr__: я хочу, чтобы они печатались в формате...

(1.0,2.0)

...вместо:

(1+2j)

Сначала я попытался просто создать подкласс из complexи переопределение __str__и __repr__, но это имеет проблему, заключающуюся в том, что при вызове непереопределенных методов возвращается стандартный комплекс, и печатается в стандартном формате:

>>> a = ComplexWrapper(1.0,1.0)
>>> a
(1.0,1.0)
>>> b = ComplexWrapper(2.0,3.0)
>>> b
(2.0,3.0)
>>> a + b
(3+4j)

Когда желаемый вывод (3.0,4.0).

Я читал о метаклассах и думал, что они решат мою проблему.Начиная с ответа в Python Class Decorator, моя текущая реализация выглядит следующим образом:

def complex_str(z):
    return '(' + str(z.real) + ',' + str(z.imag) + ')'
def complex_repr(z):
    return '(' + repr(z.real) + ',' + repr(z.imag) + ')'

class CmplxMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs['__str__'] = complex_str
        attrs['__repr__'] = complex_repr
        return super(CmplxMeta, cls).__new__(cls, name, bases, attrs)

class ComplexWrapper(complex):
    __metaclass__ = CmplxMeta

К сожалению, это похоже на то же поведение, что и предыдущее решение (например, когда два экземпляра ComplexWrapperдобавляются друг к другу).

Признаюсь, я не совсем понимаю метаклассы. Может быть, мою проблему можно решить по-другому?

Конечно, я мог бы вручную переопределить соответствующие методы, такие как __add__, __subtract__и т. д. Но это было бы очень повторяющимся, поэтому я бы предпочел более элегантное решение.

Любая помощь приветствуется.


РЕДАКТИРОВАТЬ: Ответ на ответ agf:

Итак, ряд вещей, которые я не понимаю в вашем коде:

  1. Где метод __new__метода ReturnTypeWrapperметакласс получает свои аргументы? Если они передаются автоматически, я бы ожидал, что в этом случае name = "Complex", bases = (complex), dict = {}. Это правильно? Является ли этот метод автоматической передачи данных класса специфичным для метаклассов?

  2. Почему вы используете cls = type.__new__(mcs, name, bases, dct)вместо cls = type(mcs, name, bases, dct)? Это просто для того, чтобы избежать путаницы с «другим значением» type()?

  3. Я скопировал ваш код и добавил свои специальные реализации __str__и __repr__в ваш класс ComplexWrapper. Но это не работает; печать любого объекта типа Complexпросто печатается в стандартном формате Python.Я этого не понимаю, так как два метода должны были быть выбраны в цикле for метакласса, но впоследствии должны были быть переопределены моими определениями.

Соответствующий раздел моего кода:

class Complex(complex):
    __metaclass__ = ReturnTypeWrapper
    wrapped_base = complex
    def __str__(self):
        return '(' + str(self.real) + ',' + str(self.imag) + ')'
    def __repr__(self):
        return '(' + repr(self.real) + ',' + repr(self.imag) + ')'

И его поведение:

>>> type(a)

>>> a.__str__

>>> a.__str__()
'(1+1j)'
>>> 

Еще раз спасибо за ваш ответ и не стесняйтесь редактировать/удалять приведенное выше, если вы решите их в своем ответе!

9
задан Community 23 May 2017 в 11:49
поделиться