Imposing typesystem
on a class#
Properties#
It is often necessary to allow only certain values to be assigned to an attribute of a class. That can be achieved using properties. For instance, if we want to implement class Square
and allow only positive values for the square edge length a
, the implementation would be:
[1]:
class Square:
def __init__(self, a):
self.a = a
@property
def a(self):
return self._a
@a.setter
def a(self, value):
if value <= 0:
raise ValueError("Edge length must be positive (a>0).")
else:
self._a = value
Now, if we attempt to use an invalid value to set the edge length a
, an exception will be raised.
[2]:
s = Square(a=5) # Instantiate the class with correct attribute value
try:
s.a = -3
except ValueError:
print("Exception raised.")
Exception raised.
Imposing typesystem using ubermagutil.typesystem
#
In large projects with a large number of classes, a lot of input checks have to be performed. This makes the code grow and it causes a lot of code repetition. An example of the ubermagutil
usage for the previously shown Square
class is
[3]:
import ubermagutil.typesystem as ts
@ts.typesystem(a=ts.Scalar(positive=True))
class Square:
def __init__(self, a):
self.a = a
s = Square(a=5)
If we try to set an invalid value:
[4]:
try:
s.a = -3
except ValueError:
print("Exception raised.")
Exception raised.
Similarly, if we want to define MyClass
with the following attributes:
a
- an unsigned integerb
- a three-dimensional vectorc
- one of the allowed values from the set{'left', 'right'}
d
- variable of typelist
name
- a valid Python variable name
[5]:
@ts.typesystem(
a=ts.Scalar(expected_type=int, unsigned=True),
b=ts.Vector(size=3),
c=ts.Subset(sample_set={"left", "right"}, unpack=False),
d=ts.Typed(expected_type=list),
name=ts.Name(),
)
class MyClass:
def __init__(self, a, b, c, d, name):
self.a = a
self.b = b
self.c = c
self.d = d
self.name = name
Now, we can attempt passing invalid values expect them to be rejected by the imposed typesystem.
[6]:
mc = MyClass(
a=5, b=(1, 2, 3), c="right", d=[1, 2, "abc"], name="myclass"
) # valid initialisation
# Set mc.a with float
try:
mc.a = 3.14
except TypeError:
print("mc.a: Exception raised.")
# Set mc.b with a two-dimensional vector
try:
mc.b = (10, 11)
except ValueError:
print("mc.b: Exception raised.")
# Set mc.c with an invalid value of the string
try:
mc.c = "down"
except ValueError:
print("mc.c: Exception raised.")
# Set mc.c with an invalid value of tuple
try:
mc.d = (1, 2, 3, 4)
except TypeError:
print("mc.d: Exception raised.")
# Set mc.name with an invalid Python variable name
try:
mc.name = "Nikola Tesla" # contains spaces
except ValueError:
print("mc.name: Exception raised.")
mc.a: Exception raised.
mc.b: Exception raised.
mc.c: Exception raised.
mc.d: Exception raised.
mc.name: Exception raised.
Deleting an attribute#
Deleting an attribute is never allowed and an AttributeError
is raised.
[7]:
# Attempt to delete an attribute.
try:
del mc.a
except AttributeError:
print("Exception raised.")
Exception raised.