파이썬의 이벤트 시스템


196

어떤 이벤트 시스템을 파이썬으로 사용하십니까? 나는 이미 pydispatcher를 알고 있지만 다른 것을 발견하거나 일반적으로 사용하는 것이 궁금합니다.

큰 프레임 워크의 일부인 이벤트 관리자에는 관심이 없으며 쉽게 확장 할 수있는 작은 베어 본 솔루션을 사용하고 싶습니다.

답변:


181

PyPI 패키지

2020 년 6 월 현재 PyPI에서 제공되는 이벤트 관련 패키지는 최신 출시 날짜순으로 정렬됩니다.

더있다

그것은 매우 다른 용어 (이벤트, 신호, 핸들러, 메소드 디스패치, 후크 등)를 사용하여 선택할 수있는 많은 라이브러리입니다.

위 패키지에 대한 개요와 여기 답변에 언급 된 기술을 유지하려고합니다.

먼저, 일부 용어는 ...

관찰자 패턴

이벤트 시스템의 가장 기본적인 스타일은 Observer 패턴 의 간단한 구현 인 '핸들러 메서드 백' 입니다 .

기본적으로 핸들러 메소드 (호출 가능)는 배열에 저장되며 이벤트가 발생할 때 각각 호출됩니다.

발행-구독

관찰자 이벤트 시스템의 단점은 실제 이벤트 오브젝트 (또는 핸들러 목록)에서만 핸들러를 등록 할 수 있다는 것입니다. 따라서 등록시 이벤트가 이미 존재해야합니다.

그렇기 때문에 두 번째 스타일의 이벤트 시스템 인 publish-subscribe 패턴 이 존재합니다 . 여기서 핸들러는 이벤트 오브젝트 (또는 핸들러 목록)가 아니라 중앙 디스패처에 등록됩니다. 또한 알리미는 운영자와 만 대화합니다. 듣거나 게시 할 내용은 이름 (문자열)에 지나지 않는 '신호'에 의해 결정됩니다.

중재자 패턴

또한 관심을 가질만한 것은 Mediator pattern 입니다.

갈고리

'후크'시스템은 응용 프로그램 플러그인의 맥락에서 일반적으로 사용됩니다. 응용 프로그램에는 고정 통합 지점 (후크)이 포함되어 있으며 각 플러그인은 해당 후크에 연결하여 특정 작업을 수행 할 수 있습니다.

다른 '이벤트'

참고 : threading.Event 는 위의 의미에서 '이벤트 시스템'이 아닙니다. 하나의 스레드가 다른 스레드가 Event 객체를 '신호'할 때까지 기다리는 스레드 동기화 시스템입니다.

네트워크 메시징 라이브러리는 종종 '이벤트'라는 용어를 사용합니다. 때때로 이들은 개념 상 유사하다. 때로는 그렇지 않습니다. 물론 스레드, 프로세스 및 컴퓨터 경계를 통과 할 수 있습니다. 예를 들어 pyzmq , pymq , Twisted , Tornado , gevent , eventlet을 참조하십시오 .

약한 참조

Python에서 메소드 또는 객체에 대한 참조를 보유하면 가비지 수집기에 의해 삭제되지 않습니다. 이것은 바람직 할 수 있지만 메모리 누수가 발생할 수 있습니다. 연결된 처리기는 정리되지 않습니다.

일부 이벤트 시스템은이를 해결하기 위해 일반 시스템 대신 약한 참조를 사용합니다.

다양한 라이브러리에 대한 몇 마디

