पायथन में घटना प्रणाली


196

पायथन के लिए आप किस इवेंट सिस्टम का उपयोग करते हैं? मुझे पहले से ही pydispatcher के बारे में पता है , लेकिन मैं सोच रहा था कि और क्या मिल सकता है, या आमतौर पर उपयोग किया जाता है?

मुझे इवेंट मैनेजरों में कोई दिलचस्पी नहीं है जो बड़े चौखटे का हिस्सा हैं, मैं एक छोटे से नंगे हड्डियों के समाधान का उपयोग करूंगा जिसे मैं आसानी से बढ़ा सकता हूं।

जवाबों:


179

PyPI पैकेज

जून 2020 तक, ये घटना-संबंधित पैकेज PyPI पर उपलब्ध हैं, जो कि हालिया रिलीज़ डेट द्वारा आदेशित किया गया है।

अभी और है

बहुत अधिक शब्दावली (घटनाओं, संकेतों, संचालकों, विधि प्रेषण, हुक, ...) का उपयोग करके बहुत से पुस्तकालयों का चयन करना है।

मैं उपरोक्त पैकेजों का अवलोकन रखने की कोशिश कर रहा हूं, साथ ही साथ उत्तर में वर्णित तकनीकों को भी।

सबसे पहले, कुछ शब्दावली ...

ऑब्जर्वर पैटर्न

ईवेंट सिस्टम की सबसे बुनियादी शैली 'हैंडलर विधियों का बैग' है, जो ऑब्जर्वर पैटर्न का एक सरल कार्यान्वयन है ।

मूल रूप से, हैंडलर तरीके (कॉलबेल) एक सरणी में संग्रहीत किए जाते हैं और प्रत्येक को घटना 'फायर' होने पर कहा जाता है।

प्रकाशित-सदस्यता लें

ऑब्जर्वर इवेंट सिस्टम का नुकसान यह है कि आप केवल वास्तविक इवेंट ऑब्जेक्ट (या हैंडलर सूची) पर हैंडलर पंजीकृत कर सकते हैं। तो पंजीकरण के समय घटना पहले से ही मौजूद है।

यही कारण है कि इवेंट सिस्टम की दूसरी शैली मौजूद है: प्रकाशन-सदस्यता पैटर्न । यहाँ, हैंडलर किसी ईवेंट ऑब्जेक्ट (या हैंडलर सूची) पर पंजीकरण नहीं करते हैं, लेकिन एक केंद्रीय डिस्पैचर पर। इसके अलावा नोटिफ़ायर केवल डिस्पैचर से बात करते हैं। क्या सुनना है, या क्या प्रकाशित करना है यह 'सिग्नल' द्वारा निर्धारित किया जाता है, जो कि नाम (स्ट्रिंग) से ज्यादा कुछ नहीं है।

मध्यस्थ पैटर्न

रुचि भी हो सकती है: मध्यस्थ पैटर्न

हुक्स

एक 'हुक' प्रणाली का उपयोग एप्लिकेशन प्लगइन्स के संदर्भ में किया जाता है। एप्लिकेशन में निश्चित एकीकरण बिंदु (हुक) होते हैं, और प्रत्येक प्लगइन उस हुक से जुड़ सकता है और कुछ क्रियाएं कर सकता है।

अन्य 'घटनाएँ'

नोट: threading.Event उपरोक्त अर्थ में एक 'ईवेंट सिस्टम' नहीं है। यह एक थ्रेड सिंक्रोनाइज़ेशन सिस्टम है जहाँ एक थ्रेड तब तक प्रतीक्षा करता है जब तक कि कोई अन्य थ्रेड इवेंट ऑब्जेक्ट को 'सिग्नल' नहीं देता।

नेटवर्क मैसेजिंग लाइब्रेरी अक्सर 'घटनाओं' शब्द का भी उपयोग करते हैं; कभी-कभी ये अवधारणा में समान होते हैं; कभी-कभी नहीं। वे बेशक धागा-, प्रक्रिया- और कंप्यूटर की सीमाओं को पार कर सकते हैं। जैसे देखें pyzmq , pymq , मुड़ , तूफान , gevent , eventlet

कमजोर संदर्भ

