Skip to main content
added 39 characters in body; edited tags; edited title
Source Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237

Python, WeakBoundMethod Wrapping bound methods

I've written this small class in PyhonPython that wraps bound methods but does not prevent the deletion of self. Do you have any though on my code? Do you think I handle errors appropriately? Is it missing anything, what should be done to make it more robust? Is the documentation clear enough?

[This code has be edited per the reviews below. Older versions can be seen in the history.]

  1. Do you have any thoughts on my code?
  2. Do you think I handle errors appropriately?
  3. Is it missing anything?
  4. What should be done to make it more robust?
  5. Is the documentation clear enough?
import weakref

class WeakBoundMethod:
    """
    Wrapper around a method bound to a class instance. As opposed to bare
    bound methods, it holds only a weak reference to the `self` object,
    allowing it to be deleted.
    
    This can be useful when implementing certain kinds of systems that
    manage callback functions, such as an event manager.
    
    """
    def __init__(self, meth):
        """
        Initializes the class instance. It should be ensured that methods
        passed through the `meth` parameter are always bound methods. Static
        methods and free functions will produce an `AttributeError``AssertionError`.
        
        """
        assert (hasattr(meth, '__func__') and hasattr(meth, '__self__')),\
               'Object is not a bound method.'
               
        self._self = weakref.ref(meth.__self__)
        self._func = meth.__func__

    def __call__(self, *args, **kw):
        """
        Calls the bound method and returns whatever object the method returns.
        Any arguments passed to this will also be forwarded to the method.
        
        ExceptionsIn case an exception is raised by the called bound method are, notit caught.will Theybe
        shouldcaught beand handledthrown byagain to the caller of this `WeakBoundMethod` object.
        
        Calling this on objects that have been collected will result in
        aan `ReferenceError``AssertionError` being raised.
        
        """
        _self = self._self()

        if _self is None:
            raiseassert ReferenceErrorself.alive(), 'Bound method called on deleted object.')

        return self._func(_self, 
 *args, **kw)

    def __eq__(self, other)try:
        """
      return self._func(self._self(), For*args, two**kw)
 objects to be equal, the `self` and `func`except attributesException, ofe:
        the bound methods must have the same ids,raise respectivelly.e

    def alive(self):
   
      """
   A third condition must also beChecks met:whether the `self` ofobject the this bound
