Porting C defines the Python path

tl; dr What is the method of Pythonic (Py 3) to specify a large number of specific bit masks and constants? Use Enum or just load constants as class variables? And the advantages / disadvantages?

Background

I port C code to Python 3. C code has a large number of definitions that are used as bit masks:

#define ERR_1 = 0x8 #define ERR_2 = 0x2 #define ERR_4 = 0x100 ... 

I thought in Python that Python could be using Enum , I came across IntEnum , which means that I don't need to use .value everywhere, as I would with regular Enum

 from enum import IntEnum class Errors(IntEnum): BROKEN = 0x8 FUBARED = 0x4 GIVEUP = 0x7 print(0xFF & Errors.BROKEN) 

but it's even more verbose than just having print(0xFF & ERR_1) , which I could get if I had everything like consts.

+6
source share
4 answers

It all depends.

If the constants are logically integer (with them arithmetic, bitwise logics, etc.), they should be regular global variables. The flag values ​​fall here, as for the various os.open() flags, but if you just specified a constant to indicate which operation to perform, the enumeration is more suitable (or, even better, several driver functions).

Enumerated types are usually used when you have a (relatively) small set of values, and these values ​​are mainly compared only with each other (for example, if x is MyEnum.FIRST... elif x is MyEnum.SECOND , etc. ) In this case, you usually do not need IntEnum , because you should not often use .value to get started. In fact, IntEnum is basically a backward compatibility hack.

+2
source

I think that using enum more pythonic - there was a lot of effort to create this module, and not without reason.

The "standard" method used constants (for example, ERR_1 ), but it is ugly, buggy and very difficult to execute. That is why this module was developed.

Now that I have said a simple answer, I can also suggest that you use both at the same time:

 ERR_1 = 0x8 ERR_2 = 0x4 ERR_3 = 0x7 class Errors(IntEnum): BROKEN = ERR_1 FUBARED = ERR_2 GIVEUP = ERR_3 print(0xFF & Errors.BROKEN) print(0xFF & ERR_1) 

I think this is ugly, but you were torn between the two options, so I wanted to show you how to make them one.

Also worth reading:

+1
source

Using Enum is definitely the way to go, and you can even just have enumeration elements in the scope of the module:

 class Errors(IntEnum): BROKEN = 0x8 FUBARED = 0x4 GIVEUP = 0x7 globals().update(Errors.__members__) 

so your comparison will be:

 print(0xFF & BROKEN) 

The only time it makes sense not to use Enum is that you have several names that are not actually duplicated, but are mapped to the same value, since duplicate values ​​will be displayed with the same name:

 class Bad(Enum): BROKEN = 0x01 MISSING = 0x01 BLUE = 0x01 

But if you have different names for unique values, Enum is the way to go.

+1
source

I don’t think there is any reason to have them as part of a class.

errors module

 # errors.py BROKEN = 0x8 FUBARED = 0x4 GIVEUP = 0x7 

main module

 import errors print(0xFF & errors.BROKEN) 
0
source

All Articles