Type-safe (bit)flags for python 3
Project description
Quick Intro
With this module you can define type-safe (bit)flags. The style of the flag definition is very similar to the enum definitions you can create using the standard enum module of python 3.
Defining flags with the class syntax:
>>> from flags import Flags
>>> class TextStyle(Flags):
>>> bold = 1 # value = 1 << 0
>>> italic = 2 # value = 1 << 1
>>> underline = 4 # value = 1 << 2
In most cases you just want to use the flags as a set (of bool variables) and the actual flag values aren’t important. To avoid manually setting unique flag values you can use auto assignment. To auto-assign a unique flag value use an empty iterable (for example empty tuple or list) as the value of the flag. Auto-assignment picks the first unused least significant bit for each auto-assignable flag in top-to-bottom order.
>>> class TextStyle(Flags):
>>> bold = () # value = 1 << 0
>>> italic = () # value = 1 << 1
>>> underline = () # value = 1 << 2
As a shortcut you can call a flags class to create a subclass of it. This pattern has also been stolen from the standard enum module. The following flags definition is equivalent to the previous definition that uses the class syntax:
>>> TextStyle = Flags('TextStyle', 'bold italic underline')
Flags have human readable string representations and repr with more info:
>>> print(TextStyle.bold)
TextStyle.bold
>>> print(repr(TextStyle.bold))
<TextStyle.bold bits=0x0001 data=None>
The type of a flag is the flags class it belongs to:
>>> type(TextStyle.bold)
<class '__main__.TextStyle'>
>>> isinstance(TextStyle.bold, TextStyle)
True
You can combine flags with bool operators. The result is also an instance of the flags class with the previously described properties.
>>> result = TextStyle.bold | TextStyle.italic
>>>
>>> print(result)
TextStyle(bold|italic)
>>> print(repr(result))
<TextStyle(bold|italic) bits=0x0003>
Operators work in a type-safe way: you can combine only flags of the same type. Trying to combine them with instances of other types results in error:
>> result = TextStyle.bold | 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'TextStyle' and 'int'
>>>
>>> class OtherFlags(Flags):
... flag0 = ()
...
>>> result = TextStyle.bold | OtherFlags.flag0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'TextStyle' and 'OtherFlags'
Flags and their combinations (basically the instances of the flags class) are immutable and hashable so they can be used as set members and dictionary keys:
>>> font_files = {}
>>> font_files[TextStyle.bold] = 'bold.ttf'
>>> font_files[TextStyle.italic] = 'italic.ttf'
>>> font_files == {TextStyle.bold: 'bold.ttf', TextStyle.italic: 'italic.ttf'}
True
The flags you define automatically have two “virtual” flags: no_flags and all_flags. no_flags is basically the zero flag and all_flags is the combination of all flags you’ve defined:
>>> TextStyle.no_flags
<TextStyle() bits=0x0000>
>>> TextStyle.all_flags
<TextStyle(bold|italic|underline) bits=0x0007>
Testing whether specific flags are set:
>>> result = TextStyle.bold | TextStyle.italic
>>> bool(result & TextStyle.bold) # 1. oldschool bit twiddling
True
>>> TextStyle.bold in result # 2. in operator
True
>>> result.bold # 3. attribute-style access
True
>From the above testing methods the attribute-style access can only check the presence of a single flag. With the & and in operators you can check the presence of multiple flags at the same time:
>>> bool((TextStyle.bold | TextStyle.italic) & TextStyle.all_flags)
True
>>> (TextStyle.bold | TextStyle.italic) in TextStyle.all_flags
True
If for some reason you need the actual integer value of the flags then you can cast them to int:
>>> int(TextStyle.bold)
1
You can convert the int() and str() representations of flags back into flags instances:
>>> TextStyle(2)
<TextStyle.italic bits=0x0002 data=None>
>>> TextStyle('TextStyle.bold')
<TextStyle.bold bits=0x0001 data=None>
Installation
pip install py-flags
Alternatively you can download the distribution from the following places:
Introduction
We can use integers as an effective storage for a bunch of bool variables packed together. If we name the individual bits of the integer and we aren’t interested in their individual position/value then we can also treat our integer as:
A set: a named bool variable - a bit from the integer - is part of the set if it’s value is True/1.
An object that has (of course named) bool attributes.
As usual this has its own pros and cons:
Pros:
You can store several bool values in a single object:
It’s easy to pass it as a single function parameter and function signatures don’t have to be refactored in case of adding/removing bool variables that are part of the flag.
Performing bool operations on flags can result in simpler and easier to read code than doing the same with individual bool variables that make up the integer.
Cons:
treat flags as a bunch of bool variables packed together into an integer. If we give each bit a name in the integer and the actual integer value of each bit doesn’t matter then we can also treat this as a set of items/flags.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.