I am looking for feedback on this compact Python API for defining bitwise flags and setting/manipulating them.
Under the hood, FlagType extends int
and all operations are true bitwise, so there is little to no performance impact. Also, because they are all int
, any standard bitwise operation can be performed on them.
Usage Example
>>> class sec(FlagType):
... admin = 1
... read = 2
... write = 4
... usage = 8
...
>>> flags = +sec.read -sec.write +sec.usage
>>> x.read
True
>>> print(flags)
10
>>> repr(flags)
"<sec (0b1010) {'write': False, 'usage': True, 'admin': False, 'read': True}>"
This code arose out of the desire to replace:
class Name(Varchar):
InsertRead = True
InsertWrite = True
InsertRequired = True
UpdateRead = True
UpdateRequired = True
UpdateWrite = False
with:
class Name(Varchar)
flags = +Read +Write +Required -UpdateWrite
More Examples:
class color(FlagType):
red = 0b0001
blue = 0b0010
purple = 0b0011
_default_ = red
color()
:: <color (0b1) {'blue': False, 'purple': False, 'red': True}>
flags = +color.blue
flags
:: # Note the default of red came through
:: # Note that purple is also true because blue and red are set
:: <color (0b11) {'blue': True, 'purple': True, 'red': True}>
flags.blue
:: True
flags.red
:: True
flags -= color.blue
flags.blue
:: False
flags.purple
:: False
flags.red
:: True
flags
:: <color (0b1) {'blue': False, 'purple': False, 'red': True}>
flags[color.red]
:: True
Source Code:
class FlagMeta(type):
def __new__(metacls, name, bases, classdict):
if '_default_' in classdict:
def __new__(cls, value=classdict.get('_default_')):
return int.__new__(cls, value)
del classdict['_default_']
classdict['__new__'] = __new__
cls = type.__new__(metacls, name, bases, classdict)
for flagname,flagvalue in classdict.items():
if flagname.startswith('__'):
continue
setattr(cls, flagname, cls(flagvalue))
return cls
def __setattr__(cls, name, value):
if type(value) is not cls:
raise AttributeError("Attributes of class '{0}' must be instances of '{0}'.".format(cls.__name__))
if type(value) is FlagType:
raise AttributeError("Class '{0}' is read-only.".format(cls.name))
type.__setattr__(cls, name, value)
class FlagType(int, metaclass=FlagMeta):
def __pos__(self):
'''
Creates a new default instance of the same class and then adds the current
value to it.
'''
return type(self)() + self
def __neg__(self):
'''
Creates a new default instance of the same class and then subtracts the current
value from it.
'''
return type(self)() - self
def __add__(self, other):
'''
Adding only works with flags of this class or a more generic (parent) class
'''
if not isinstance(self, type(other)):
raise TypeError("unsupported operand type(s) for {0}: '{1}' and '{2}'".format(('+'), type(self), type(other)))
return type(self)(self | other)
def __sub__(self, other):
'''
Subtracting only works with flags of this class or a more generic (parent) class
'''
if not isinstance(self, type(other)):
raise TypeError("unsupported operand type(s) for {0}: '{1}' and '{2}'".format(('-'), type(self), type(other)))
return type(self)(self & ~other)
def __getattribute__(self, othername):
'''
If the requested attribute starts with __, then just return it.
Otherwise, fetch it and pass it through the self[...] syntax (__getitem__)
'''
if othername.startswith('__'):
return object.__getattribute__(self, othername)
else:
return self[getattr(type(self), othername)]
def __setattr__(self, name, val):
'''
Readonly.
'''
raise AttributeError("'{0}' object is readonly.".format(type(self).__name__))
def __getitem__(self, other):
'''
Passing an instance of this same class (or a parent class) to the item getter[]
syntax will return True if that flag is completely turned on.
'''
if not isinstance(self, type(other)):
raise TypeError("unsupported operand type(s) for {0}: '{1}' and '{2}'".format(('-'), type(self), type(other)))
return self & other == other
def __repr__(self):
'''
For example:
<FieldFlag (0b1001) {'Read': True, 'Required': False, 'Write': True, 'Execute': False}>
'''
fields = {}
for c in type(self).__mro__[0:-2]: #up to but not including (int, object)
for fieldname, fieldvalue in c.__dict__.items():
if not fieldname.startswith('__'):
fields[fieldname] = self[fieldvalue]
return '<' + type(self).__name__ + ' (' + bin(self) + ') ' + str(fields) + '>'
dict
. – Lattyware Feb 26 at 23:23