Python'da etkinlik sistemi


196

Python için hangi olay sistemini kullanıyorsunuz? Pydispatcher'ın zaten farkındayım , ancak başka neler bulunabileceğini veya yaygın olarak kullanıldığını merak ediyordum?

Büyük çerçevelerin bir parçası olan etkinlik yöneticileriyle ilgilenmiyorum, kolayca genişletebileceğim küçük bir çıplak kemik çözümü kullanmayı tercih ederim.

Yanıtlar:


181

PyPI paketleri

Haziran 2020 itibariyle, bunlar en son çıkış tarihlerine göre sıralanan PyPI'de bulunan olayla ilgili paketlerdir.

Fazlası var

Çok farklı bir terminoloji (olaylar, sinyaller, işleyiciler, yöntem dağıtımı, kancalar, ...) kullanarak seçim yapabileceğiniz çok sayıda kütüphane.

Yukarıdaki paketlerin yanı sıra burada cevaplarda belirtilen tekniklerin bir özetini tutmaya çalışıyorum.

İlk olarak, bazı terminoloji ...

Gözlemci modeli

Olay sisteminin en temel tarzı, Gözlemci modelinin basit bir uygulaması olan 'işleyici yöntemleri torbası'dır .

Temel olarak, işleyici yöntemleri (callables) bir dizide saklanır ve olay 'tetiklendiğinde' her biri çağrılır.

Yayınla-Abone

Observer olay sistemlerinin dezavantajı, işleyicileri yalnızca gerçek Event nesnesine (veya işleyiciler listesine) kaydedebilmenizdir. Yani kayıt sırasında etkinliğin zaten mevcut olması gerekir.

Bu nedenle olay sistemlerinin ikinci stili vardır: yayınla-abone ol düzeni . Burada, işleyiciler bir olay nesnesine (veya işleyici listesine) değil, merkezi bir dağıtım programına kaydolur. Ayrıca bildirimler yalnızca dağıtım görevlisi ile konuşur. Ne dinleyeceğiniz veya ne yayınlayacağınız, bir isimden (dize) başka bir şey olmayan 'sinyal' ile belirlenir.

Aracı deseni

İlgilenebilir: Arabulucu modeli .

Kancalar

Bir 'kanca' sistemi genellikle uygulama eklentileri bağlamında kullanılır. Uygulama sabit entegrasyon noktaları (kancalar) içerir ve her eklenti bu kancaya bağlanabilir ve belirli eylemleri gerçekleştirebilir.

Diğer olaylar'

Not: threading.Event , yukarıdaki anlamda bir 'olay sistemi' değildir. Bir iş parçacığının başka bir iş parçacığı Event nesnesini 'işaretleyene' kadar beklediği iş parçacığı eşitleme sistemidir.

Ağ mesajlaşma kütüphaneleri genellikle 'etkinlikler' terimini de kullanır; bazen bunlar kavramda benzerdir; bazen değil. Elbette iş parçacığı, süreç ve bilgisayar sınırlarını aşabilirler. Bkz. Örneğin pyzmq , pymq , Twisted , Tornado , gevent , eventlet .

Zayıf referanslar

Python'da, bir yönteme veya nesneye başvuruda bulunulması, çöp toplayıcı tarafından silinmemesini sağlar. Bu arzu edilebilir, ancak bellek sızıntılarına da neden olabilir: bağlı işleyiciler asla temizlenmez.

Bazı olay sistemleri bunu çözmek için normal olanlar yerine zayıf referanslar kullanır.

Çeşitli kütüphaneler hakkında bazı kelimeler

