lundi 30 novembre 2020

How to construct a configuration data class with no logic?

Within our project, we need to describe the properties of the HW we are working with. This information is then used within the program to make some decisions on how the program should execute.

I'm trying to find the optimal data structure for the job and I've been wondering what would be the pythonic approach in this case?

  • These properties do not change at runtime and should not be changed by accident.

  • Additionally, some of the HW is very similar, so it probably makes sense to use an inheritance mechanism to only specify the difference between two pieces of HW, instead of re-typing all the properties.

  • A lot of properties come from lists of possible values (enums).

  • It should be easy to add new properties or to rename existing ones. E.g. if another HW property becomes relevant for the execution of the program, we can easily add a new property (at coding time). If the naming of an existing property is too similar to the new one, we might need to rename it to make it clearer how they differ.

  • Last requirement (and this is the problematic one, I think) is that we want these properties to not depend on any logic (e.g. they should be direct assignments). Here is a clarification on the last requirement:

    name = "Joe" #OK
    
    if gender == Gender.male: #not OK
       name = "Joe"
    else:
       name = "Jane"
    

The reason for the last limitation is that we want to be able to have an overview of the values without executing the code (e.g. by glancing over it). While the above example is quite readable, it gets complicated quickly when a series of functions from imported modules is called to obtain the values. We'd like to avoid the need to run the code to see what the property values are.

At the moment we're using JSON file format, which doesn't allow any code by default, just contains pure values. But this brings other problems, e.g. no integrated support for inheritance, more difficult refactoring, dependency on 3rd party modules (pydantic)...

  • I've looked into dataclass, but this doesn't limit the assignment logic.
  • I've checked the @property decorator, but this doesn't limit the assignment logic either.
  • I've checked NamedTuple, but this creates issues with inheritance (and still allows logic to be added).
  • I've seen some posts that use source-code awareness with the ast.parse, but this seems hacky.

The closest I came was with the use of a dataclass (my apologies if the car example isn't very realistic):

from dataclasses import dataclass, field
from enum import Enum

class TransmissionType(Enum):
    MANUAL = "Manual"
    AUTOMATIC = "Automatic"
    
class BodyType(Enum):
    COUPE = "Coupe"
    WAGON = "Wagon"
    
@dataclass (frozen=True)
class Car:
    transmissionType: TransmissionType = field(init=False)
    bodyType: BodyType = field(init=False)
    numberOfDoors: int = field(init=False)
    consumptionPer100km: int = field(init=False)
    
@dataclass (frozen=True)
class FancyModel2019(Car):
    transmissionType = TransmissionType.MANUAL
    bodyType = BodyType.COUPE
    consumptionPer100km = 5
    if bodyType == BodyType.COUPE: #This should not be possible
        numberOfDoors = 3
    else:
        numberOfDoors = 5
    
@dataclass (frozen=True)
class FancyModel2020(FancyModel2019):
    consumptionPer100km = 4
    
myCar = FancyModel2020()
print(myCar.numberOfDoors)

However, this still allows the programmer to add logic, that calculates the values of the properties (numberOfDoors in the example above) instead of doing pure assignments only. I'm looking for a way to discourage / prevent this at coding time.

Is there a better-suited construct for this job?

Aucun commentaire:

Enregistrer un commentaire