简体   繁体   中英

Creating __repr__ to print subclass data

I would like to create a base class that has properties that all the children inherit, and also has a descriptive __repr__ method. Does the following seem like an acceptable implementation of this?

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

Specifically, does the self._kwargs seem like a hack? What might be a better way to do that? Or, is there a better, more pythonic way of doing the above than I'm currently doing?

It's not necessary to pass self.__class__.__name__ to super().__init__ - the superclass's __init__ method can access it directly, just the same as your __repr__ method can. So the self.type attribute is redundant.

Here is a reasonable way to write __repr__ in the base class: instead of using your own _kwargs attribute, you can use the object's own __dict__ . This includes the identifier attribute, so you don't need to add that separately.

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

Example:

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

I've labelled some of the code with comments about co-operative subclassing . In a class hierarchy, each __init__ method can take its arguments as keywords, and pass its **kwargs to the super().__init__ method so that you don't have to write the same argument names multiple times in both classes.

It's also important to call super().__init__ even from your base class if you are using multiple inheritance. Even if you don't use multiple inheritance, this will invoke object.__init__ which has the neat effect of making sure there are no "unused" arguments that weren't handled by another __init__ method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM