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!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.