관찰자 스타일의 이벤트 시스템 :

  • zope.event 는 이것이 어떻게 작동하는지의 핵심을 보여줍니다 ( Lenart 's answer 참조 ). 참고 :이 예제는 핸들러 인수도 지원하지 않습니다.
  • LongPoke의 '호출 가능 목록' 구현은 이러한 이벤트 시스템이 서브 클래 싱을 통해 매우 최소한으로 구현 될 수 있음을 보여줍니다 list.
  • Felk의 변형 EventHook 은 또한 수신자 및 발신자의 서명을 보장합니다.
  • spassig의 EventHook (Michael Foord의 이벤트 패턴)은 간단한 구현입니다.
  • Josip의 소중한 수업 이벤트 클래스 는 기본적으로 동일하지만 a set대신 a list를 사용하여 가방을 저장하고 __call__둘 다 합리적인 추가 기능을 구현 합니다.
  • PyNotify 는 개념 상 유사하며 추가 변수 및 조건 개념 ( '변수 변경 이벤트')을 제공합니다. 홈페이지가 작동하지 않습니다.
  • axel 는 기본적으로 스레딩, 오류 처리 등과 관련된 더 많은 기능을 갖춘 처리기입니다.
  • python-dispatch 에는 짝수 소스 클래스가 필요합니다 pydispatch.Dispatcher.
  • buslane 은 클래스 기반이며 단일 또는 다중 핸들러를 지원하며 광범위한 유형 힌트를 지원합니다.
  • Pithikos의 관찰자 / 이벤트 는 가벼운 디자인입니다.

발행-구독 라이브러리 :

  • 깜박임 에는 자동 연결 해제 및 발신자 기반 필터링과 같은 멋진 기능이 있습니다.
  • PyPubSub 는 안정적인 패키지이며 "주제와 메시지의 디버깅 및 유지 관리를 용이하게하는 고급 기능"을 약속합니다.
  • pymitter 는 Node.js EventEmitter2의 Python 포트이며 네임 스페이스, 와일드 카드 및 TTL을 제공합니다.
  • PyDispatcher 는 다 대다 출판 등의 측면에서 유연성을 강조하는 것 같습니다. 약한 참조를 지원합니다.
  • louie 는 재 작업 된 PyDispatcher이며 "다양한 상황에서"작동해야합니다.
  • pypydispatcherPyDispatcher 를 기반으로하며 PyPy에서도 작동합니다.
  • django.dispatch 는 재 작성된 PyDispatcher "인터페이스가 더 제한적이지만 성능이 더 뛰어납니다".
  • pyeventdispatcher 는 PHP의 Symfony 프레임 워크의 이벤트 디스패처를 기반으로합니다.
  • dispatcher 는 django.dispatch에서 추출되었지만 상당히 오래되었습니다.
  • Cristian Garcia의 EventManger 는 실제로 짧은 구현입니다.

기타 :

  • pluggy 에는 pytest플러그인에서 사용하는 후크 시스템이 포함되어 있습니다.
  • RxPy3 는 Observable 패턴을 구현하고 이벤트 병합, 재시도 등을 허용합니다.
  • Qt의 신호 및 슬롯은 PyQt 또는 PySide2 에서 사용할 수 있습니다 . 동일한 스레드에서 사용될 때 콜백 또는 두 개의 다른 스레드간에 이벤트 (이벤트 루프 사용)로 작동합니다. 신호 및 슬롯은에서 파생되는 클래스의 객체에서만 작동한다는 제한이 있습니다 QObject.

2
PyDispatcher에 기반이되는, 또한 루이있다 : pypi.python.org/pypi/Louie/1.1
the979kid

@ the979kid louie가 제대로 유지되지 않는 것 같습니다. pypi 페이지는 GitHub의 404에 연결됩니다. 11craft.github.io/louie ; github.com/gldnspud/louie . 해야 github.com/11craft/louie .
florisla

1
weakref'ed 이벤트 리스너는 일반적으로 필요합니다. 그렇지 않으면 실제 사용이 힘들어집니다. 유용 할 수있는 솔루션을 지원합니다.
kxr

Pypubsub 4는 다 대다이며 메시지에 대한 강력한 디버깅 도구와 메시지 페이로드를 제한하는 몇 가지 방법을 통해 유효하지 않은 데이터 나 누락 된 데이터를 보낸시기를 미리 알 수 있습니다. PyPubSub 4는 Python 3을 지원합니다 (PyPubSub 3.x는 Python 2를 지원합니다).
Oliver

최근 에이 목록에 적합 할 수있는 pymq github.com/thrau/pymq 라는 라이브러리를 게시했습니다 .
thrau

100

나는 이런 식으로하고있다 :

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)

그러나 내가 본 다른 모든 것과 마찬가지로 자동 생성 된 pydoc도없고 서명도 없습니다.


3
나는이 스타일이 다소 흥미 롭다. 다정한 뼈입니다. 이벤트와 구독자를 자율 작업으로 조작 할 수 있다는 점이 마음에 듭니다. 실제 프로젝트에서 어떻게 진행되는지 보겠습니다.
Rudy Lattae

2
매우 아름다운 미니멀리즘 스타일! 감독자!
akaRem

2
나는 이것을 충분히지지 할 수 없다. 이것은 정말로 간단하고 쉽다.

