素材牛VIP会员
如何使用python模拟javascript的事件机制?
 陈***康  分类:Python  人气:1033  回帖:4  发布于6年前 收藏

我对于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)

我找了很长时间,没有发现网上有类似的资源。我了解到pythonasyncio以及twister等库可以使用,但是现在我并不想真正让其异步,只是单纯想使用python模拟javascript的这种编码风格而已。请问我应该怎么做?

讨论这个帖子(4)垃圾回帖将一律封号处理……

Lv5 码农
马***1 技术总监 6年前#1

javascript的异步是纤程异步,python的异步是并发异步。

javascript的异步来自libuv,如果你想用python模拟,起码先要找到libuv或同类的包装库吧。

Lv3 码奴
Go***ng 职业无 6年前#2

最后还是加入了多线程来尝试解决这个问题:
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之后再来考虑这个问题吧。

Lv1 新人
袜***了 PHP开发工程师 6年前#3

如果只要求风格相近的话,有一个折衷的办法:先绑定再执行

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()
Lv6 码匠
許***愿 Linux系统工程师 6年前#4
fs.createReadStream('sample.txt', 'utf-8')

createReadStream里面执行emitter.emit的时候,events_dict里面是空的,create完才通过on方法注册了event_handler,顺序完全反了。。

 文明上网,理性发言!   😉 阿里云幸运券,戳我领取