पायथन में, किसी विधि या वस्तु के संदर्भ को रखने से यह सुनिश्चित होता है कि यह कचरा संग्राहक द्वारा हटाया नहीं जाएगा। यह वांछनीय हो सकता है, लेकिन यह मेमोरी लीक को भी जन्म दे सकता है: लिंक किए गए हैंडलर को कभी भी साफ नहीं किया जाता है।

कुछ ईवेंट सिस्टम इसे सुलझाने के लिए नियमित के बजाय कमजोर संदर्भों का उपयोग करते हैं।

विभिन्न पुस्तकालयों के बारे में कुछ शब्द

प्रेक्षक शैली की घटना प्रणाली:

  • zope.event नंगे हड्डियों को दिखाता है कि यह कैसे काम करता है ( Lennart का जवाब देखें )। नोट: यह उदाहरण हैंडलर के तर्कों का भी समर्थन नहीं करता है।
  • लॉन्गपोक की P कॉल करने योग्य सूची ’ कार्यान्वयन से पता चलता है कि इस तरह की घटना प्रणाली को बहुत कम करके उप-वर्ग द्वारा लागू किया जा सकता है list
  • फेलक की भिन्नता EventHook भी callees और callers के हस्ताक्षर सुनिश्चित करता है।
  • स्पैसिग का इवेंटहुक (माइकल फॉर्ड्स इवेंट पैटर्न) एक सीधा कार्यान्वयन है।
  • जोसिप के वैल्यूड लेसनस इवेंट क्लास मूल रूप से एक ही है, लेकिन बैग को स्टोर करने के लिए setइसके बजाय का उपयोग करता है list, और लागू होता है __call__जो दोनों उचित जोड़ हैं।
  • PyNotify अवधारणा में समान है और यह चर और स्थितियों ('परिवर्तनशील परिवर्तन') की अतिरिक्त अवधारणा भी प्रदान करता है। मुखपृष्ठ क्रियाशील नहीं है।
  • एक्सल मूल रूप से एक बैग-ऑफ-हैंडलर है जिसमें थ्रेडिंग, एरर हैंडलिंग, ...
  • अजगर-प्रेषण के लिए भी स्रोत वर्गों की आवश्यकता होती है pydispatch.Dispatcher
  • Buslane वर्ग-आधारित है, एकल या एकाधिक हैंडलर का समर्थन करता है और व्यापक प्रकार के संकेत की सुविधा देता है।
  • Pithikos ' ऑब्जर्वर / इवेंट एक हल्का डिज़ाइन है।

प्रकाशित-सदस्यता पुस्तकालय:

  • ब्लिंकर में कुछ निफ्टी फीचर्स होते हैं जैसे ऑटोमैटिक डिसकनेक्शन और सेंडर पर आधारित फ़िल्टरिंग।
  • PyPubSub एक स्थिर पैकेज है, और "उन्नत सुविधाओं का वादा करता है जो डिबगिंग और विषयों और संदेशों को बनाए रखने की सुविधा प्रदान करता है"।
  • pymitter Node.js EventEmitter2 का पायथन पोर्ट है और नामस्थान, वाइल्डकार्ड और TTL प्रदान करता है।
  • PyDispatcher कई-कई प्रकाशनों के संबंध में लचीलेपन पर जोर देने लगता है आदि कमजोर संदर्भों का समर्थन करता है।
  • लुई एक पर फिर से काम PyDispatcher है और "संदर्भों की एक विस्तृत विविधता में" काम करना चाहिए।
  • pypydispatcher पर आधारित है (आप यह अनुमान लगाया ...) PyDispatcher और PyPy में भी काम करता है।
  • django.dispatch एक पुनर्लेखन लिखित PyDispatcher है "अधिक सीमित इंटरफ़ेस के साथ, लेकिन उच्च प्रदर्शन"।
  • pyeventdispatcher PHP के सिम्फनी फ्रेमवर्क के इवेंट-डिस्पैचर पर आधारित है।
  • डिस्पैचर django.dispatch से निकाला गया था, लेकिन काफी पुराना हो रहा है।
  • क्रिस्टियन गार्सिया का EventManger वास्तव में एक छोटा कार्यान्वयन है।

