Problem 638017 · easy · Level 06 Heuristics & Optimization

One Rule for Every Setting

py-descriptors · py-classes · validation · __set_name__ · attribute access

A training script and a statistics module both have numeric settings that must stay within bounds: a learning rate between 0 and 1, a whole number of epochs of at least 1, a positive shape parameter. Writing a property for each would repeat the same checks many times. Write a class Bounded whose instances, placed on a class, check an attribute every time it is assigned:

class Trainer:
    learning_rate = Bounded(0, 1)
    epochs = Bounded(1, None, integer=True)

Bounded(low, high, integer=False):

  • An assigned value must be an int or a float but not a bool, otherwise TypeError. With integer=True it must be an int (and not a bool), otherwise TypeError.
  • It must satisfy low <= value <= high, where a bound of None means no limit on that side, otherwise ValueError.
  • The message of each error starts with the attribute's name, for example "epochs must be at least 1, got 0".
  • A rejected assignment leaves the previous value in place. Each instance has its own value; reading an attribute that was never assigned raises AttributeError.
  • Reading the attribute on the class itself (Trainer.epochs) returns the Bounded object, which has the attributes name, low and high.

The tests build classes that use your Bounded inside setup helpers you can call with Run: trainer_class(), beta_class(), configure(rows), change_later(), two_trainers(), on_the_class(), not_set_yet() and shapes(pairs). outcome(call) returns a result, or the exception's name and the first word of its message.

Examples

Input:  configure([(0.1, 20), (0.1, 0), (1.5, 3), (0.2, 5, True), ("0.1", 5)])
Output: [(0.1, 20, 32), ("ValueError", "epochs"), ("ValueError", "learning_rate"), ("TypeError", "batch_size"), ("TypeError", "learning_rate")]

Input:  on_the_class()
Output: ("Bounded", "epochs", 1, None, "batch_size", "alpha", 0.001)

Constraints

  • Bounds are numbers or None; values are checked in the order the class assigns them.

Goals

  • Write a data descriptor with `__get__`, `__set__` and `__set_name__`
  • Store each instance's value on the instance, under a name that does not clash with the descriptor
  • Reuse one validation rule for many attributes of several classes
Starting Python…