Hệ thống sự kiện trong Python


195

Bạn sử dụng hệ thống sự kiện nào cho Python? Tôi đã biết về pydispatcher , nhưng tôi đã tự hỏi những gì khác có thể được tìm thấy, hoặc thường được sử dụng?

Tôi không quan tâm đến các nhà quản lý sự kiện là một phần của các khung lớn, tôi muốn sử dụng một giải pháp xương nhỏ mà tôi có thể dễ dàng mở rộng.

Câu trả lời:


178

Gói PyPI

Kể từ tháng 6 năm 2020, đây là các gói liên quan đến sự kiện có sẵn trên PyPI, được đặt hàng trước ngày phát hành gần đây nhất.

Còn nữa

Đó là rất nhiều thư viện để lựa chọn, sử dụng thuật ngữ rất khác nhau (sự kiện, tín hiệu, trình xử lý, công văn phương thức, hook, ...).

Tôi đang cố gắng giữ một cái nhìn tổng quan về các gói trên, cộng với các kỹ thuật được đề cập trong các câu trả lời ở đây.

Đầu tiên, một số thuật ngữ ...

Mẫu quan sát

Phong cách cơ bản nhất của hệ thống sự kiện là 'túi phương thức xử lý', đây là một triển khai đơn giản của mẫu Observer .

Về cơ bản, các phương thức xử lý (các hàm gọi được) được lưu trữ trong một mảng và mỗi phương thức được gọi khi sự kiện 'kích hoạt'.

Theo dõi công khai

Nhược điểm của hệ thống sự kiện Observer là bạn chỉ có thể đăng ký trình xử lý trên đối tượng Sự kiện thực tế (hoặc danh sách trình xử lý). Vì vậy, tại thời điểm đăng ký sự kiện đã cần phải tồn tại.

Đó là lý do tại sao kiểu thứ hai của các hệ thống sự kiện tồn tại: mẫu đăng ký xuất bản . Ở đây, các trình xử lý không đăng ký trên một đối tượng sự kiện (hoặc danh sách xử lý), nhưng trên một bộ điều phối trung tâm. Ngoài ra các thông báo chỉ nói chuyện với người điều phối. Những gì cần nghe, hoặc những gì để xuất bản được xác định bởi 'signal', không có gì khác hơn một tên (chuỗi).

Mẫu hòa giải

Cũng có thể được quan tâm: mẫu Người hòa giải .

Móc

Hệ thống 'hook' được sử dụng thông thường trong bối cảnh các plugin ứng dụng. Ứng dụng chứa các điểm tích hợp cố định (hook) và mỗi plugin có thể kết nối với hook đó và thực hiện một số hành động nhất định.

Các sự kiện khác'

Lưu ý: phân luồng.Event không phải là một "hệ thống sự kiện" theo nghĩa trên. Đó là một hệ thống đồng bộ hóa luồng trong đó một luồng chờ cho đến khi một luồng khác 'báo hiệu' đối tượng Sự kiện.

Thư viện nhắn tin mạng cũng thường sử dụng thuật ngữ 'sự kiện'; đôi khi những điều này là tương tự trong khái niệm; đôi khi không. Tất nhiên họ có thể đi qua ranh giới luồng, quy trình và máy tính. Xem ví dụ: pyzmq , pymq , Twisted , Tornado , gevent , eventlet .

Tài liệu tham khảo yếu

Trong Python, việc giữ tham chiếu đến một phương thức hoặc đối tượng đảm bảo rằng nó sẽ không bị xóa bởi trình thu gom rác. Điều này có thể được mong muốn, nhưng nó cũng có thể dẫn đến rò rỉ bộ nhớ: các trình xử lý được liên kết không bao giờ được dọn sạch.

Một số hệ thống sự kiện sử dụng tài liệu tham khảo yếu thay vì thông thường để giải quyết vấn đề này.

Một số từ về các thư viện khác nhau

