我对于javascript中的事件驱动机制非常好奇。如果使用javascript
异步读取文件,可以使用如下代码:
var fs = require('fs');
// 打开一个流:
var rs = fs.createReadStream('sample.txt', 'utf-8');
rs.on('data', function (chunk) {
console.log('DATA:')
console.log(chunk);
});
rs.on('end', function () {
console.log('END');
});
rs.on('error', function (err) {
console.log('ERROR: ' + err);
});
而如果使用python
则代码会是这样:
try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()
我自己尝试使用python
模拟javascript
的写法(不一定是异步,只是想模仿这种风格),但是失败了,运行 fs_test.py
没有任何输出。 自己的代码如下:
events.py
class EventEmitter():
events_dict = {}
# "on" function
def on(self, event, event_handler):
if self.events_dict.get(event) == None:
self.events_dict[event] = [event_handler]
elif not event_handler in self.events_dict[event]:
self.events_dict[event].append(event_handler)
# "emit" function
def emit(self, event):
if self.events_dict.get(event) == None:
pass
else:
for event_handler in self.events_dict[event]:
event_handler(event)
def get_event_handler_length(self, event):
return len(self.events_dict.get(event))
fs.py
from events import EventEmitter
def createReadStream(srcFile, code):
emitter = EventEmitter()
try :
f = open(srcFile, 'r', encoding=code)
data = f.read()
emitter.emit('data')
emitter.emit('end')
except Exception:
emitter.emit('error')
return emitter
fs_test.py
import fs
rs = fs.createReadStream('sample.txt', 'utf-8')
def data(chunk):
print('DATA:')
print(chunk)
def end():
print('END')
def error(error):
print('ERROR: ' + error)
rs.on('data', data)
rs.on('end', end)
rs.on('error', error)
我找了很长时间,没有发现网上有类似的资源。我了解到python
有asyncio
以及twister
等库可以使用,但是现在我并不想真正让其异步,只是单纯想使用python
模拟javascript
的这种编码风格而已。请问我应该怎么做?
最后还是加入了多线程来尝试解决这个问题:fs.py
#!/usr/bin/env python3
# coding: utf8
from events import EventEmitter
import threading, time
class FileEventEmitter(EventEmitter) :
def on(self, event, event_handler):
if self.events_dict.get(event) == None:
self.events_dict[event] = [event_handler]
elif not event_handler in self.events_dict[event]:
self.events_dict[event].append(event_handler)
def emit(self, event, msg=None):
if self.events_dict.get(event) == None:
pass
else:
for event_handler in self.events_dict[event]:
if msg==None:
event_handler()
else :
event_handler(msg)
emitter = FileEventEmitter()
srcFile = ""
coding = "utf8"
def readFile():
try :
# time.sleep(0.1)
f = open(srcFile, 'r', encoding=coding)
data = f.read()
emitter.emit('data', data)
emitter.emit('end')
except Exception as e:
emitter.emit('error', str(e))
def createReadStream(src, code):
global srcFile, coding
srcFile = src
coding = code
t = threading.Thread(target=readFile, name='LoopThread')
t.start()
return emitter
不过很明显,代码非常难看。全局变量、event_handler
参数的处理等都非常不满意。python
的协程、asyncio等机制个人还没有理解,不会用,还是深入研究javascript
以及python
之后再来考虑这个问题吧。
如果只要求风格相近的话,有一个折衷的办法:先绑定再执行
events.py
from functools import wraps
from collections import defaultdict
def chain_method(func):
@wraps(func)
def inner(self, *args, **kwargs):
func(self, *args, **kwargs)
return self
return inner
class EventEmitter(object):
def __init__(self, *args, **kwargs):
self.event_handlers = defaultdict(lambda : [])
@chain_method
def on(self, event_name, handler):
self.event_handlers[event_name].append(handler)
def emit(self, event_name, *args, **kwargs):
for handler in self.event_handlers[event_name]:
handler(*args, **kwargs)
fs.py
from events import EventEmitter
class ReadFileStream(EventEmitter):
def __init__(self, file_name):
super(ReadFileStream, self).__init__(file_name)
self.file_name = file_name
def execute(self):
try:
with open(self.file_name, 'r') as file:
self.emit('data', file.read())
except Exception as err:
self.emit('error', err)
test.py
#! /usr/bin/python3
from fs import ReadFileStream
def data_handler(data):
print(data)
def error_handler(error):
print(error)
ReadFileStream('test.txt') \
.on('data', data_handler) \
.on('error', error_handler) \
.execute()