2

I'm implementing an enum class and want to retrieve an aggregate information about the enum members. Each enum represents a breakout board with different number of pins, and I want to get the maximum number of pins over all boards. My approach was to add a class attribute _max_pins which is adjusted during __init__ of each member.

Problem is, it is not possible to define _max_pins ahead if the members as it would become a member, too. It does not help to define it after the members as then the members cannot access it during their __init__ I've looked at Declare a static variable in an enum class but the table can be set up after __init__ - that would be possible but would need to scan again all members after their initialization.

class IgelTyp(Enum):
    LED_1 = (24, 1)
    LED_2 = (24, 2)
    LED_3 = (16, 4)

    _max_pin = -1

    def __init__(self, pins, groups):
        if _max_pin < pins//groups:     # gives error
            _max_pin = pins//groups

    @classmethod
    def get_max_pins(cls):
        return cls._max_pin

Above code produces UnboundLocalError: local variable '_max_pin' referenced before assignment

When I move the assignment of _max_pin in front of the member definition it tells me that TypeError: __init__() missing 2 required positional arguments: ...

Edit 1 Actually, the TypeError is raised regardless where I put the assignment within the class. And when I use IgelTyp._max_pin = -1 I get a NameError: name 'IgelTyp' is not defined

Anyone has an efficient and readable solution?

14
  • Maybe you want IgelTyp._max_pin instead. Commented Jun 24, 2019 at 11:03
  • I tried, but does not work while initialization of IgelTyp is not done (see edit 1). Commented Jun 24, 2019 at 11:24
  • I mean inside the __init__ method. Commented Jun 24, 2019 at 11:29
  • Im not 100% certain, but shouldn't it be self._max_pin ... Commented Jun 24, 2019 at 11:38
  • @Goyo Unfortunately the same NameError is given. Commented Jun 24, 2019 at 11:40

1 Answer 1

3

One-off solution:

Change your __init__ to directly access the class's dictionary:

def __init__(self, pins, groups):
    max_pin = self.__class__.__dict__.get('max_pin', 0)
    self.__class__.max_pin = max(max_pin, pins//groups)

Interestingly, you could easily have each LED member store its own max_pin by adding this line at the end of __init__:

    self.max_pin = pins//groups

so:

>>> IgelTyp.max_pin
24

but:

>>> IgelType.LED_2.max_pin
12


Reusable solution

Create your own class attribute descriptor, and use that to shield max_pin from becoming an IgelTyp member:

class ClassVar:              # add (object) if using Python 2
    "a class variable"

    def __init__(self, value):
        # store initial value
        self.value = value

    def __get__(self, *args):
        # get value in ClassVar instance (only doable because all instances
        # share same value)
        return self.value

    def __set__(self, _, value):
        # save value in ClassVar instance (only doable because all instances
        # share same value)
        self.value = value

Descriptors, such as property usually store the value on the instance itself so that each instance can have it's own value (such as 24, 12, and 4 from your example); however, since you want to know the maximum number of pins across all instances, we just save that single value on the ClassVar instance.

The changes to IgelTyp:

class IgelTyp(Enum):

    LED_1 = (24, 1)
    LED_2 = (24, 2)
    LED_3 = (16, 4)

    max_pin = ClassVar(0)

    def __init__(self, pins, groups):
        self.max_pin = max(self.max_pin, pins//groups)

and in use:

>>> IgelTyp.max_pin
24
>>> IgelTyp.LED_2.max_pin
24
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for that answer @Ethan Furman. While it seems a proper and reusable solution, it is overshooting for my one-time occasion of use.
@Walter: I added a one-off solution at the top. Hope that works better for you.
Thank you for the addition - the one-off solution might not be as clear and straight as the Reusable solution, but it is what I was looking for!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.