Hệ thống sự kiện theo phong cách quan sát viên:

  • zope.event chỉ ra những điều cốt lõi về cách thức hoạt động của nó (xem câu trả lời của Lennart ). Lưu ý: ví dụ này thậm chí không hỗ trợ các đối số xử lý.
  • Việc triển khai list danh sách có thể gọi được của LongPoke cho thấy một hệ thống sự kiện như vậy có thể được triển khai rất tối giản bằng cách phân lớp list.
  • Biến thể của Felk EventHook cũng đảm bảo chữ ký của callees và người gọi.
  • Spassig's EventHook (Mẫu sự kiện của Michael Foord) là một triển khai đơn giản.
  • Lớp Sự kiện Bài học có giá trị của Josip về cơ bản là giống nhau, nhưng sử dụng setthay vì listđể lưu trữ túi và thực hiện __call__cả hai bổ sung hợp lý.
  • PyNotify tương tự về khái niệm và cũng cung cấp các khái niệm bổ sung về các biến và điều kiện ('sự kiện thay đổi biến'). Trang chủ không hoạt động.
  • axel về cơ bản là một túi xử lý với nhiều tính năng liên quan đến phân luồng, xử lý lỗi, ...
  • python-Clark yêu cầu các lớp nguồn chẵn xuất phát từ pydispatch.Dispatcher.
  • buslane dựa trên lớp, hỗ trợ các trình xử lý đơn hoặc nhiều và tạo điều kiện cho các gợi ý kiểu mở rộng.
  • Observer / Event của Pithikos là một thiết kế gọn nhẹ.

Thư viện xuất bản-đăng ký:

  • Blinker có một số tính năng tiện lợi như tự động ngắt kết nối và lọc dựa trên người gửi.
  • PyPubSub là gói ổn định và hứa hẹn "các tính năng nâng cao hỗ trợ gỡ lỗi và duy trì các chủ đề và tin nhắn".
  • pymitter là một cổng Python của Node.js EventEuctor2 và cung cấp các không gian tên, ký tự đại diện và TTL.
  • PyDispatcher dường như nhấn mạnh tính linh hoạt liên quan đến xuất bản nhiều-nhiều, v.v. Hỗ trợ các tài liệu tham khảo yếu.
  • louie là một PyDispatcher được làm lại và nên hoạt động "trong nhiều bối cảnh khác nhau".
  • pypydispatcher dựa trên (bạn đoán nó ...) PyDispatcher và cũng hoạt động trong PyPy.
  • django.dispatch là một PyDispatcher viết lại "với giao diện hạn chế hơn, nhưng hiệu suất cao hơn".
  • pyeventdispatcher dựa trên bộ phân phối sự kiện của khung Symfony của PHP.
  • Bộ điều phối được trích xuất từ ​​django.dispatch nhưng đang trở nên khá cũ.
  • EventManger của Cristian Garcia là một triển khai thực sự ngắn.

Khác:

  • pluggy chứa một hệ thống hook được sử dụng bởi các pytestplugin.
  • RxPy3 thực hiện mẫu có thể quan sát và cho phép hợp nhất các sự kiện, thử lại, v.v.
  • Tín hiệu và Slots của Qt có sẵn từ PyQt hoặc PySide2 . Chúng hoạt động như gọi lại khi được sử dụng trong cùng một luồng hoặc như các sự kiện (sử dụng vòng lặp sự kiện) giữa hai luồng khác nhau. Tín hiệu và Slots có giới hạn là chúng chỉ hoạt động trong các đối tượng của các lớp xuất phát từ đó QObject.

2
Ngoài ra còn có louie, dựa trên PyDispatcher: pypi.python.org/pypi/Louie/1.1
the979kid

@ the979kid louie dường như được duy trì rất tệ, trang pypi liên kết đến 404 trên GitHub: 11craft.github.io/louie ; github.com/gldnspud/louie . Nên là github.com/11craft/louie .
florisla 14/03/2016

1
người nghe sự kiện yếu kém là một nhu cầu phổ biến. Nếu không sử dụng thế giới thực trở nên khó khăn. Một lưu ý mà giải pháp hỗ trợ có thể hữu ích.
kxr

Pypubub 4 rất nhiều và có các công cụ gỡ lỗi mạnh mẽ cho tin nhắn và một số cách hạn chế tải trọng tin nhắn để bạn biết sớm hơn khi bạn đã gửi dữ liệu không hợp lệ hoặc thiếu dữ liệu. PyPubSub 4 hỗ trợ Python 3 (và PyPubSub 3.x hỗ trợ Python 2).
Oliver

