
from weakref import WeakValueDictionary


class Signal(object):
    def __init__(self):
        self.__slots = WeakValueDictionary()

    def __call__(self, *args, **kargs):
        for key in self.__slots:
            func, _ = key
            func(self.__slots[key], *args, **kargs)

    def connect(self, slot):
        key = (slot.im_func, id(slot.im_self))
        self.__slots[key] = slot.im_self

    def disconnect(self, slot):
        key = (slot.im_func, id(slot.im_self))
        if key in self.__slots:
            self.__slots.pop(key)

    def clear(self):
        self.__slots.clear()


from weakref import WeakValueDictionary
import inspect


class Signal(object):

    def __init__(self):
        self.__slots = WeakValueDictionary()

        # For keeping references to _FuncHost objects.
        self.__funchosts = {}

    def __call__(self, *args, **kargs):
        for key in self.__slots:
            func, _ = key
            func(self.__slots[key], *args, **kargs)

    def connect(self, slot):
        if inspect.ismethod(slot):
            key = (slot.im_func, id(slot.im_self))
            self.__slots[key] = slot.im_self
        else:
            host = _FuncHost(slot)
            self.connect(host.meth)
            # We stick a copy in here just to keep the instance alive.
            self.__funchosts[slot] = host

    def disconnect(self, slot):
        if inspect.ismethod(slot):
            key = (slot.im_func, id(slot.im_self))
            if key in self.__slots:
                self.__slots.pop(key)
        else:
            if slot in self.__funchosts:
                self.disconnect(self.__funchosts[slot].meth)
                self.__funchosts.pop(slot)

    def clear(self):
        self.__slots.clear()


class _FuncHost(object):
    """Turn a function into a method."""
    def __init__(self, func):
        self.func = func

    def meth(self, *args, **kwargs):
        self.func(*args, **kwargs)


if __name__ == '__main__':
   
    a = 0
    def test_func():
        def foo():
            global a 
            a = a + 1
        s = Signal()
        s()
        s.connect(foo)
        s()
        s.disconnect(foo)
        s()        
        assert a == 1

    test_func()

Add support for connecting functions to Signal objects.
Add support for connecting functions to Signal objects.

Less elegant than the original recipe, but more functional.

  
  from weakref import WeakValueDictionary
+ import inspect
  
  
  class Signal(object):
+ 
      def __init__(self):
          self.__slots = WeakValueDictionary()
+ 
+         # For keeping references to _FuncHost objects.
+         self.__funchosts = {}
  
      def __call__(self, *args, **kargs):
          for key in self.__slots:
              func, _ = key
              func(self.__slots[key], *args, **kargs)
  
      def connect(self, slot):
+         if inspect.ismethod(slot):
-         key = (slot.im_func, id(slot.im_self))
+             key = (slot.im_func, id(slot.im_self))
-         self.__slots[key] = slot.im_self
+             self.__slots[key] = slot.im_self
+         else:
+             host = _FuncHost(slot)
+             self.connect(host.meth)
+             # We stick a copy in here just to keep the instance alive.
+             self.__funchosts[slot] = host
  
      def disconnect(self, slot):
+         if inspect.ismethod(slot):
-         key = (slot.im_func, id(slot.im_self))
+             key = (slot.im_func, id(slot.im_self))
-         if key in self.__slots:
+             if key in self.__slots:
-             self.__slots.pop(key)
+                 self.__slots.pop(key)
+         else:
+             if slot in self.__funchosts:
+                 self.disconnect(self.__funchosts[slot].meth)
+                 self.__funchosts.pop(slot)
  
      def clear(self):
          self.__slots.clear()
+ 
+ 
+ class _FuncHost(object):
+     """Turn a function into a method."""
+     def __init__(self, func):
+         self.func = func
+ 
+     def meth(self, *args, **kwargs):
+         self.func(*args, **kwargs)
+ 
+ 
+ if __name__ == '__main__':
+    
+     a = 0
+     def test_func():
+         def foo():
+             global a 
+             a = a + 1
+         s = Signal()
+         s()
+         s.connect(foo)
+         s()
+         s.disconnect(foo)
+         s()        
+         assert a == 1
+ 
+     test_func()
  
Add support for connecting functions to Signal objects.
## Code Before:

from weakref import WeakValueDictionary


class Signal(object):
    def __init__(self):
        self.__slots = WeakValueDictionary()

    def __call__(self, *args, **kargs):
        for key in self.__slots:
            func, _ = key
            func(self.__slots[key], *args, **kargs)

    def connect(self, slot):
        key = (slot.im_func, id(slot.im_self))
        self.__slots[key] = slot.im_self

    def disconnect(self, slot):
        key = (slot.im_func, id(slot.im_self))
        if key in self.__slots:
            self.__slots.pop(key)

    def clear(self):
        self.__slots.clear()

## Instruction:
Add support for connecting functions to Signal objects.
## Code After:

from weakref import WeakValueDictionary
import inspect


class Signal(object):

    def __init__(self):
        self.__slots = WeakValueDictionary()

        # For keeping references to _FuncHost objects.
        self.__funchosts = {}

    def __call__(self, *args, **kargs):
        for key in self.__slots:
            func, _ = key
            func(self.__slots[key], *args, **kargs)

    def connect(self, slot):
        if inspect.ismethod(slot):
            key = (slot.im_func, id(slot.im_self))
            self.__slots[key] = slot.im_self
        else:
            host = _FuncHost(slot)
            self.connect(host.meth)
            # We stick a copy in here just to keep the instance alive.
            self.__funchosts[slot] = host

    def disconnect(self, slot):
        if inspect.ismethod(slot):
            key = (slot.im_func, id(slot.im_self))
            if key in self.__slots:
                self.__slots.pop(key)
        else:
            if slot in self.__funchosts:
                self.disconnect(self.__funchosts[slot].meth)
                self.__funchosts.pop(slot)

    def clear(self):
        self.__slots.clear()


class _FuncHost(object):
    """Turn a function into a method."""
    def __init__(self, func):
        self.func = func

    def meth(self, *args, **kwargs):
        self.func(*args, **kwargs)


if __name__ == '__main__':
   
    a = 0
    def test_func():
        def foo():
            global a 
            a = a + 1
        s = Signal()
        s()
        s.connect(foo)
        s()
        s.disconnect(foo)
        s()        
        assert a == 1

    test_func()