अन्य:

  • Pluggy में एक हुक सिस्टम होता है जो pytestप्लगइन्स द्वारा उपयोग किया जाता है।
  • RxPy3 ऑब्जर्वेबल पैटर्न को लागू करता है और विलय की घटनाओं, रिट्री आदि की अनुमति देता है।
  • क्यूटी के सिग्नल और स्लॉट PyQt या PySide2 से उपलब्ध हैं । वे एक ही धागे में, या दो अलग-अलग धागों के बीच (इवेंट लूप का उपयोग करके) कॉलबैक के रूप में काम करते हैं। सिग्नल और स्लॉट की सीमा होती है कि वे केवल उन कक्षाओं की वस्तुओं में काम करते हैं जो इससे प्राप्त होते हैं QObject

2
लूई भी है, जो PyDispatcher पर आधारित है: pypi.python.org/pypi/Louie/1.1
the979kid

@ the979kid लूई बुरी तरह से बनाए रखा जा रहा है, GitHub पर 404s के लिए pypi पेज लिंक: 11craft.github.io/louie ; github.com/gldnspud/louieGithub.com/11craft/louie होना चाहिए ।
फ्लोरिसेला

1
कमज़ोर घटना वाले श्रोताओं की एक सामान्य ज़रूरत है। अन्यथा वास्तविक दुनिया का उपयोग कठिन हो जाता है। एक नोट जो समाधान का समर्थन करता है जो उपयोगी हो सकता है।
kxr

Pypubsub 4 कई-से-कई है, और इसमें संदेशों के लिए शक्तिशाली डीबगिंग उपकरण हैं, और संदेश पेलोड को विवश करने के कई तरीके हैं जिससे आप पहले जानते हैं जब आपने अमान्य डेटा या लापता डेटा भेजा है। PyPubSub 4 Python 3 (और PyPubSub 3.x Python 2 का समर्थन करता है) का समर्थन करता है।
ओलिवर

मैंने हाल ही में pymq github.com/thrau/pymq नाम से एक लाइब्रेरी प्रकाशित की है जो इस सूची के लिए एक अच्छा फिट हो सकता है।
थ्रू

99

मैं इसे इस तरह से कर रहा हूं:

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)

हालांकि, मैंने जो कुछ भी देखा है, उसके साथ, इसके लिए कोई ऑटो जनरेट किया गया पाइडोक नहीं है, और कोई हस्ताक्षर नहीं है, जो वास्तव में बेकार है।


3
मुझे यह शैली पेचीदा लगती है। यह मीठी नंगी-हड्डियाँ हैं। मुझे यह तथ्य पसंद है कि यह घटनाओं और उनके ग्राहकों को स्वायत्त संचालन के रूप में हेरफेर करने की अनुमति देता है। मैं देखूंगा कि यह एक वास्तविक परियोजना में कैसे किराए पर है।
रुडी लट्टे

2
बहुत सुंदर न्यूनतर शैली! उत्तम!
उर्फ राम

2
मैं इसे पर्याप्त नहीं कर सकता, यह वास्तव में सरल और आसान है।

2
बड़ा एहसान, कोई मुझे इस तरह समझा सकता था 10? क्या यह वर्ग मुख्य वर्ग को विरासत में मिला है? मैं एक init इतना सुपर () इस्तेमाल नहीं किया जाएगा नहीं दिख रहा है । यह किसी कारण से मेरे लिए क्लिक नहीं कर रहा है।
omgimdrunk

1
@omgimdrunk जब भी कोई घटना होती है तो एक साधारण घटना हैंडलर एक या अधिक कॉल करने योग्य कार्यों को बंद कर देता है। आपके लिए इसे "प्रबंधित" करने के लिए एक वर्ग को निम्नलिखित विधियों की आवश्यकता होगी - न्यूनतम आग और आग। उस वर्ग के भीतर आपको निष्पादित होने के लिए हैंडलर की एक सूची बनाए रखने की आवश्यकता होगी। आइए डालते हैं उदाहरण चर में _bag_of_handlersजो एक सूची है। क्लास की ऐड मेथड बस होगी self._bag_of_handlers.append(some_callable)। कक्षा की अग्नि विधि थ्रू को \ _बैग_ऑफ_हैंडलर्स को प्रदान करेगी और हैंडलर को प्रदान किए गए आर्ग और कंवर को पार करेगी और प्रत्येक क्रम में निष्पादित करेगी।
गाबे स्प्रेडलिन