Gözlemci tarzı olay sistemleri:

  • zope.event bunun nasıl çalıştığına dair çıplak kemikleri gösterir (bkz . Lennart'ın cevabı ). Not: bu örnek işleyici argümanlarını bile desteklemez.
  • LongPoke'ın 'çağrılabilir liste' uygulaması, böyle bir olay sisteminin alt sınıflandırma yoluyla çok minimalist olarak uygulanabileceğini göstermektedir list.
  • Felk'in varyasyonu EventHook ayrıca callees ve arayanların imzalarını da sağlar.
  • spassig'in EventHook'u (Michael Foord'un Etkinlik Paterni) basit bir uygulamadır.
  • Josip'in Değerli Dersleri Event sınıfı temelde aynıdır, ancak çantayı saklamak için a setyerine listve __call__her ikisi de makul eklemeler olan aletler kullanır .
  • PyNotify , kavram olarak benzerdir ve ayrıca değişkenler ve koşullar ('değişken değiştirilmiş olay') için ek kavramlar sağlar. Ana sayfa işlevsel değil.
  • axel temel olarak diş çekme, hata işleme, ... ile ilgili daha fazla özelliğe sahip bir torba işleyicidir.
  • python-dispatch , türetmek için çift kaynak sınıflarını gerektirir pydispatch.Dispatcher.
  • buslane sınıf tabanlıdır, tek veya çoklu işleyicileri destekler ve kapsamlı tip ipuçlarını kolaylaştırır.
  • Pithikos'un Gözlemcisi / Etkinliği hafif bir tasarımdır.

Yayınlama-abone olma kütüphaneleri:

  • flaşör , göndericiye dayalı otomatik bağlantı kesme ve filtreleme gibi bazı şık özelliklere sahiptir.
  • PyPubSub kararlı bir pakettir ve "konuların ve mesajların hata ayıklamasını ve bakımını kolaylaştıran gelişmiş özellikler" vaat eder.
  • pymitter , Node.js EventEmitter2'nin bir Python bağlantı noktasıdır ve ad alanları, joker karakterler ve TTL sunar.
  • PyDispatcher, çoktan çoğa yayın vb. İle ilgili esnekliği vurgulamaktadır. Zayıf referansları destekler.
  • louie elden geçirilmiş bir PyDispatcher ve "çok çeşitli bağlamlarda" çalışmalıdır.
  • pypydispatcher dayanır (tahmin ettiniz ...) PyDispatcher ve aynı zamanda PyPy çalışır.
  • django.dispatch "daha sınırlı bir arayüz ancak daha yüksek performansa sahip" yeniden yazılmış bir PyDispatcher.
  • pyeventdispatcher , PHP'nin Symfony çerçevesinin olay göndericisini temel alır.
  • dağıtıcı django.dispatch dosyasından ayıklandı, ancak oldukça eski oluyor.
  • Cristian Garcia'nın EventManger'ı gerçekten kısa bir uygulama.

Diğerleri:

  • pluggy , pytesteklentiler tarafından kullanılan bir kanca sistemi içerir .
  • RxPy3 , Gözlenebilir patern uygular ve olayların birleştirilmesine, yeniden denenmesine vb. Olanak tanır.
  • Qt'nin Sinyalleri ve Yuvaları PyQt veya PySide2'den edinilebilir . Aynı iş parçacığında kullanıldığında geri arama veya iki farklı iş parçacığı arasında olaylar (bir olay döngüsü kullanarak) olarak çalışırlar. Sinyaller ve Yuvalar, sadece türetilmiş sınıf nesnelerinde çalışma sınırına sahiptir QObject.

2
Ayrıca PyDispatcher'a dayanan louie var: pypi.python.org/pypi/Louie/1.1
the979kid

@ the979kid louie kötü bir şekilde korunuyor gibi görünüyor, pypi sayfası GitHub'da 404'lere bağlanıyor : 11craft.github.io/louie ; github.com/gldnspud/louie . Olmalı github.com/11craft/louie .
florisla

1
zayıf cevaplı olay dinleyicileri ortak bir ihtiyaçtır. Aksi takdirde gerçek dünya kullanımı güçleşir. Yararlı olabilecek çözümlerin desteklediği bir not.
kxr

Pypubsub 4 çoktan çoğa ve mesajlar için güçlü hata ayıklama araçlarına ve mesaj yüklerini kısıtlamanın çeşitli yollarına sahiptir, böylece geçersiz veri veya eksik veri gönderdiğinizde daha önce bilirsiniz. PyPubSub 4 Python 3'ü destekler (ve PyPubSub 3.x Python 2'yi destekler).
Oliver

Geçenlerde pymq github.com/thrau/pymq adlı bir kütüphane yayınladım .
thrau

100

Bunu şu şekilde yapıyorum:

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)

Ancak, gördüğüm her şeyde olduğu gibi, bunun için otomatik olarak oluşturulan bir pydoc ve gerçekten berbat hiçbir imza yok.


3
Bu stili oldukça ilgi çekici buluyorum. Tatlı çıplak kemikler. Birinin olayları ve abonelerini otonom operasyonlar olarak manipüle etmesine izin vermesini seviyorum. Gerçek bir projede nasıl yürüdüğünü göreceğim.
Rudy Lattae

2
Çok güzel minimalist tarzı! Süper!
akaRem

