patternpythonMinor
Synchronous and asynchronous motor movement
Viewed 0 times
synchronousasynchronousmovementandmotor
Problem
I am working on an API that is mostly intended to be used interactively/little scripts. There are some classes with methods that could act slightly differently depending on the intention of the user, I have explored several options and would like to get some feedback on caveats, and alternative options.
I will use a simple example:
I want to use an instance of Motor in two ways (dictated by the value of _blocking attribute)
There are many different calls (not only move_to) that would follow this pattern.
The wish: Usage like:
So, in summary, the desire is that it is quick and fast to switch between behaviors, preferably within the same line of code and without lasting side effects.
Explored but discarded options:
Manually handling the flag variable: potentially dangerous (easy to forget), three liner every time.
Duplicate the methods into blocking and not blocking: API duplicates in size.
Context manager: Taking care of the flag and cleaning up the logic in the move_to method. Still a bit too verbose for my taste, specially for command line (I skip the context manager code). Note that the advantage of the context manager of being able to unb
I will use a simple example:
class Motor(object):
def __init__(self, name):
self.name = name
self._blocking = True
def move_to(self, position):
self.block_until_idle() # protects from conflict in any case
#internal move call here
if self._blocking:
self.block_until_idle()
else:
self._blocking = True
def block_until_idle(self):
#internal status query loopI want to use an instance of Motor in two ways (dictated by the value of _blocking attribute)
- The movement call blocks until idle, because I want to take a picture at that position.
- The movement call returns ASAP, because I want to do other things meanwhile (for example move a completely different motor).
There are many different calls (not only move_to) that would follow this pattern.
The wish: Usage like:
motor = Motor("A")
motor2 = Motor("B")
motor.move_to(20)
# > wait until done
noblock(motor.move_to(50))
# > returns immediately so I can move another
motor2.move_to(30)So, in summary, the desire is that it is quick and fast to switch between behaviors, preferably within the same line of code and without lasting side effects.
Explored but discarded options:
Manually handling the flag variable: potentially dangerous (easy to forget), three liner every time.
Duplicate the methods into blocking and not blocking: API duplicates in size.
Context manager: Taking care of the flag and cleaning up the logic in the move_to method. Still a bit too verbose for my taste, specially for command line (I skip the context manager code). Note that the advantage of the context manager of being able to unb
Solution
My suggestion would be to create a special result value (similar to Future) that would represent an asynchronous computation:
where
Then let all your methods return instances of
The actual implementation in
class Future(object):
def result(self):
raise NotImplementedError()where
result waits until the underlying computation finishes and returns the reuslt.Then let all your methods return instances of
Future asynchronously. If you want a synchronous invocation, just callmotor.move_to(position).result()The actual implementation in
Motor would depend on your needs, in particular:- When calling two subsequent asynchronous computations, should the second block or should it be queued?
- Should
result()block only until its corresponding call has finished, or until the motor is idle? The former makes more sense, but would require something different thanblock_until_idle.
- Should the class be thread-safe? (Your example code isn't.)
Code Snippets
class Future(object):
def result(self):
raise NotImplementedError()motor.move_to(position).result()Context
StackExchange Code Review Q#85501, answer score: 2
Revisions (0)
No revisions yet.