2
큰 호의, 누군가 내가 10 살처럼 이것을 설명 할 수 있을까? 이 클래스는 메인 클래스에 상속됩니까? init 이 보이지 않으므로 super ()가 사용되지 않습니다. 어떤 이유로 든 클릭하지 않습니다.
omgimdrunk

1
@omgimdrunk 간단한 이벤트 핸들러는 이벤트가 발생할 때마다 하나 이상의 호출 가능한 함수를 발생시킵니다. 이것을 "관리"하는 클래스는 최소한 다음과 같은 메소드가 필요합니다-add & fire. 해당 클래스 내에서 실행할 핸들러 목록을 유지해야합니다. _bag_of_handlers이것을 목록 인 인스턴스 변수에 넣겠습니다 . 클래스의 add 메소드는 단순히입니다 self._bag_of_handlers.append(some_callable). 클래스의 fire 메소드는 제공된 args와 kwargs를 핸들러에 전달하여 _bag_of_handlers를 통해 루프하고 순차적으로 실행합니다.
Gabe Spradlin

69

Michael Foord가 제안한 EventHook을 그의 이벤트 패턴에서 사용합니다 .

다음과 같이 클래스에 EventHooks를 추가하십시오.

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()

객체에서 모든 리스너를 Michaels 클래스로 제거하는 기능을 추가하고 다음과 같이 끝냈습니다.

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

이를 사용하는 단점은 구독자로 등록하기 전에 먼저 이벤트를 추가해야한다는 것입니다. 게시자 만 이벤트를 추가해야하는 경우 (필수, 좋은 방법은 아님) 대규모 프로젝트에서는 고통
Jonathan