2
Bunu yeterince değerlendiremiyorum, bu gerçekten basit ve kolaydır.

2
büyük bir iyilik, birisi bunu 10 yaşında gibi açıklayabilir mi? Bu sınıf ana sınıf tarafından miras alınır? Süper () kullanılmayacak bir init görmüyorum . Bir sebepten dolayı benim için tıklamıyor.
omgimdrunk

1
@omgimdrunk Basit bir olay işleyicisi, bir olay tetiklendiğinde bir veya daha fazla çağrılabilir işlevi tetikler. Bunu sizin için "yönetmek" için bir sınıf en azından aşağıdaki yöntemlere gereksinim duyar - add & fire. Bu sınıf içinde yürütülecek işleyicilerin bir listesini tutmanız gerekir. Bunu _bag_of_handlersbir liste olan örnek değişkenine koyalım . Sınıfın add yöntemi basitçe olurdu self._bag_of_handlers.append(some_callable). Sınıfın yangın yöntemi, sağlanan argümanları ve kwarg'ları işleyicilere ileten _bag_of_handlers` ile döngü yapar ve her birini sırayla yürütür.
Gabe Spradlin

69

Etkinlik Kalıbında Michael Foord'un önerdiği gibi bir EventHook kullanıyoruz :

Sadece aşağıdakilere sahip sınıflarınıza EventHooks ekleyin:

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

Tüm dinleyiciyi bir nesneden Michaels sınıfına kaldırma işlevini ekliyoruz ve bununla sonuçlanıyoruz:

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

Bunu kullanmanın bir dezavantajı, abone olarak kaydolmadan önce bir etkinlik eklemeniz gerektiğidir. Yalnızca yayıncılar etkinliklerini eklerse (bir zorunluluk değil, sadece iyi bir uygulama), yayıncıları büyük projelerde bir acı olan abonelerden önce başlatmalısınız
Jonathan