Gần đây tôi đã xuất bản một thư viện có tên pymq github.com/thrau/pymq có thể phù hợp với danh sách này.
thrau

98

Tôi đã làm theo cách này:

class Event(list):
    """Event subscription.

    A list of callable objects. Calling an instance of this will cause a
    call to each item in the list in ascending order by index.

    Example Usage:
    >>> def f(x):
    ...     print 'f(%s)' % x
    >>> def g(x):
    ...     print 'g(%s)' % x
    >>> e = Event()
    >>> e()
    >>> e.append(f)
    >>> e(123)
    f(123)
    >>> e.remove(f)
    >>> e()
    >>> e += (f, g)
    >>> e(10)
    f(10)
    g(10)
    >>> del e[0]
    >>> e(2)
    g(2)

    """
    def __call__(self, *args, **kwargs):
        for f in self:
            f(*args, **kwargs)

    def __repr__(self):
        return "Event(%s)" % list.__repr__(self)

Tuy nhiên, giống như mọi thứ khác tôi đã thấy, không có pydoc được tạo tự động cho điều này, và không có chữ ký, thực sự rất tệ.


3
Tôi thấy phong cách này khá hấp dẫn. Đó là xương trần ngọt ngào. Tôi thích thực tế là nó cho phép một người thao túng các sự kiện và người đăng ký của họ như các hoạt động tự trị. Tôi sẽ xem giá vé như thế nào trong một dự án thực sự.
Rudy Lattae

2
Phong cách tối giản rất đẹp! siêu!
akaRem

2
Tôi không thể nâng cao điều này đủ, điều này thực sự đơn giản và dễ dàng.

2
ủng hộ lớn, ai đó có thể giải thích điều này như tôi 10 tuổi? Lớp này có được kế thừa bởi lớp chính không? Tôi không thấy một init nên siêu () sẽ không được sử dụng. Nó không nhấp cho tôi vì một số lý do.
omgimdrunk

