jsのsetTimeout的な非同期っぽい感じのsleepをpythonでどうするのか考えてみた。
こんな方向でいけるかもしれない。書いたコードは、setTimeoutとは似ても似つかないけれど。
イメージ見たいなものはつかめた。。。かもしれない。
- 実際にsleepをするわけではなく、現在時刻との差分を確認
- 「できるよ」、「おわった」、「まだ待って」みたいな状態を持てば良さそう。
無駄にメタクラスとか使っているけど、単に同じようなメソッドをたくさん定義したくなかっただけです。
import sys import time class Queue(object): """FIFO""" def __init__(self): self._queue = [] def enqueue(self, x): self._queue.append(x) def dequeue(self): return self._queue.pop(0) class EvalQueue(Queue): def dorun(self): result = [] while len(self._queue) != 0: e = self.dequeue() self._do(e, result) return result def _do(self, event, result): if event.state == "finished": pass elif event.state == "waiting": self.enqueue(event) elif event.state == "runnable": result.append(event()) if event.state != "finished": self.enqueue(event) else: raise NotImplementedError class StateMeta(type): def __new__(cls, name, bases, attrs): for state in attrs["state_candidates"]: attrs[state] = cls._set_state_generate(state) return super(StateMeta, cls).__new__(cls, name, bases, attrs) @classmethod def _set_state_generate(cls, state): def _set_state(self): self._state = state return _set_state class Event(object): __metaclass__ = StateMeta state_candidates = ["runnable", "waiting", "finished"] def __init__(self, fn, *args): self.fn = fn self.args = args self.runnable() @property def state(self): return self._state def __call__(self): result = self.fn(*self.args) self.finished() return result class TimedEvent(Event): state_candidates = ["runnable", "waiting", "finished"] def __init__(self, *args,**kw): super(TimedEvent, self).__init__(*args, **kw) self.start_time = time.time() self._waittime = 0 self.waiting() def _get_waittime(self): return self._waittime def _set_waittime(self, N): self._waittime = N waittime = property(_get_waittime, _set_waittime) @property def diff(self): return time.time() - self.start_time @property def state(self): if self._state != "finished" and self.diff > self._waittime: self.runnable() return self._state class StickyEvent(Event): state_candidates = ["runnable", "waiting", "finished"] def __init__(self, *args,**kw): super(StickyEvent, self).__init__(*args, **kw) self._life = 1 def _get_life(self): return self._life def _set_life(self, n): self._life = n life = property(_get_life, _set_life) def __call__(self): result = super(StickyEvent, self).__call__() self.life -= 1 if self.life >= 0: self.runnable() return result class EventFactory(object): @classmethod def event(cls, fn, *args): return Event(fn, *args) @classmethod def tevent(cls, N, fn, *args): te = TimedEvent(fn, *args) te.waittime = N return te @classmethod def sevent(cls, life, fn, *args): se = StickyEvent(fn, *args) se.life = life return se eq = EvalQueue() # x = Event(lambda x : x + x, 10) # x.runnable() # print x.state # print x() # print x.state # import sys # tx = TimedEvent(lambda x : sys.stdout.write("hey:%s" % x)) # tx.waittime = 2 # for i in range(10): # print "%s: waittime %s, diff %s" % (tx.state, tx._waittime, tx.diff) # time.sleep(0.5) eq.enqueue(EventFactory.tevent(3, lambda : sys.stdout.write("hey"))) eq.enqueue(EventFactory.tevent(1.5, lambda : sys.stdout.write("foo"))) eq.enqueue(EventFactory.sevent(50, lambda : sys.stdout.write("(@.@)\n"))) eq.enqueue(EventFactory.sevent(17, lambda : time.sleep(0.1))) eq.dorun()
あと、「ある処理の後に、この処理を。。。」とかは関係を持つようなeventでwrapすればできそうな感じ。