Coverage for Users/vladimirpavlov/PycharmProjects/parameterizable/src/mixinforge/parameterizable_mixin.py: 100%

1"""Basic infrastructure for parameterizable classes.

2

3 This module provides functionality for working with parameterizable classes:

4 classes that have (hyper)parameters which define an object's configuration,

5 but not its internal contents or data. Such parameters are typically

6 passed to the __init__ method.

7

8 The module provides an API for getting parameter values from an object,

9 and for converting the parameters to and from a portable dictionary

10(a dictionary with sorted str keys that only contains

11 basic types and portable sub-dictionaries).

12"""

13 import inspect

14 from typing import Any

15

16 from .dict_sorter import sort_dict_by_keys

17 from .json_processor import loadjs,dumpjs, JsonSerializedObject

18

19

20 class ParameterizableMixin:

21 """Base class for parameterizable classes.

22

23 Classes deriving from this base expose a stable set of configuration

24 parameters that define their behavior. Subclasses implement get_params

25 to return these parameters, which can then be serialized to and from

26 a portable JSON representation.

27

28 Note:

29 This class is intended to be subclassed. The default implementation of

30 get_params returns an empty mapping.

31 """

32

33

34 def get_params(self) -> dict[str, Any]:

35 """Return this instance's configuration parameters.

36

37 Parameters define the object's configuration but not its internal

38 contents or data. They are typically passed to __init__ at creation time.

39

40 Returns:

41 A mapping of parameter names to values.

42

43 Note:

44 Subclasses should override this method to return their specific

45 parameters. The default implementation returns an empty dictionary.

46 """

47 params = dict()

48 return params

49

50

51 def get_jsparams(self) -> JsonSerializedObject:

52 """Return this instance's parameters encoded as JSON.

53

54 Returns:

55 JSON string produced by dumpjs.

56 """

57 return dumpjs(self.get_params())

58

59

60 @classmethod

61 def get_default_params(cls) -> dict[str, Any]:

62 """Get the default parameters of the class as a dictionary.

63

64 Default values are taken from keyword parameters of __init__ and

65 returned as a key-sorted dictionary. Subclasses may override if default

66 computation requires custom logic.

67

68 Returns:

69 The class's default parameters sorted by key.

70 """

71 signature = inspect.signature(cls.__init__)

72 # Skip the first parameter (self/cls)

73 params_to_consider = list(signature.parameters.values())[1:]

74 params = {

75 p.name: p.default

76 for p in params_to_consider

77 if p.default is not inspect.Parameter.empty

78 }

79 sorted_params = sort_dict_by_keys(params)

80 return sorted_params

81

82

83 @classmethod

84 def get_default_jsparams(cls) -> JsonSerializedObject:

85 """Return default constructor parameters encoded as JSON.

86

87 Returns:

88 JSON string with default parameters.

89 """

90 return dumpjs(cls.get_default_params())

91

92

93 @property

94 def essential_param_names(self) -> set[str]:

95 """Names of parameters that define the object's core identity and behavior.

96

97 Essential parameters are those that fundamentally define an object's behavior

98 or identity - for example, the maximum number of trees in a random forest

99 or the maximum depth of a decision tree.

100

101 These parameters are typically immutable throughout the object's lifetime.

102

103 Note:

104 Subclasses should override this property to specify which parameters are

105 essential. The default implementation considers all parameters essential.

106

107 Returns:

108 Names of essential parameters.

109 """

110 return set(self.get_params().keys())

111

112

113 @property

114 def auxiliary_param_names(self) -> set[str]:

115 """Names of the object's auxiliary parameters.

116

117 Auxiliary parameters are parameters that do not fundamentally impact the

118 object's behavior or identity. These might include settings like logging

119 verbosity, debug flags, or probability thresholds for consistency checks.

120

121 Returns:

122 Set of auxiliary parameter names.

123 """

124 return set(self.get_params().keys()) - self.essential_param_names

125

126

127 def get_essential_params(self) -> dict[str, Any]:

128 """Return only the essential parameters.

129

130 Returns:

131 Mapping of essential parameter names to values.

132 """

133 return {k: v for k, v in self.get_params().items()

134 if k in self.essential_param_names}

135

136

137 def get_essential_jsparams(self) -> JsonSerializedObject:

138 """Return essential parameters encoded as JSON.

139

140 Returns:

141 JSON string with essential parameters.

142 """

143 return dumpjs(self.get_essential_params())

144

145

146 def get_auxiliary_params(self) -> dict[str, Any]:

147 """Return only the auxiliary parameters.

148

149 Returns:

150 Mapping of auxiliary parameter names to values.

151 """

152 return {k: v for k, v in self.get_params().items()

153 if k in self.auxiliary_param_names}

154

155

156 def get_auxiliary_jsparams(self) -> JsonSerializedObject:

157 """Return auxiliary parameters encoded as JSON.

158

159 Returns:

160 JSON string with auxiliary parameters.

161 """

162 return dumpjs(self.get_auxiliary_params())