69

हम अपने इवेंट पैटर्न में माइकल फॉर्ड से सुझाए गए 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()

हम सभी श्रोताओं को एक वस्तु से माइकल्स वर्ग में निकालने के लिए कार्यक्षमता जोड़ते हैं और इसके साथ समाप्त होते हैं:

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

इसका उपयोग करने का एक दोष यह है कि आपको एक ग्राहक के रूप में पंजीकरण करने से पहले एक घटना को जोड़ना होगा। यदि केवल प्रकाशक अपनी घटनाओं को जोड़ते हैं (एक चाहिए, सिर्फ एक अच्छा अभ्यास नहीं), तो आपको प्रकाशकों को ग्राहकों से पहले इनिशियलाइज़ करना होगा जो बड़ी परियोजनाओं में एक दर्द है
जोनाथन

6
अंतिम विधि को रोक दिया गया है क्योंकि स्व .__ संचालकों को पुनरावृत्तियों के दौरान संशोधित किया गया है। फिक्स: `स्व। _ हैंडलर = [एच फॉर एच में एच। स्व। हैंडलर अगर एच.आईएम_सेल्फ! = ओब्ज]`
साइमन बर्गोट

1
@ साइमन सही है, लेकिन एक बग का परिचय देता है क्योंकि हम स्व। हैंडलर में अनबाउंड कार्य कर सकते हैं। फिक्स:self.__handlers = [h for h in self._handlers if getattr(h, 'im_self', False) != obj]
एरिक मार्कोस

20

मैं zope.event का उपयोग करता हूं । यह सबसे नंगी हड्डियाँ हैं जिनकी आप कल्पना कर सकते हैं। :-) वास्तव में, यहाँ पूरा स्रोत कोड है:

subscribers = []

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

ध्यान दें कि आप उदाहरण के लिए प्रक्रियाओं के बीच संदेश नहीं भेज सकते। यह मैसेजिंग सिस्टम नहीं है, सिर्फ एक इवेंट सिस्टम है, कुछ ज्यादा नहीं, कुछ कम नहीं।


17
pypi.python.org/pypi/zope.event ... बेचारे Google को बचाने के लिए कुछ बैंडवाँ ;-)
Boldewyn

मैं अभी भी संदेश भेजने में सक्षम होना चाहता हूं। मैं Tkinter पर निर्मित एप्लिकेशन में ईवेंट सिस्टम का उपयोग कर रहा हूं। मैं इसे इवेंट सिस्टम का उपयोग नहीं कर रहा हूं क्योंकि यह संदेशों का समर्थन नहीं करता है।
जोसिप

आप zope.event के साथ जो चाहें भेज सकते हैं। लेकिन मेरा कहना यह है कि यह एक उचित संदेश प्रणाली नहीं है, क्योंकि आप घटनाओं / संदेशों को अन्य प्रक्रियाओं या अन्य कंप्यूटरों में नहीं भेज सकते हैं। आपको अपनी आवश्यकताओं के साथ संभवतः अधिक विशिष्ट होना चाहिए।
लेन्नर्ट रेगेब्र जूल

15

मुझे यह छोटी स्क्रिप्ट वैल्यूड लेसन पर मिली । ऐसा लगता है कि मेरे पास केवल सही सादगी / शक्ति अनुपात है। पीटर थैचर निम्नलिखित कोड के लेखक हैं (कोई लाइसेंसिंग का उल्लेख नहीं है)।

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
दो बार पंजीकृत होने से बचने के लिए सूची के बजाय सेट () का उपयोग करना अच्छा है। एक परिणाम यह है कि हैंडलर को उस क्रम में नहीं बुलाया जा रहा है जो वे पंजीकृत थे। जरूरी नहीं कि एक बुरी चीज हो ...
florisla

1
@florisla ऑर्डरेडसेट के लिए स्वैप कर सकता है, अगर कोई ऐसा चाहता है।
रोबिनो

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

मुझे आपका डिज़ाइन पसंद है, यह न्यूनतर और समझने में आसान है। और यह कुछ मॉड्यूल आयात नहीं करने से हल्का होगा।
अत्रेगौरव

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

आउटपुट:

प्रारंभिक सलामी
अभिवादन ऑस्कर
हैलो ऑस्कर