1
@omgimdrunk Một trình xử lý sự kiện đơn giản sẽ loại bỏ một hoặc nhiều chức năng có thể gọi được mỗi khi một sự kiện được kích hoạt. Một lớp để "quản lý" điều này cho bạn sẽ yêu cầu các phương thức sau ở mức tối thiểu - thêm & bắn. Trong lớp đó, bạn sẽ cần duy trì một danh sách các trình xử lý sẽ được thực thi. Hãy đặt nó trong biến thể _bag_of_handlershiện là một danh sách. Phương thức thêm của lớp sẽ đơn giản là self._bag_of_handlers.append(some_callable). Phương thức chữa cháy của lớp sẽ lặp lại thông qua _bag_of_handlers` truyền các đối số và kwarg được cung cấp cho các trình xử lý và thực hiện từng chuỗi theo thứ tự.
Gabe Spradlin

68

Chúng tôi sử dụng một EventHook theo đề xuất từ ​​Michael Foord trong Mẫu sự kiện của anh ấy :

Chỉ cần thêm EventHooks vào các lớp của bạn với:

class MyBroadcaster()
    def __init__():
        self.onChange = EventHook()

theBroadcaster = MyBroadcaster()

# add a listener to the event
theBroadcaster.onChange += myFunction

# remove listener from the event
theBroadcaster.onChange -= myFunction

# fire event
theBroadcaster.onChange.fire()

Chúng tôi thêm chức năng để loại bỏ tất cả người nghe khỏi một đối tượng vào lớp Michaels và kết thúc với điều này:

class EventHook(object):

    def __init__(self):
        self.__handlers = []

    def __iadd__(self, handler):
        self.__handlers.append(handler)
        return self

    def __isub__(self, handler):
        self.__handlers.remove(handler)
        return self

    def fire(self, *args, **keywargs):
        for handler in self.__handlers:
            handler(*args, **keywargs)

    def clearObjectHandlers(self, inObject):
        for theHandler in self.__handlers:
            if theHandler.im_self == inObject:
                self -= theHandler

Một nhược điểm của việc sử dụng này là trước tiên bạn cần thêm một sự kiện trước khi bạn đăng ký làm người đăng ký. Nếu chỉ có các nhà xuất bản thêm các sự kiện của họ (không bắt buộc, chỉ là một thông lệ tốt), thì bạn phải khởi tạo các nhà xuất bản trước những người đăng ký gây đau khổ trong các dự án lớn
Jonathan

6
phương thức cuối cùng bị lỗi vì trình xử lý tự .__ được sửa đổi trong các lần lặp. Khắc phục: `self .__ handlers = [h for h in self .__ handlers if h.im_elf! = Obj]`
Simon Bergot

1
@Simon đúng, nhưng giới thiệu một lỗi vì chúng ta có thể có các chức năng không liên kết trong trình xử lý tự .__. Khắc phục:self.__handlers = [h for h in self._handlers if getattr(h, 'im_self', False) != obj]
Eric Marcos

20

Tôi sử dụng zope.event . Đó là xương trần nhất mà bạn có thể tưởng tượng. :-) Trên thực tế, đây là mã nguồn hoàn chỉnh:

subscribers = []

def notify(event):
    for subscriber in subscribers:
        subscriber(event)

Lưu ý rằng bạn không thể gửi tin nhắn giữa các quy trình, ví dụ. Đó không phải là một hệ thống nhắn tin, chỉ là một hệ thống sự kiện, không hơn, không kém.


17
pypi.python.org/pypi/zope.event ... để cứu Google nghèo một số bandwith ;-)
Boldewyn

Tôi vẫn muốn có thể gửi tin nhắn. Tôi sẽ sử dụng hệ thống sự kiện trong ứng dụng được xây dựng trên Tkinter. Tôi không sử dụng hệ thống sự kiện vì nó không hỗ trợ tin nhắn.
Josip

Bạn có thể gửi bất cứ điều gì bạn muốn với zope.event. Nhưng quan điểm của tôi là nó không phải là một hệ thống nhắn tin thích hợp, vì bạn không thể gửi các sự kiện / tin nhắn đến các quy trình khác hoặc các máy tính khác. Bạn có thể nên là một nhưng cụ thể hơn với yêu cầu của bạn.
Lennart Regebro

15

Tôi tìm thấy kịch bản nhỏ này trên các bài học có giá trị . Nó dường như có tỷ lệ đơn giản / sức mạnh đúng sau tôi. Peter Thatcher là tác giả của mã sau đây (không cấp phép được đề cập).

class Event:
    def __init__(self):
        self.handlers = set()

    def handle(self, handler):
        self.handlers.add(handler)
        return self

    def unhandle(self, handler):
        try:
            self.handlers.remove(handler)
        except:
            raise ValueError("Handler is not handling this event, so cannot unhandle it.")
        return self

    def fire(self, *args, **kargs):
        for handler in self.handlers:
            handler(*args, **kargs)

    def getHandlerCount(self):
        return len(self.handlers)

    __iadd__ = handle
    __isub__ = unhandle
    __call__ = fire
    __len__  = getHandlerCount

class MockFileWatcher:
    def __init__(self):
        self.fileChanged = Event()

    def watchFiles(self):
        source_path = "foo"
        self.fileChanged(source_path)

def log_file_change(source_path):
    print "%r changed." % (source_path,)

def log_file_change2(source_path):
    print "%r changed!" % (source_path,)

watcher              = MockFileWatcher()
watcher.fileChanged += log_file_change2
watcher.fileChanged += log_file_change
watcher.fileChanged -= log_file_change2
watcher.watchFiles()

1
Sử dụng một tập hợp () thay vì một danh sách là tốt để tránh các trình xử lý được đăng ký hai lần. Một hậu quả là những người xử lý không được gọi theo thứ tự họ đã đăng ký. Không hẳn là một điều xấu mặc dù ...
florisla

1
@florisla có thể trao đổi cho Orderedset, nếu muốn.
Robino

9

Đây là một thiết kế tối thiểu nên hoạt động tốt. Những gì bạn phải làm là chỉ đơn giản là kế thừa Observertrong một lớp và sau đó sử dụng observe(event_name, callback_fn)để lắng nghe một sự kiện cụ thể. Bất cứ khi nào sự kiện cụ thể đó được kích hoạt ở bất cứ đâu trong mã (ví dụ. Event('USB connected')), Cuộc gọi lại tương ứng sẽ kích hoạt.

class Observer():
    _observers = []
    def __init__(self):
        self._observers.append(self)
        self._observed_events = []
    def observe(self, event_name, callback_fn):
        self._observed_events.append({'event_name' : event_name, 'callback_fn' : callback_fn})


class Event():
    def __init__(self, event_name, *callback_args):
        for observer in Observer._observers:
            for observable in observer._observed_events:
                if observable['event_name'] == event_name:
                    observable['callback_fn'](*callback_args)

Thí dụ:

class Room(Observer):
    def __init__(self):
        print("Room is ready.")
        Observer.__init__(self) # DON'T FORGET THIS
    def someone_arrived(self, who):
        print(who + " has arrived!")

# Observe for specific event
room = Room()
room.observe('someone arrived',  room.someone_arrived)

# Fire some events
Event('someone left',    'John')
Event('someone arrived', 'Lenard') # will output "Lenard has arrived!"
Event('someone Farted',  'Lenard')

Tôi thích thiết kế của bạn, nó tối giản và dễ hiểu. và nó sẽ nhẹ hơn khi không phải nhập một số mô-đun.
Atreyagaurav

8

Tôi đã tạo một EventManagerlớp (mã ở cuối). Cú pháp như sau:

#Create an event with no listeners assigned to it
EventManager.addEvent( eventName = [] )

#Create an event with listeners assigned to it
EventManager.addEvent( eventName = [fun1, fun2,...] )

#Create any number event with listeners assigned to them
EventManager.addEvent( eventName1 = [e1fun1, e1fun2,...], eventName2 = [e2fun1, e2fun2,...], ... )

#Add or remove listener to an existing event
EventManager.eventName += extra_fun
EventManager.eventName -= removed_fun

#Delete an event
del EventManager.eventName

#Fire the event
EventManager.eventName()

Đây là một ví dụ:

def hello(name):
    print "Hello {}".format(name)
    
def greetings(name):
    print "Greetings {}".format(name)

EventManager.addEvent( salute = [greetings] )
EventManager.salute += hello

print "\nInitial salute"
EventManager.salute('Oscar')

print "\nNow remove greetings"
EventManager.salute -= greetings
EventManager.salute('Oscar')

Đầu ra:


Lời chào ban đầu Lời chào Oscar
Xin chào Oscar

Bây giờ xóa lời
chào Xin chào Oscar

Mã sự kiện:

class EventManager:
    
    class Event:
        def __init__(self,functions):
            if type(functions) is not list:
                raise ValueError("functions parameter has to be a list")
            self.functions = functions
            
        def __iadd__(self,func):
            self.functions.append(func)
            return self
            
        def __isub__(self,func):
            self.functions.remove(func)
            return self
            
        def __call__(self,*args,**kvargs):
            for func in self.functions : func(*args,**kvargs)
            
    @classmethod
    def addEvent(cls,**kvargs):
        """
        addEvent( event1 = [f1,f2,...], event2 = [g1,g2,...], ... )
        creates events using **kvargs to create any number of events. Each event recieves a list of functions,
        where every function in the list recieves the same parameters.
        
        Example:
        
        def hello(): print "Hello ",
        def world(): print "World"
        
        EventManager.addEvent( salute = [hello] )
        EventManager.salute += world
        
        EventManager.salute()
        
        Output:
        Hello World
        """
        for key in kvargs.keys():
            if type(kvargs[key]) is not list:
                raise ValueError("value has to be a list")
            else:
                kvargs[key] = cls.Event(kvargs[key])
        
        cls.__dict__.update(kvargs)

8

Bạn có thể có một cái nhìn về pymitter ( pypi ). Đây là một cách tiếp cận một tệp nhỏ (~ 250 loc) "cung cấp không gian tên, ký tự đại diện và TTL".

Đây là một ví dụ cơ bản:

from pymitter import EventEmitter

ee = EventEmitter()

# decorator usage
@ee.on("myevent")
def handler1(arg):
   print "handler1 called with", arg

# callback usage
def handler2(arg):
    print "handler2 called with", arg
ee.on("myotherevent", handler2)

# emit
ee.emit("myevent", "foo")
# -> "handler1 called with foo"

ee.emit("myotherevent", "bar")
# -> "handler2 called with bar"

6

Tôi đã thực hiện một biến thể của phương pháp tối giản của Longpoke để đảm bảo chữ ký cho cả người gọi và người gọi:

class EventHook(object):
    '''
    A simple implementation of the Observer-Pattern.
    The user can specify an event signature upon inizializazion,
    defined by kwargs in the form of argumentname=class (e.g. id=int).
    The arguments' types are not checked in this implementation though.
    Callables with a fitting signature can be added with += or removed with -=.
    All listeners can be notified by calling the EventHook class with fitting
    arguments.

    >>> event = EventHook(id=int, data=dict)
    >>> event += lambda id, data: print("%d %s" % (id, data))
    >>> event(id=5, data={"foo": "bar"})
    5 {'foo': 'bar'}

    >>> event = EventHook(id=int)
    >>> event += lambda wrong_name: None
    Traceback (most recent call last):
        ...
    ValueError: Listener must have these arguments: (id=int)

    >>> event = EventHook(id=int)
    >>> event += lambda id: None
    >>> event(wrong_name=0)
    Traceback (most recent call last):
        ...
    ValueError: This EventHook must be called with these arguments: (id=int)
    '''
    def __init__(self, **signature):
        self._signature = signature
        self._argnames = set(signature.keys())
        self._handlers = []

    def _kwargs_str(self):
        return ", ".join(k+"="+v.__name__ for k, v in self._signature.items())

    def __iadd__(self, handler):
        params = inspect.signature(handler).parameters
        valid = True
        argnames = set(n for n in params.keys())
        if argnames != self._argnames:
            valid = False
        for p in params.values():
            if p.kind == p.VAR_KEYWORD:
                valid = True
                break
            if p.kind not in (p.POSITIONAL_OR_KEYWORD, p.KEYWORD_ONLY):
                valid = False
                break
        if not valid:
            raise ValueError("Listener must have these arguments: (%s)"
                             % self._kwargs_str())
        self._handlers.append(handler)
        return self

    def __isub__(self, handler):
        self._handlers.remove(handler)
        return self

    def __call__(self, *args, **kwargs):
        if args or set(kwargs.keys()) != self._argnames:
            raise ValueError("This EventHook must be called with these " +
                             "keyword arguments: (%s)" % self._kwargs_str())
        for handler in self._handlers[:]:
            handler(**kwargs)

    def __repr__(self):
        return "EventHook(%s)" % self._kwargs_str()

3

Nếu tôi làm mã trong pyQt, tôi sử dụng mô hình ổ cắm / tín hiệu QT, tương tự đối với django

Nếu tôi đang làm async, tôi / OI sử dụng mô đun chọn gốc

Nếu tôi sử dụng trình phân tích cú pháp python SAX, tôi đang sử dụng API sự kiện do SAX cung cấp. Vì vậy, có vẻ như tôi là nạn nhân của API cơ bản :-)

Có lẽ bạn nên tự hỏi những gì bạn mong đợi từ khung / mô-đun sự kiện. Sở thích cá nhân của tôi là sử dụng mô hình Ổ cắm / Tín hiệu từ QT. Thông tin thêm về điều đó có thể được tìm thấy ở đây


2

Đây là một mô-đun khác để xem xét. Có vẻ như một sự lựa chọn khả thi cho các ứng dụng đòi hỏi khắt khe hơn.

Py-notify là gói Python cung cấp các công cụ để triển khai mẫu lập trình Observer. Những công cụ này bao gồm các tín hiệu, điều kiện và các biến.

Tín hiệu là danh sách các trình xử lý được gọi khi tín hiệu được phát ra. Các điều kiện về cơ bản là các biến boolean kết hợp với tín hiệu được phát ra khi trạng thái điều kiện thay đổi. Chúng có thể được kết hợp bằng cách sử dụng các toán tử logic tiêu chuẩn (không, và, v.v.) vào các điều kiện ghép. Các biến, không giống như các điều kiện, có thể chứa bất kỳ đối tượng Python nào, không chỉ là booleans, mà chúng không thể được kết hợp.


1
Trang chủ đã hết hoa hồng cho trang này, có lẽ không còn được hỗ trợ nữa?
David

1

Nếu bạn muốn làm những việc phức tạp hơn như hợp nhất các sự kiện hoặc thử lại, bạn có thể sử dụng mẫu có thể quan sát được và một thư viện trưởng thành thực hiện điều đó. https://github.com/ReactiveX/RxPY . Các đài quan sát rất phổ biến trong Javascript và Java và rất thuận tiện để sử dụng cho một số tác vụ không đồng bộ.

from rx import Observable, Observer


def push_five_strings(observer):
        observer.on_next("Alpha")
        observer.on_next("Beta")
        observer.on_next("Gamma")
        observer.on_next("Delta")
        observer.on_next("Epsilon")
        observer.on_completed()


class PrintObserver(Observer):

    def on_next(self, value):
        print("Received {0}".format(value))

    def on_completed(self):
        print("Done!")

    def on_error(self, error):
        print("Error Occurred: {0}".format(error))

source = Observable.create(push_five_strings)

source.subscribe(PrintObserver())

ĐẦU RA :

Received Alpha
Received Beta
Received Gamma
Received Delta
Received Epsilon
Done!

1

Nếu bạn cần một eventbus hoạt động xuyên suốt quá trình hoặc ranh giới mạng, bạn có thể thử PyMQ . Nó hiện hỗ trợ pub / sub, hàng đợi tin nhắn và RPC đồng bộ. Phiên bản mặc định hoạt động ở trên cùng của phụ trợ Redis, vì vậy bạn cần một máy chủ Redis đang chạy. Ngoài ra còn có một phụ trợ trong bộ nhớ để thử nghiệm. Bạn cũng có thể viết phụ trợ của riêng bạn.

import pymq

# common code
class MyEvent:
    pass

# subscribe code
@pymq.subscriber
def on_event(event: MyEvent):
    print('event received')

# publisher code
pymq.publish(MyEvent())

# you can also customize channels
pymq.subscribe(on_event, channel='my_channel')
pymq.publish(MyEvent(), channel='my_channel')

Để khởi tạo hệ thống:

from pymq.provider.redis import RedisConfig

# starts a new thread with a Redis event loop
pymq.init(RedisConfig())

# main application control loop

pymq.shutdown()

Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của thư viện này


0

Bạn có thể thử buslanemô-đun.

Thư viện này làm cho việc thực hiện hệ thống dựa trên tin nhắn dễ dàng hơn. Nó hỗ trợ các lệnh (trình xử lý đơn) và cách tiếp cận các sự kiện (0 hoặc nhiều trình xử lý). Buslane sử dụng các chú thích kiểu Python để đăng ký xử lý đúng cách.

Ví dụ đơn giản:

from dataclasses import dataclass

from buslane.commands import Command, CommandHandler, CommandBus


@dataclass(frozen=True)
class RegisterUserCommand(Command):
    email: str
    password: str


class RegisterUserCommandHandler(CommandHandler[RegisterUserCommand]):

    def handle(self, command: RegisterUserCommand) -> None:
        assert command == RegisterUserCommand(
            email='john@lennon.com',
            password='secret',
        )


command_bus = CommandBus()
command_bus.register(handler=RegisterUserCommandHandler())
command_bus.execute(command=RegisterUserCommand(
    email='john@lennon.com',
    password='secret',
))

Để cài đặt buslane, chỉ cần sử dụng pip:

$ pip install buslane

0

Cách đây một thời gian tôi đã viết thư viện có thể hữu ích cho bạn. Nó cho phép bạn có người nghe địa phương và toàn cầu, nhiều cách đăng ký khác nhau, ưu tiên thực hiện, v.v.

from pyeventdispatcher import register

register("foo.bar", lambda event: print("second"))
register("foo.bar", lambda event: print("first "), -100)

dispatch(Event("foo.bar", {"id": 1}))
# first second

Có một cái nhìn pyeventdispatcher

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.