6
self .__ handlers가 반복 중에 수정되기 때문에 마지막 방법은 버그가 있습니다. 수정 :`self .__ handlers = [h.im_self! = obj] 인 경우 self .__ handlers에서 h의 h
Simon Bergot

1
@Simon은 옳지 만 self .__ handlers에 언 바운드 기능을 가질 수 있기 때문에 버그가 발생합니다. 수정 :self.__handlers = [h for h in self._handlers if getattr(h, 'im_self', False) != obj]
Eric Marcos

20

zope.event 사용 합니다 . 당신이 상상할 수있는 가장 베어 본입니다. :-) 사실 완전한 소스 코드는 다음과 같습니다.

subscribers = []

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

예를 들어 프로세스간에 메시지를 보낼 수는 없습니다. 메시징 시스템이 아니라 이벤트 시스템 일뿐입니다.


17
pypi.python.org/pypi/zope.event ... 빈약 한 구글을 밴드와 함께 구하기 위해 ;-)
Boldewyn

여전히 메시지를 보내려고합니다. Tkinter에 구축 된 응용 프로그램에서 이벤트 시스템을 사용하고 있습니다. 메시지를 지원하지 않기 때문에 이벤트 시스템을 사용하지 않습니다.
Josip

zope.event로 원하는 것을 보낼 수 있습니다. 그러나 요점은 다른 프로세스 또는 다른 컴퓨터로 이벤트 / 메시지를 보낼 수 없으므로 적절한 메시징 시스템이 아니라는 것입니다. 아마도 요구 사항에 좀 더 구체적이어야합니다.
Lennart Regebro

15

Valued Lessons 에서이 작은 스크립트를 찾았습니다 . 내가 추구하는 올바른 단순성 / 출력 비율을 가진 것 같습니다. Peter Thatcher는 다음 코드의 작성자입니다 (라이센스는 언급되지 않음).

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
목록 대신 set ()을 사용하면 핸들러가 두 번 등록되는 것을 피할 수 있습니다. 결과적으로 핸들러가 등록 된 순서대로 호출되지 않습니다. 반드시 나쁜 것은 아니지만…
florisla

1
@florisla는 원하는 경우 OrderedSet로 교체 할 수 있습니다.
Robino

9

다음은 잘 작동하는 최소한의 디자인입니다. 당신이해야 할 일은 단순히 Observer클래스에서 상속 하고 나중에 observe(event_name, callback_fn)특정 이벤트를 수신 하는 데 사용 하는 것입니다. 특정 이벤트가 코드의 어느 위치 (예 :)에서 시작될 때마다 Event('USB connected')해당 콜백이 실행됩니다.

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)

예:

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')

나는 당신의 디자인을 좋아합니다. 최소한이고 이해하기 쉽습니다. 일부 모듈을 가져 오지 않아도되므로 가벼워집니다.
Atreyagaurav에서

8

EventManager클래스를 만들었습니다 (끝 부분에 코드). 구문은 다음과 같습니다.

#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()

다음은 예입니다.

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')

산출:

첫 인사
인사말 오스카
안녕하세요 오스카

인사말 삭제
안녕하세요 Oscar

EventManger 코드 :

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

당신은 pymitter ( pypi)를 볼 수 있습니다 )를 . "단일 네임 스페이스, 와일드 카드 및 TTL 제공"의 작은 단일 파일 (~ 250 개 위치) 접근 방식입니다.

기본 예는 다음과 같습니다.

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

나는 수신자와 발신자 모두의 서명을 보장하는 Longpoke의 최소한의 접근 방식을 변형했습니다.

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

pyQt에서 코드를 작성하면 QT 소켓 / 신호 패러다임을 사용합니다 .django와 동일합니다.

비동기 I / OI를 수행하는 경우 기본 선택 모듈을 사용하십시오.

SAX 파이썬 파서를 사용한다면 SAX에서 제공하는 이벤트 API를 사용하고 있습니다. 따라서 기본 API의 희생자 인 것 같습니다 :-)

이벤트 프레임 워크 / 모듈에서 무엇을 기대해야하는지 스스로에게 물어봐야합니다. 개인적으로 선호하는 것은 QT의 소켓 / 신호 패러다임을 사용하는 것입니다. 그것에 대한 자세한 정보는 여기 에서 찾을 수 있습니다


2

고려해야 할 다른 모듈 이 있습니다. 보다 까다로운 응용 분야에 적합한 선택 인 것 같습니다.

Py-notify는 Observer 프로그래밍 패턴을 구현하기위한 도구를 제공하는 Python 패키지입니다. 이러한 도구에는 신호, 조건 및 변수가 포함됩니다.

신호는 신호가 방출 될 때 호출되는 핸들러 목록입니다. 조건은 기본적으로 조건 상태가 변경 될 때 방출되는 신호와 결합 된 부울 변수입니다. 표준 논리 연산자 (not 등)를 사용하여 복합 조건으로 결합 할 수 있습니다. 조건과 달리 변수는 부울뿐만 아니라 모든 Python 객체를 보유 할 수 있지만 결합 할 수는 없습니다.


1
홈페이지가 더 이상 지원되지 않아 더 이상 지원되지 않습니까?
David Parks

1

이벤트 병합 또는 재시 도와 같이보다 복잡한 작업을 수행하려면 Observable 패턴과이를 구현하는 성숙한 라이브러리를 사용할 수 있습니다. https://github.com/ReactiveX/RxPY . Observable은 Javascript 및 Java에서 매우 일반적이며 일부 비동기 작업에 사용하기가 매우 편리합니다.

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())

출력 :

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

1

당신은 프로세스 또는 네트워크 경계를 넘어 작동하는 eventbus이 필요한 경우 시도 할 수 PyMQ을 . 현재 발행 / 구독, 메시지 큐 및 동기 RPC를 지원합니다. 기본 버전은 Redis 백엔드에서 작동하므로 실행중인 Redis 서버가 필요합니다. 테스트를위한 인 메모리 백엔드도 있습니다. 자신의 백엔드를 작성할 수도 있습니다.

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')

시스템을 초기화하려면

from pymq.provider.redis import RedisConfig

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

# main application control loop

pymq.shutdown()

면책 조항 : 나는이 도서관의 저자입니다


0

buslane모듈 을 사용해 볼 수 있습니다 .

이 라이브러리를 사용하면 메시지 기반 시스템을보다 쉽게 ​​구현할 수 있습니다. 명령 (단일 핸들러) 및 이벤트 (0 또는 다중 핸들러) 접근 방식을 지원합니다. Buslane은 Python 유형 주석을 사용하여 핸들러를 올바르게 등록합니다.

간단한 예 :

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',
))

buslane을 설치하려면 간단히 pip를 사용하십시오.

$ pip install buslane

0

얼마 전에 저는 여러분에게 유용한 라이브러리를 작성했습니다. 로컬 및 글로벌 리스너, 여러 가지 등록 방법, 실행 우선 순위 등을 가질 수 있습니다.

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

모양의이 pyeventdispatcher을

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.