Elegant way to do this in Python?

Started by
1 comment, last by Hollower 12 years, 9 months ago
I'm working on a project where we have a lot of existing code which checks some readings from sensors and other parts of the code and acts accordingly, all in Python. Much of it is like a state machine and it currently goes to the next state after a single 'frame' of data matching some criterion. This works fine *most* of the time, but if we get a single bad frame, it can throw the system into another state which is highly undesirable. I'm trying to make a generic means of checking for consistency over time. Here's what I've got so far: a class which checks if 3 of the last 5 values have been 'good'.

[source lang="python"]class ConsistencyCheck:

def __init__(self, count=3, total=5):
self.count = 3
self.total = 5
self.values = []

def check(self, value):
self.values.append(value)
if len(self.values) > self.total:
self.values[:] = self.values[-self.total:]

if len( filter(lambda x: x == True, self.values) ) > self.count:
return True
else:
return Fals[/source]

This is fine, but I'd like a less cumbersome means of using this. To change an existing condition to use this, I need to make a member variable of the class which uses the condition, then put self.some_checker.check() around the predicate. It's OK, but I'd prefer if I could just wrap the predicate directly in a consistency checker. For example:

[source lang="python"]## This:
if ConsistencyCheck( distance < close_threshold ):
change_state()

## Instead of:
self.close_checker = ConsistencyCheck()
# Some time later....
if self.close_checker.check( distance < close_threshold ):
change_state()[/source]

This is ideal because it consolidates the changes, doesn't require a member variable for every status check and lets us specify the configuration of the predicate (ie. count and total values) at the same place that it is used, not back at initialization. However, I can't think of any way to do this. Is anyone's Python magic stronger than mine?
Advertisement
If I understand correctly, for each kind of check you want to perform, you have to store a list of values somewhere.

So you either need N different checkers, or some multi-checker that stores them all, but then you have to call that checker by providing the type of check you want.
Since I don't think you can do things like use functions as map keys (without storing the N different functions), you would need to use some kind of enum to select the check type.

Or do all check type operate on the same set of values ? In which case what you want should be feasible.
I'm a little unclear on what you want exactly but I think I have the general idea. I get that you want to assign the predicate ahead of time. You can do that if the predicate is an actual function rather than just an expression. You can also pass in the change_state function as a callback. Barring some kind of event/subscriber pattern you still need to call the function to perform the check in your processing loop or wherever that is, but this eliminates the arguments and the if statement. You can also store a collection of these in a list and iterate over them calling check() on each.

[source lang="python"]
class ConsistencyCheck:

def __init__(self, predicate, callback, count=3, total=5):
self.count = count
self.total = total
self.predicate = predicate
self.callback = callback
self.results = []

def check(self):
self.results.append( self.predicate() )
while len(self.results) > self.total:
self.results.pop(0)
if self.results.count(True) > self.count:
self.callback()

# usage:
def is_near():
return distance < near_threshold

near_checker = ConsistencyCheck(is_near, change_state)
# some time later...
near_checker.check()
[/source]

The predicate must be a function which takes no args and returns a bool. If needed you can use functools.partial to bind arguments. I also cleaned up some overly verbose stuff and changed a few names according to personal preferences (feel free to disregard). I think "results" is more descriptive than "values", and I use "near" instead of "close" due to the latter being a homonym of another common programming task.

This topic is closed to new replies.

Advertisement