Использование метаклассов для переопределения методов комплексного построения
В качестве учебного упражнения я пытаюсь реализовать класс, который будет эмулировать поведение complex
встроенного complex
python, но с различным поведением методов __str__
и __repr__
: я хочу, чтобы они печатались в формате …
(1.0,2.0)
…вместо:
(1+2j)
Сначала я попробовал просто подклассифицировать из complex
и переопределить __str__
и __repr__
, но у этого есть проблема, что при __repr__
методов возвращается стандартный complex
и печатается в стандартном формате:
>>> 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__
и т. Д. Но это было бы очень повторяющимся, поэтому я предпочел бы более элегантное решение.
Любая помощь оценивается.
EDIT: Ответ на ответ agf:
Поэтому ряд вещей, которые я не понимаю о вашем коде:
-
Где метод
ReturnTypeWrapper
метаклассаReturnTypeWrapper
получает свои аргументы? Если они будут переданы автоматически, я бы ожидал в этом случае, чтоname = "Complex", bases = (complex), dict = {}
. Это верно? Является ли этот метод автоматической передачи данных класса конкретным для метаклассов? -
Почему вы используете
cls = type.__new__(mcs, name, bases, dct)
вместоcls = type(mcs, name, bases, dct)
? Следует ли просто избегать путаницы с «другим значением»type()
? -
Я скопировал ваш код и добавил свои специальные реализации
__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) <class 'Cmplx2.Complex'> >>> a.__str__ <bound method Complex.wrapper of (1+1j)> >>> a.__str__() '(1+1j)' >>>
Еще раз спасибо за ваш ответ и не стесняйтесь редактировать / удалять выше, если вы обратитесь к ним в своем ответе!
- Блокировка в файле dask.multiprocessing.get и добавление метаданных в HDF
- Откат объекта, копирование на запись, версия прокси и т. Д. В Python
- Могу ли я определить __repr__ для класса, а не для экземпляра?
- Чтение заголовка CR2 (Raw Canon Image) с использованием Python
- Может ли метакласс быть вызванным?
Ваш текущий подход не будет работать. Как вы определяете свой класс, это не проблема. Методы complex
создают новые экземпляры complex
при их вызове, а не используют type
входных объектов. Вы всегда будете возвращать экземпляры complex
а не ComplexWrapper
, поэтому ваши настраиваемые методы не будут вызываться:
>>> type(ComplexWrapper(1.0,1.0) + ComplexWrapper(2.0,3.0)) <type 'complex'>
Вместо этого вам нужно преобразовать новые complex
объекты, возвращаемые методами complex
чтобы возвращать объекты производного класса.
Этот метакласс обертывает все методы указанного базового класса и присоединяет завершенные методы к классу. Обертка проверяет, является ли возвращаемое значение экземпляром базового класса (но исключая экземпляры подклассов), а если это так, преобразует его в экземпляр производного класса.
class ReturnTypeWrapper(type): def __new__(mcs, name, bases, dct): cls = type.__new__(mcs, name, bases, dct) for attr, obj in cls.wrapped_base.__dict__.items(): # skip 'member descriptor's and overridden methods if type(obj) == type(complex.real) or attr in dct: continue if getattr(obj, '__objclass__', None) is cls.wrapped_base: setattr(cls, attr, cls.return_wrapper(obj)) return cls def return_wrapper(cls, obj): def convert(value): return cls(value) if type(value) is cls.wrapped_base else value def wrapper(*args, **kwargs): return convert(obj(*args, **kwargs)) wrapper.__name__ = obj.__name__ return wrapper class Complex(complex): __metaclass__ = ReturnTypeWrapper wrapped_base = complex def __str__(self): return '({0}, {1})'.format(self.real, self.imag) def __repr__(self): return '{0}({1!r}, {2!r})'.format(self.__class__.__name__, self.real, self.imag) a = Complex(1+1j) b = Complex(2+2j) print type(a + b)
Обратите внимание, что это не будет обертывать специальный метод __coerce__
, так как он возвращает tuple
complex
s; обертка может быть легко преобразована для просмотра последовательностей, если это необходимо.
__objclass__
несвязанных методов кажется недокументированным, но он указывает на класс, на котором определен метод, поэтому я использовал его для фильтрации методов, определенных на классах, отличных от того, с которого мы конвертируем. Я также использую его здесь для фильтрации атрибутов, которые не являются несвязанными методами.
- сравнение python с c / fortran
- Вариант построения протокола для использования многоядерных процессоров
- О метаклассе в Python
- блог пеликана определяет метаданные для категории index page
- __metaclass__ добавление недопустимого атрибута для созданного класса?
- Метаклассы в Python: несколько вопросов для уточнения
- Python 3 супер и метапрограммирование
- Автоматическое преобразование из геттера / сеттера в свойства
- 1 класс наследует 2 разных метакласса (abcmeta и определенные пользователем мета)
- Как извлечь метаданные из изображения с помощью python?
- Python metaclass для автоматического запуска метода класса на производном классе