6
son yöntem hatalıdır çünkü yinelemeler sırasında self .__ handler değiştirilmiştir. Düzeltme: `` self .__ handlers = [h.im_self ise self .__ handlers içinde h için h =! Obj] `
Simon Bergot

1
@Simon haklı, ancak bir hata veriyor çünkü self .__ handler'da bağlı olmayan fonksiyonlara sahip olabiliriz. Düzeltme:self.__handlers = [h for h in self._handlers if getattr(h, 'im_self', False) != obj]
Eric Marcos

20

Ben zope.event kullanıyorum . Hayal edebileceğiniz en çıplak kemikler. :-) Aslında, tam kaynak kodu:

subscribers = []

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

Örneğin, işlemler arasında mesaj gönderemeyeceğinizi unutmayın. Bu bir mesajlaşma sistemi değil, sadece bir olay sistemi, başka bir şey, hiçbir şey daha az.


17
pypi.python.org/pypi/zope.event ... fakir Google'ı bazı bant genişliği kaydetmek için ;-)
Boldewyn

Hala mesaj gönderebilmek istiyorum. Olay sistemini Tkinter üzerine kurulu uygulamada kullanıyordum. Mesaj sistemini desteklemediği için bu olay sistemini kullanmıyorum.
Josip

Zope.event ile istediğiniz her şeyi gönderebilirsiniz. Ama benim açımdan, diğer işlemlere veya diğer bilgisayarlara olay / ileti gönderemeyeceğiniz için, bunun uygun bir mesajlaşma sistemi olmamasıdır. Muhtemelen gereksinimlerinize göre bir ama daha spesifik olmalısınız.
Lennart Regebro

15

Bu küçük senaryoyu Değerli Derslerde buldum . Sadece doğru basitlik / güç oranına sahip olduğum anlaşılıyor. Peter Thatcher aşağıdaki kodun yazarıdır (lisanstan bahsedilmez).

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
İşleyicilerin iki kez kaydedilmesini önlemek için liste yerine set () kullanmak güzeldir. Bunun bir sonucu, işleyicilerin kaydedildikleri sırayla çağrılmamasıdır. Ama mutlaka kötü bir şey değil ...
florisla

1
@florisla istenirse OrderedSet için takas yapabilir.
Robino

9

İşte iyi çalışması gereken minimal bir tasarım. Yapmanız gereken sadece Observerbir sınıfı devralmak ve daha sonra observe(event_name, callback_fn)belirli bir olayı dinlemek için kullanmaktır . Söz konusu olay kodun herhangi bir yerine (ör. Event('USB connected')) Her tetiklendiğinde , karşılık gelen geri arama tetiklenir .

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)

Misal:

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

Tasarımınızı seviyorum, minimalist ve anlaşılması kolay. ve bazı modülleri içe aktarmak zorunda kalmadan hafif olacaktı.
Atreyagaurav

8

Bir EventManagersınıf (sonunda kod) oluşturdum. Sözdizimi şöyledir:

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

İşte bir örnek:

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

Çıktı:

İlk selam
selamlar Oscar
Merhaba Oscar

Şimdi selamları kaldır
Merhaba Oscar

EventManger Kodu:

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'a ( pypi ) bir göz atabilirsiniz . Onun küçük bir tek dosya (~ 250 loc) yaklaşım "ad alanları, joker karakterler ve TTL sağlayan".

İşte temel bir örnek:

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'un hem callees hem de arayanlar için imzalar sağlayan minimalist yaklaşımının bir varyasyonunu yaptım:

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

Eğer pyQt kod yaparsam QT yuvaları / sinyalleri paradigmasını kullanırsam, aynı django için

Zaman uyumsuz G / Ç yapıyorsam yerel seçim modülünü kullan

Bir SAX python ayrıştırıcısı kullanıyorsam SAX tarafından sağlanan olay API'sini kullanıyorum. Yani altta yatan API kurbanı gibi görünüyor :-)

Belki kendinize olay çerçevesinden / modülünden ne beklediğinizi sormalısınız. Kişisel tercihim, QT'den Soket / Sinyal paradigmasını kullanmaktır. bunun hakkında daha fazla bilgiyi burada bulabilirsiniz


2

İşte dikkate alınacak başka bir modül . Daha zorlu uygulamalar için uygun bir seçim gibi görünüyor.

Py-notify, Gözlemci programlama modelini uygulamak için araçlar sağlayan bir Python paketidir. Bu araçlar sinyalleri, koşulları ve değişkenleri içerir.

Sinyaller, sinyal verildiğinde çağrılan işleyicilerin listeleridir. Koşullar temel olarak koşul durumu değiştiğinde yayılan bir sinyale bağlı boole değişkenleridir. Standart mantıksal operatörler (değil, vb.) Kullanılarak bileşik koşullara birleştirilebilirler. Değişkenler, koşulların aksine, yalnızca boolean'ları değil, herhangi bir Python nesnesini tutabilirler, ancak birleştirilemezler.


1
Ana sayfa bunun için komisyon dışında, belki de artık desteklenmiyor mu?
David Parks

1

Etkinlikleri birleştirmek veya yeniden denemek gibi daha karmaşık şeyler yapmak istiyorsanız, Gözlemlenebilir modeli ve bunu uygulayan olgun bir kütüphaneyi kullanabilirsiniz. https://github.com/ReactiveX/RxPY . Gözlemlenebilirler Javascript ve Java'da çok yaygındır ve bazı zaman uyumsuz görevler için kullanımı çok uygundur.

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

ÇIKTI :

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

1

Eğer süreç veya ağ sınırları boyunca çalışan bir eventbus gerekiyorsa deneyebileceğiniz PyMQ . Şu anda pub / sub, mesaj kuyrukları ve senkronize RPC'yi desteklemektedir. Varsayılan sürüm bir Redis arka ucunun üstünde çalışır, bu nedenle çalışan bir Redis sunucusuna ihtiyacınız vardır. Test için bir bellek içi arka uç da vardır. Kendi arka ucunuzu da yazabilirsiniz.

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

Sistemi başlatmak için:

from pymq.provider.redis import RedisConfig

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

# main application control loop

pymq.shutdown()

Feragatname: Bu kütüphanenin yazarıyım


0

buslaneModülü deneyebilirsiniz .

Bu kütüphane, mesaj tabanlı sistemin uygulanmasını kolaylaştırır. Komutları (tek işleyici) ve olayları (0 veya birden fazla işleyici) yaklaşımını destekler. Buslane, işleyiciyi doğru şekilde kaydetmek için Python tipi ek açıklamalar kullanır.

Basit örnek:

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 kurmak için pip kullanın:

$ pip install buslane

0

Bir süre önce sizin için yararlı olabilecek bir kitaplık yazdım. Yerel ve global dinleyicilere, onları kaydetmenin birden fazla farklı yoluna, yürütme önceliğine vb. Sahip olmanızı sağlar.

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

Göz atın pyeventdispatcher

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.