method is bound to has
    method must not have been collected. This immitates the behavior
        of the standard `weakref.ref` type.

        """
        _self =return self._self()
        return _self and _self is other._self() and self._func isnot other._funcNone

@Winston Ewert's review

  1. New-style class
    I am using Python 3 atm, but if I need to use Python 2, I'll make it a new style class.

  2. Assert in __init__
    I also wondered about whether that assert is any use. I kept it because it gives a more explicit error message, but I might remove it after all.

  3. Assert in __call__
    Initially (see revision), I did raise a ReferenceError as you also suggested. Here's why I decided to change that to an assert:

    a) Ideally, the user of this class shouldn't care if it was given a bare bound method, a free function, or a WeakBoundMethod. If I raise a ReferenceError, then the user better be handling it, which means he has to know that he might get passed a WeakBoundMethod.

    b) Users who do want to couple with WeakBoundMethod can instead use the alive() function to determine if the functor is okay to call. (I admit, though, that this implementation has some limitations.)

    c) Because __call__ does not check if the weak reference is still alive (only in the debug version it does), programmers who can ensure that the bound method never gets called for collected objects through their program architecture, don't have to pay the performance price of checking on every single call. (This is also my case.)

Python, WeakBoundMethod

I've written this small class in Pyhon that wraps bound methods but does not prevent the deletion of self. Do you have any though on my code? Do you think I handle errors appropriately? Is it missing anything, what should be done to make it more robust? Is the documentation clear enough?

[This code has be edited per the reviews below. Older versions can be seen in the history.]

import weakref

class WeakBoundMethod:
    """
    Wrapper around a method bound to a class instance. As opposed to bare
    bound methods, it holds only a weak reference to the `self` object,
    allowing it to be deleted.

    This can be useful when implementing certain kinds of systems that
    manage callback functions, such as an event manager.

    """
    def __init__(self, meth):
        """
        Initializes the class instance. It should be ensured that methods
        passed through the `meth` parameter are always bound methods. Static
        methods and free functions will produce an `AttributeError`.

        """
        self._self = weakref.ref(meth.__self__)
        self._func = meth.__func__

    def __call__(self, *args, **kw):
        """
        Calls the bound method and returns whatever object the method returns.
        Any arguments passed to this will also be forwarded to the method.
        
        Exceptions raised by the called bound method are not caught. They
        should be handled by the caller of this `WeakBoundMethod` object.
        
        Calling this on objects that have been collected will result in
        a `ReferenceError` being raised.
        
        """
        _self = self._self()

        if _self is None:
            raise ReferenceError('Bound method called on deleted object.')

        return self._func(_self, *args, **kw)

    def __eq__(self, other):
        """
        For two objects to be equal, the `self` and `func` attributes of
        the bound methods must have the same ids, respectivelly.
        
         A third condition must also be met: the `self` of the this bound
        method must not have been collected. This immitates the behavior
        of the standard `weakref.ref` type.

        """
        _self = self._self()
        return _self and _self is other._self() and self._func is other._func

@Winston Ewert's review

  1. New-style class
    I am using Python 3 atm, but if I need to use Python 2, I'll make it a new style class.

  2. Assert in __init__
    I also wondered about whether that assert is any use. I kept it because it gives a more explicit error message, but I might remove it after all.

  3. Assert in __call__
    Initially (see revision), I did raise a ReferenceError as you also suggested. Here's why I decided to change that to an assert:

    a) Ideally, the user of this class shouldn't care if it was given a bare bound method, a free function, or a WeakBoundMethod. If I raise a ReferenceError, then the user better be handling it, which means he has to know that he might get passed a WeakBoundMethod.

    b) Users who do want to couple with WeakBoundMethod can instead use the alive() function to determine if the functor is okay to call. (I admit, though, that this implementation has some limitations.)

    c) Because __call__ does not check if the weak reference is still alive (only in the debug version it does), programmers who can ensure that the bound method never gets called for collected objects through their program architecture, don't have to pay the performance price of checking on every single call. (This is also my case.)

Wrapping bound methods

I've written this small class in Python that wraps bound methods but does not prevent the deletion of self.

  1. Do you have any thoughts on my code?
  2. Do you think I handle errors appropriately?
  3. Is it missing anything?
  4. What should be done to make it more robust?
  5. Is the documentation clear enough?
import weakref

class WeakBoundMethod:
    """
    Wrapper around a method bound to a class instance. As opposed to bare
    bound methods, it holds only a weak reference to the `self` object,
    allowing it to be deleted.
    
    This can be useful when implementing certain kinds of systems that
    manage callback functions, such as an event manager.
    
    """
    def __init__(self, meth):
        """
        Initializes the class instance. It should be ensured that methods
        passed through the `meth` parameter are always bound methods. Static
        methods and free functions will produce an `AssertionError`.
        
        """
        assert (hasattr(meth, '__func__') and hasattr(meth, '__self__')),\
               'Object is not a bound method.'
               
        self._self = weakref.ref(meth.__self__)
        self._func = meth.__func__

    def __call__(self, *args, **kw):
        """
        Calls the bound method and returns whatever object the method returns.
        Any arguments passed to this will also be forwarded to the method.
        
        In case an exception is raised by the bound method, it will be
        caught and thrown again to the caller of this `WeakBoundMethod` object.
        
        Calling this on objects that have been collected will result in
        an `AssertionError` being raised.
        
        """        
        assert self.alive(), 'Bound method called on deleted object.'
         
        try:
            return self._func(self._self(), *args, **kw)
        except Exception, e:
            raise e

    def alive(self):
        """
        Checks whether the `self` object the method is bound to has
        been collected.
        
        """
        return self._self() is not None
Added 'weak-references' tag
Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237
deleted 64 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
import weakref

class WeakBoundMethod:
    """
    Wrapper around a method bound to a class instance. As opposed to bare
    bound methods, it holds only a weak reference to the `self` object,
    allowing it to be deleted.

    This can be useful when implementing certain kinds of systems that
    manage callback functions, such as an event manager.

    """
    def __init__(self, meth):
        """
        Initializes the class instance. It should be ensured that methods
        passed through the `meth` parameter are always bound methods. Static
        methods and free functions will produce an `AttributeError`.

        """
        self._self = weakref.ref(meth.__self__)
        self._func = meth.__func__

    def __call__(self, *args, **kw):
        """
        Calls the bound method and returns whatever object the method returns.
        Any arguments passed to this will also be forwarded to the method.
        
        Exceptions raised by the called bound method are not caught. They
        should be handled by the caller of this `WeakBoundMethod` object.
        
        Calling this on objects that have been collected will result in
        a `ReferenceError` being raised.
        
        """
        _self = self._self()

        if _self is None:
            raise ReferenceError('Bound method called on deleted object.')

        return self._func(_self, *args, **kw)

    def __eq__(self, other):
        """
        For two objects to be equal, the `self` and `func` attributes of
        the bound methods must have the same ids, respectivelly.
        
        A third condition must also be met: the `self` of the this bound
        method must not have been collected. This immitates the behavior
        of the standard `weakref.ref` type.

        """
        _self = self._self()
        return ((_self is not None) and
                (_self is other._self()) and
                (self._func is other._func))
import weakref

class WeakBoundMethod:
    """
    Wrapper around a method bound to a class instance. As opposed to bare
    bound methods, it holds only a weak reference to the `self` object,
    allowing it to be deleted.

    This can be useful when implementing certain kinds of systems that
    manage callback functions, such as an event manager.

    """
    def __init__(self, meth):
        """
        Initializes the class instance. It should be ensured that methods
        passed through the `meth` parameter are always bound methods. Static
        methods and free functions will produce an `AttributeError`.

        """
        self._self = weakref.ref(meth.__self__)
        self._func = meth.__func__

    def __call__(self, *args, **kw):
        """
        Calls the bound method and returns whatever object the method returns.
        Any arguments passed to this will also be forwarded to the method.
        
        Exceptions raised by the called bound method are not caught. They
        should be handled by the caller of this `WeakBoundMethod` object.
        
        Calling this on objects that have been collected will result in
        a `ReferenceError` being raised.
        
        """
        _self = self._self()

        if _self is None:
            raise ReferenceError('Bound method called on deleted object.')

        return self._func(_self, *args, **kw)

    def __eq__(self, other):
        """
        For two objects to be equal, the `self` and `func` attributes of
        the bound methods must have the same ids, respectivelly.
        
        A third condition must also be met: the `self` of the this bound
        method must not have been collected. This immitates the behavior
        of the standard `weakref.ref` type.

        """
        _self = self._self()
        return ((_self is not None) and
                (_self is other._self()) and
                (self._func is other._func))
import weakref

class WeakBoundMethod:
    """
    Wrapper around a method bound to a class instance. As opposed to bare
    bound methods, it holds only a weak reference to the `self` object,
    allowing it to be deleted.

    This can be useful when implementing certain kinds of systems that
    manage callback functions, such as an event manager.

    """
    def __init__(self, meth):
        """
        Initializes the class instance. It should be ensured that methods
        passed through the `meth` parameter are always bound methods. Static
        methods and free functions will produce an `AttributeError`.

        """
        self._self = weakref.ref(meth.__self__)
        self._func = meth.__func__

    def __call__(self, *args, **kw):
        """
        Calls the bound method and returns whatever object the method returns.
        Any arguments passed to this will also be forwarded to the method.
        
        Exceptions raised by the called bound method are not caught. They
        should be handled by the caller of this `WeakBoundMethod` object.
        
        Calling this on objects that have been collected will result in
        a `ReferenceError` being raised.
        
        """
        _self = self._self()

        if _self is None:
            raise ReferenceError('Bound method called on deleted object.')

        return self._func(_self, *args, **kw)

    def __eq__(self, other):
        """
        For two objects to be equal, the `self` and `func` attributes of
        the bound methods must have the same ids, respectivelly.
        
        A third condition must also be met: the `self` of the this bound
        method must not have been collected. This immitates the behavior
        of the standard `weakref.ref` type.

        """
        _self = self._self()
        return _self and _self is other._self() and self._func is other._func
added 2 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
added 455 characters in body; Post Made Community Wiki
Source Link
Paul
  • 242
  • 2
  • 10
Loading
added 117 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
Tweeted twitter.com/#!/StackCodeReview/status/110377317112623104
deleted 7 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
deleted 234 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
added 1376 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
added 1376 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
deleted 4 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
deleted 8 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
deleted 32 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
added 31 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
added 6 characters in body
Source Link
Paul
  • 242
  • 2
  • 10
Loading
Source Link
Paul
  • 242
  • 2
  • 10
Loading