अब
नमस्कार नमस्ते ऑस्कर को हटा दें

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 ) पर एक नजर पड़ सकती है । इसकी एक छोटी सिंगल-फाइल (~ 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

मैंने लॉन्गपोक के अतिसूक्ष्म दृष्टिकोण में बदलाव किया है, जो कि कैलिस और कॉलर्स दोनों के लिए हस्ताक्षर सुनिश्चित करता है:

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 में कोड करता हूं तो मैं क्यूटी सॉकेट / सिग्नल प्रतिमान का उपयोग करता हूं, वही django के लिए है

अगर मैं async I / OI कर रहा हूँ तो देशी चुनिंदा मॉड्यूल का उपयोग करें

यदि मैं एक SAX अजगर पार्सर का उपयोग कर रहा हूं, तो मैं SAX द्वारा प्रदान की गई इवेंट API का उपयोग कर रहा हूं। तो ऐसा लगता है कि मैं अंतर्निहित API का शिकार हूं :-)

शायद आपको अपने आप से पूछना चाहिए कि आप इवेंट फ्रेमवर्क / मॉड्यूल से क्या उम्मीद करते हैं। मेरी व्यक्तिगत प्राथमिकता क्यूटी से सॉकेट / सिग्नल प्रतिमान का उपयोग करना है। इसके बारे में अधिक जानकारी यहाँ मिल सकती है


2

यहाँ विचार के लिए एक और मॉड्यूल है। यह अधिक मांग वाले अनुप्रयोगों के लिए एक व्यवहार्य विकल्प लगता है।

Py-सूचना एक पायथन पैकेज है जो ऑब्जर्वर प्रोग्रामिंग पैटर्न को लागू करने के लिए उपकरण प्रदान करता है। इन उपकरणों में सिग्नल, स्थितियां और चर शामिल हैं।

सिग्नल हैंडलर की सूची है जिसे सिग्नल उत्सर्जित होने पर कहा जाता है। स्थिति मूल रूप से बूलियन चर एक संकेत के साथ युग्मित होती है जो स्थिति की स्थिति में परिवर्तन होने पर उत्सर्जित होती है। उन्हें यौगिक स्थितियों में मानक तार्किक ऑपरेटरों (नहीं, और, आदि) का उपयोग करके जोड़ा जा सकता है। परिस्थितियों के विपरीत चर, किसी भी पायथन ऑब्जेक्ट को पकड़ सकते हैं, न केवल बूलियन, बल्कि उन्हें संयुक्त नहीं किया जा सकता है।


1
मुख पृष्ठ इस एक के लिए कमीशन से बाहर है, शायद किसी भी लंबे समय तक समर्थन नहीं किया जा रहा है?
डेविड पार्क्स

1

यदि आप विलय की घटनाओं या पुन: प्रयास जैसी अधिक जटिल चीजें करना चाहते हैं तो आप अवलोकन योग्य पैटर्न और एक परिपक्व पुस्तकालय का उपयोग कर सकते हैं जो इसे लागू करता है। https://github.com/ReactiveX/RxPY । ऑब्जर्वबल्स जावास्क्रिप्ट और जावा में बहुत आम हैं और कुछ एसिंक्स कार्यों के लिए उपयोग करने के लिए बहुत सुविधाजनक हैं।

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

OUTPUT :

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

1

यदि आपको एक इवेंटबस की आवश्यकता है जो प्रक्रिया या नेटवर्क सीमाओं के पार काम करता है तो आप PyMQ की कोशिश कर सकते हैं । यह वर्तमान में पब / उप, संदेश कतारों और तुल्यकालिक RPC का समर्थन करता है। डिफ़ॉल्ट संस्करण एक रेडिस बैकएंड के शीर्ष पर काम करता है, इसलिए आपको एक रनिंग रेडिस सर्वर की आवश्यकता है। परीक्षण के लिए इन-मेमोरी बैकएंड भी है। आप अपना स्वयं का बैकएंड भी लिख सकते हैं।

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 ठीक से हैंडलर को पंजीकृत करने के लिए पायथन प्रकार के एनोटेशन का उपयोग करता है।

सरल उदाहरण:

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

बसलेन को स्थापित करने के लिए, बस पाइप का उपयोग करें:

$ 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 है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.