This package contains the following:
simpleset.Constant
— An alternative to Python's Enum for defining immutable data sets in code. Supports a range of use cases, from simple list-of-strings to immutable named objects of arbitrary complexity.simpleset.strict
— Contains a class mixin (StrictMixin
) which adds strictness to set definitions, preventing the mixing of forms or inconsistent attribute sets, and an alternateConstant
class which includes the mixin. See the Customizing page for the details.simpleset.graphene
— Contains a class mixin (GrapheneMixin
) which adds helper methods for generating Graphene enums, and an alternateConstant
class which includes the mixin. See the Integrations page for the details.simpleset.Error
— A subclass ofException
that provides a pair of utility functions for defining sets or entire families of exception classes in a single line. (A variation on the enum theme.) See the Error page for the details.
Use Constant.define_set()
to specify the set name and instance names:
Color = Constant.define_set( "Color", "RED", "GREEN", "BLUE" )
This will:
- construct a subclass of
Constant
namedColor
- create an instance of
Color
for each name, which is known as the canonical name or cname - attach those instances to the Color class using their cnames
issubclass( Color, Constant ) # -> True
isinstance( Color.RED, Color ) # -> True
Color.RED.cname # -> "RED"
This is the simplest form of definition, referred to as "Form 1".
Instead of just a cname, you can specify both a cname and a value:
Color = Constant.define_set( "Color", RED="ff0000", GREEN="00ff00", BLUE="0000ff" )
Color.RED.value # -> "ff0000"
If you'd like to associate more more than than one value with each instance, use Form 3, which supports the setting of arbitrary attributes:
Color = Constant.define_set(
"Color",
RED = dict( hex="ff0000", like=True ),
GREEN = dict( hex="00ff00", like=True ),
BLUE = dict( hex="0000ff", like=False ),
)
Color.RED.hex # -> "ff0000"
Color.RED.like # -> True
Color.RED.value # "AttributeError: 'Color' object has no attribute 'value'"
We've already covered basic attribute access:
obj.cname
obj.value
obj.<arbitrary_attribute>
There are also two convenience properties:
obj.cname_pretty # "FOO_BAR" -> "Foo Bar"
obj.ordinal # definition order (from 1 to N, where N is the size of the set)
Notice that the ordinal
property gives you the equivalent of auto-numbered enums for free:
Color.RED.ordinal # -> 1
Color.BLUE.ordinal # -> 3
Lastly, instances support a number of magic methods, implementing various Python protocols:
str( Color.RED ) # -> "RED"
repr( Color.RED ) # -> "RED"
len( Color.RED ) # -> 3, because "RED" is three characters long
# comparisons
Color.RED == "RED" # -> True (compared by cname)
Color.RED < Color.BLUE # -> True (compared by ordinal)
# using as dict key
mydict[ Color.RED ] = "my favorite"
The Constant
class also provides a number of properties and methods for working with the entire set of instances.
The all
property and as_cnames()
method will return a list of instances or cnames, respectively.
Color.all # -> [ Color.RED, Color.GREEN, Color.BLUE ]
Color.as_cnames() # -> [ "RED", "GREEN", "BLUE" ]
You can also iterate the class:
for color in Color:
print( Color.cname )
Or get the count of instances:
len( Color ) # -> 3
Or get the length of the longest cname, which is useful for setting the size of a VARCHAR column in databases:
Color.max_length # -> 5 (because of GREEN)
To get a subset of instances, you can use the select
method to specify attribute names and values to match against each instance (simple equality only), or the filter
method to provide a function that will receive each instance and must return True or False to indicate matching.
Char = Constant.define_set(
"Char",
A = dict( ascii=65 ),
B = dict( ascii=66 ),
C = dict( ascii=67 ),
)
Char.select( ascii=66 ) # -> [ Char.B ]
Char.filter( lambda o: o.ascii < 67 ) # -> [ Char.A, Char.B ]
Note: If you pass multiple kwargs to select
, they will be "AND"-ed, meaning only instances that match all kwargs will be returned.
The get
method takes kwargs like the select
method, but returns a single instance instead of a list. Note: If your kwargs produce zero results or more than one result, ValueError
is raised.
Char.get( ascii=66 ) # -> Char.B
You can also check for inclusion using either an instance or a bare cname:
Color.RED in Color # -> True
"RED" in Color # -> True
If you're writing a function that takes an arg which might be either an instance or a cname, and want to normalize the arg into a proper instance, use member access notation:
# accepts both Color.RED and "RED"
def get_ordinal( color ):
color = Color[ color ]
return color.ordinal