Создание __repr__ для печати данных подкласса - PullRequest
0 голосов
/ 29 января 2020

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

from collections import Counter

class Component:
    cnt = Counter()
    def __init__(self, _type, **kwargs):
        Component.cnt[_type] += 1
        self.type = _type
        self.identifier = f'{self.type[0]}{Component.cnt[_type]}'
        self._kwargs = kwargs
    def __repr__(self):
        s = f'{self.__class__.__name__}('
        for k, v in self._kwargs.items():
            s += f'{k}={v!r}, '
        s = s.strip(', ') + f') # identifier: {self.identifier}'
        return s

class Battery(Component):
    # outbound is positive terminal
    def __init__(self, voltage):
        super().__init__(self.__class__.__name__, voltage=voltage)
        self.voltage = voltage

>>> b=Battery(9)
>>> b
Battery(voltage=9) # identifier: B1

В частности, self._kwargs выглядит как хак? Что может быть лучшим способом сделать это? Или есть ли лучший, более питонский c способ сделать вышеуказанное, чем я сейчас делаю?

1 Ответ

1 голос
/ 29 января 2020

Нет необходимости передавать self.__class__.__name__ в super().__init__ - метод __init__ суперкласса может обращаться к нему напрямую, точно так же, как ваш метод __repr__. Таким образом, атрибут self.type является избыточным.

Вот разумный способ написать __repr__ в базовом классе: вместо использования собственного атрибута _kwargs вы можете использовать собственный __dict__ объекта. Сюда входит атрибут identifier, поэтому вам не нужно добавлять его отдельно.

from collections import Counter

class Component:
    cnt = Counter()
    def __init__(self, **kwargs):
        _type = self.__class__.__name__
        Component.cnt[_type] += 1
        self.identifier = _type[0] + str(Component.cnt[_type])
        super().__init__(**kwargs) # co-operative subclassing
    def __repr__(self):
        return '{0}({1})'.format(
            self.__class__.__name__,
            ', '.join(
                '{0}={1!r}'.format(k, v)
                for k, v in self.__dict__.items()))

class Battery(Component):
    # outbound is positive terminal
    def __init__(self, *, voltage, **kwargs):
        self.voltage = voltage
        super().__init__(**kwargs) # co-operative subclassing

Пример:

>>> b = Battery(voltage=9) # keyword-only argument for co-operative subclassing
>>> b
Battery(voltage=9, identifier='B1')

Я пометил часть кода с комментариями о кооперативное наследование . В иерархии классов каждый метод __init__ может принимать свои аргументы в качестве ключевых слов и передавать его **kwargs методу super().__init__, чтобы вам не приходилось писать одни и те же имена аргументов несколько раз в обоих классах.

Также важно позвонить super().__init__ даже из вашего базового класса , если вы используете множественное наследование. Даже если вы не используете множественное наследование, это вызовет object.__init__, что позволит убедиться в отсутствии «неиспользуемых» аргументов, которые не были обработаны другим методом __init__.

...