2. How to use¶
The main purpose of strong_typing is to help you create C++-like Struct, that is to say
classes where the attributes are ALL defined and cannot be of any type. An undefined
attribute cannot be added later on.
- To do this, we use several objects:
strong_typing.Structwhich must be overriddenstrong_typing.typed_parameters.<type>Parameterwhich will help us to define our attributes’ types
2.1. Create a Struct¶
Let’s create our first Struct !
# mystruct.py
from strong_typing import Struct
class MyStruct(Struct):
# It is not very interesting, but as a starter, let's create a
# Struct without any attribute
pass
# script.py
from mystruct import MyStruct
my_struct = MyStruct()
my_struct.any_attrib = 10 # raises AttributeError
And that’s it ! You just created your first strongly-typed Struct in Python !! An undefined attribute cannot be added at runtime.
2.2. Add parameters¶
Creating a Struct without parameters is not really interesting. Let’s add an int and a string to our class.
# mystruct.py
from strong_typing import Struct
from strong_typing.typed_parameters import (IntegerParameter,
StringParameter)
class MyStruct(Struct):
__ATTRIBUTES__=[IntegerParameter(name="my_int"),
StringParameter(name="my_str")]
We have now two attributes available for MyStruct. When someone will try to set them,
the given value will be converted into the requested type if possible. If the given value
cannot be converted, an error will be raised.
# script.py
from mystruct import MyStruct
my_struct = MyStruct()
my_struct.my_int = 10.0 # is correct (10 will be stored)
my_struct.my_int = "20" # is correct (20 will be stored)
my_struct.my_int = "aa" # will raise an Exception
my_struct.my_str = 10.0 # is correct ("10.0" will be stored)
There is no limit to the number of parameters. Just add as many as you want in the __ATTRIBUTES__ list and you’re good to go.
2.3. Parametrize the parameters¶
Now that we know how to add parameters, let’s see how much cooler they can make our life.
For this, let’s have a look at the base class of all parameters
-
class
strong_typing.typed_parameters._parameters.ParameterType(name, description, default, id=None)¶ Base type for parameters
Parameters: - name – Name of the parameter
- description – Description of the parameter
- default – Default value
- id – Token used to access the parameter
If
defaultisNone, it means the parameter is optional and can haveNoneas a value. Otherwise, setting the parameter toNoneactually resets it to the default value.If
idisNone, it will by default be the name of the parameter turned into a snakecase string. Otherwise, the only requirement is for the id to be a snakecase string (it doesn’t have to be related to the parameter’s name)Example: If name is ‘My Parameter’, then id will be ‘my_parameter’, and its value will be accessible by calling <owning_class>.my_parameter
This means we can customize the default value of our parameters, but also that we can change the token through which they are accessed (as long as the final token is in snake-case).
# mystruct.py
from strong_typing import Struct
from strong_typing.typed_parameters import IntegerParameter
class MyStruct(Struct):
__ATTRIBUTES__=[IntegerParameter(name="My integer", id="a", default=10),
IntegerParameter(name="My other integer", id="b", default=None)]
# script.py
from mystruct import MyStruct
my_struct = MyStruct()
print my_struct.a # prints 10
print my_struct.b # prints None
my_struct.a = 20
my_struct.a = None
print my_struct.a # prints 10
| Question: | Hold on ! In |
|---|
Good question ! Let’s examine that !
2.3.1. Parameters for immutable types¶
In our previous example, the defined parameters had a default value because numeric parameters (int, float, ...) have a default default value.
-
class
strong_typing.typed_parameters.IntegerParameter(name='', description='', default=0, normalizer=None, range=(None, None), id=None)¶ Bases:
strong_typing.typed_parameters._parameters.NumericParameterInteger parameter
-
class
strong_typing.typed_parameters.FloatParameter(name='', description='', default=0.0, normalizer=None, range=(None, None), id=None)¶ Bases:
strong_typing.typed_parameters._parameters.NumericParameterFloat parameter
You can see that a default value is defined for both integer and float. But you can also notice that new arguments are available. What are they ?
-
class
strong_typing.typed_parameters._parameters.NumericParameter(name, description, default, normalizer, range=(None, None), id=None)¶ Bases:
strong_typing.typed_parameters._parameters.ParameterTypeBase type for numeric parameters
Parameters: - normalizer – Extra-verification function
- range – Couple of values representing the lowest and highest possible values the parameter can take It is possible to define only one of the two and leave the other to None to have only a minimum or only a maximum
normalizeris a user-defined function allowing him to perform an extra-verification on the inserted value. For instance, a function can be added to accept only odd numbers, or perfect square number. In case the inserted value does not match the requirement, the function must provide a valid value instead of the inserted one.
normalizer and range allow us to add new constraints on our values. We can define min
and max values, but also more specific restriction, like “odd numbers only”
# mystruct.py
from strong_typing import Struct
from strong_typing.typed_parameters import IntegerParameter
def make_odd(x):
return (x/2)*2+1
class MyStruct(Struct):
__ATTRIBUTES__=[IntegerParameter(name="norm", normalizer=make_odd),
IntegerParameter(name="ranged", range=(10,20))]
# script.py
from mystruct import MyStruct
my_struct = MyStruct()
print my_struct.norm # prints 1
# the default is 0, but as we only accept odd numbers,
# 0 was transformed in 1
print my_struct.range # prints 10
# the default is 0, but as we only accept numbers
# between 10 and 20, 0 became 10
Another parameter that has a default default value is BoolParameter
-
class
strong_typing.typed_parameters.BoolParameter(name='', description='', default=False, id=None)¶ Bases:
strong_typing.typed_parameters._parameters.ParameterType
Nothing major to say about it
The last “standard” immutable available parameter is StringParameter
-
class
strong_typing.typed_parameters.StringParameter(name='', description='', default=None, id=None)¶ Bases:
strong_typing.typed_parameters._parameters.ParameterType
The only notable difference with the previous parameters is its behavior regarding the default
value. Unlike the others, a StringParameter does not allow to set a value to None, even if
it is the default value selected. A None default value will be transformed in an empty string.
Because of that, setting a string to “” is equivalent to setting it to None, ie to resetting it
to default value. As a result, if you want to allow your string to be empty, default value MUST
be “” or None (same as numeric or bool whose default must be None if you want this value to be
allowed).
# mystruct.py
from strong_typing import Struct
from strong_typing.typed_parameters import StringParameter
class MyStruct(Struct):
__ATTRIBUTES__=[StringParameter(name="str1", default="default"),
StringParameter(name="str2")]
# script.py
from mystruct import MyStruct
my_struct = MyStruct()
print my_struct.str1 # prints "default"
my_struct.str1 = "string"
print my_struct.str1 # prints "string"
my_struct.str1 = None # reset to default
print my_struct.str1 # prints "default"
my_struct.str1 = "" # ALSO reset to default
print my_struct.str1 # prints "default"
print my_struct.str2 # prints ""
my_struct.str1 = "string"
print my_struct.str1 # prints "string"
my_struct.str1 = None # reset to default
print my_struct.str1 # prints ""
my_struct.str1 = "" # ALSO reset to default
print my_struct.str1 # prints ""
2.3.2. A special type of immutable: Enum¶
Like in C++, it is possible to define Enums in our classes. There are several ways to implement Enums in Python, we have chosen to use the library enum which is automatically installed on Python3 (and can be installed via pip on Python2).
-
class
strong_typing.typed_parameters.EnumParameter(choices, name='', description='', default=None, id=None)¶ Bases:
strong_typing.typed_parameters._parameters.ParameterTypeParameter describing a set of choices
Parameters: - choices – Set of possible choices. It can be a list of strings, or an enum.Enum
- default – Default value
If
defaultisNone, the first of the available choices is selected as default.
You can see a choices argument in EnumParameter constructor, this is where you will insert
your enum. You can create an Enum, but you can also more simply give a list of strings.
| Warning: | The default value of an EnumParameter cannot be None. If default value is not set, the first
value of your list/enum will be used as default. |
|---|
# mystruct.py
from strong_typing import Struct
from strong_typing.typed_parameters import EnumParameter
import enum
class Options(enum.Enum):
a = 1
b = 2
class MyStruct(Struct):
__ATTRIBUTES__=[EnumParameter(name="choice1", choices=["a", "b"]),
EnumParameter(name="choice2", choices=Options, default=Choices.b)]
# script.py
from mystruct import MyStruct
my_struct = MyStruct()
print my_struct.choice1 # prints "a"
my_struct.choice1 = "b"
print my_struct.choice1 # prints "b"
my_struct.choice1 = None # reset to default
print my_struct.choice1 # prints "a"
print my_struct.choice2 # prints "<Choices.a : 1>"
my_struct.choice2 = Choices.b # works
my_struct.choice2 = "b" # also works (but b is converted in Choices.b)
2.3.3. Parameters for mutable types¶
Now let’s see how are handled the mutable types.
Lists are handled through VectorParameters.
-
class
strong_typing.typed_parameters.VectorParameter(type, name='', description='', default=None, id=None)¶ Handles list
Parameters: type – Type of the elements stored in the list
You can see a new argument type. It is here to define the type of element the list will contain.
| Warning: | For now, it is not possible for a list to contain other lists with a defined type. We wish we can add this in a latter version. |
|---|
As for the strings, it is not possible to have None as a default value. It will be replaced by an empty
list. Therefore, if you set your list parameter to None, it will actually be set to []. And if you
define a default value (which must be a list), then this list will be used if you set your parameter to None.
However, unlike a StringParameter without any default value, it IS possible to set your list to [] even
with a non-default value: you can just remove all elements from the list, without assigning it directly a
value.
Maps are currently not handled. This will probably be done in a future version
2.3.4. Other Struct as parameters¶
Finally, it is possible to define a Struct as parameter of another Struct.
-
class
strong_typing.typed_parameters.StructParameter(type, default=None, name='', description='', id=None)¶ Handles a Struct
Parameters: type – Type of the Struct to be used
type is the type of the Struct you want to use as parameter. If default is left to
None a default instance of Struct is used as default.