How-to: Use classes
There are two key things to keep in mind if you want to work with classes in a ArupCompute Python library:
- ArupCompute uses JSON to represent user-defined objects, including Python class objects.
- ArupCompute parses JSON objects into Python dictionaries, not to Python class objects.
This means:
- To pass a Python object to an ArupCompute Python calculation, you need to pass a JSON string representing the object.
- ArupCompute will take the JSON string representation of an object and convert it to a Python dictionary. Therefore, to work with the object, you can work directly with the dictionary representation of your object (ie. use its key/value data) or you can convert the dictionary to a class object and work directly with the object. Similarly, if your calculation returns a Python class object, ArupCompute will represent this object either as a JSON string (via ArupCompute website and API) or a Python dictionary (via arupcomputepy).
The snippet below demonstrates how to work with a Python class object as an input:
import math
# User-defined Python class
class Circle:
def __init__(self, name: str, radius: float):
self.name = name
self.radius = radius
def __str__(self):
return f"Hello, from circle {self.name}!"
def diameter(self):
return self.radius * 2
def area(self):
return math.pi * math.pow(self.radius, 2)
# Here's a Python calculation which takes a user-defined object as input.
# We've added a type hint for the input parameter, to indicate that the intended
# type of our input is the Circle type (based on our user-defined class).
# Note that type hints don't make Python check and enforce use of a type.
# ArupCompute uses JSON to represent user-defined objects. If we want to use this
# Python calculation via ArupCompute, to pass our Circle input, we need to pass a
# JSON object representing the circle (ie. {"name":"Circle A","radius":32}).
def summarise(c: Circle) -> ArupComputeResult:
# ArupCompute parses a JSON object (ie. JSON representation of your class object)
# to a Python dictionary. As such, we can add a check of whether the provided input
# is of the desired type (Circle) and create a new instance of the Circle object based
# on dictionary data if it is not. We can do this by mapping the dictionary kvp pairs to
# the required init arguments using the **-operator (if c is {"name":"Circle A","radius":32}
# Circle(**c) is equivalent to Circle(name = "Circle A", radius = 32))
circle = c if isinstance(c, Circle) else Circle(**c)
d = ArupComputeResultItem()
# Now that we are working with our Circle object, we can access the methods we defined
# in our Circle class (ex. our diameter, area and __str__ methods)
d.value = circle.diameter()
d.symbol = "diameter"
a = ArupComputeResultItem()
a.value = circle.area()
a.symbol = "area"
acr = ArupComputeResult()
acr.arupComputeResultItems.append(d)
acr.arupComputeResultItems.append(a)
acr.arupComputeReport_HTML = f'<p>{circle.__str__()}</p>'
return acr
# We can use our class as a type hint for the method's return data type. Again, this is only
# something we add as a hint, for readability. Python is not a statically typed language, it
# will not check that the type of the output is "correct".
def double_radius(c: Circle) -> Circle:
new_circle = c if isinstance(c, Circle) else Circle(**c)
new_circle.radius = new_circle.radius * 2
# Our object will be returned as JSON (via AC frontend, via API) or as a Python dictionary
# (via arupcomputepy)
return new_circle