Python - implement CUSTOM "lshift" method - but not for bitarray elements

I have a class which has as parameter list of integer values. I must implement custom lshift method which will do the adding to the list (I must override lshift method for my use case). This is custom lshift method so it does not have anything with bitarrays but only working with integers in the list (adding it to the list ) and it receives argument element. I should do this without import of additional python classes. I have defined also tests for this functionality so you can see how result should look like. (I have already implemented custom methods for add,len and iter but I suppose this is not relevant)

class CustomShift:

    def __init__(self, iterator=None):
        self.iterator = iterator
        if (self.iterator):
            self.iterator = list(dict.fromkeys(self.iterator))

    def __lshift__(self, element):
        """Add an element to the list.

        >>> shiftInstance = CustomShift()
        >>> _ = shiftInstance << 4
        >>> sorted(shiftInstance << 5 << 6 << 4)
        [4, 5, 6]
        """
        if self.iterator is None:
            self.iterator = []
            if element:
                self.iterator.append(element)

if __name__ == "__main__":
    import doctest
    from pprint import pprint
    doctest.testmod()

first two tests pass, but third fails!

TypeError: unsupported operand type(s) for <<: 'NoneType' and 'int'

Not sure what I am doing wrong, any hint will be appreciated. Thanks in advance

1 answer

  • answered 2019-05-15 03:17 metatoaster

    First off, __lshift__ need to return a value - even if it mutates the instance it is working on. In order for chaining to work the way you expect to, it must return itself. Since the first << operation doesn't return anything, any subsequent ones will result in the TypeError exception you noted.

    Second, the __lshift__ method has a logic error - the element is only appended if the iterator argument is None. Thus in the most general use case that you intend, nothing would actually happen.

    Finally, in your test case, you wanted to call sort on the object which implies that this object need to provide some kind of iterator (via __iter__), otherwise it will simply fail with TypeError: 'CustomShift' object is not iterable. Putting this together, your class will look like so:

    class CustomShift:
    
        def __init__(self, iterator=None):
            self.iterator = iterator
            if (self.iterator):
                self.iterator = list(dict.fromkeys(self.iterator))
    
        def __lshift__(self, element):
            """Add an element to the list.
    
            >>> shiftInstance = CustomShift()
            >>> _ = shiftInstance << 4
            >>> sorted(shiftInstance << 5 << 6 << 4)
            [4, 5, 6]
            """
            if self.iterator is None:
                self.iterator = []
            if element:
                self.iterator.append(element)
            return self
    
        def __iter__(self):
            return iter(self.iterator)
    

    However, the test will still fail, because as it is structured, shiftInstance already has a 4 appended which was assigned to _.

    Failed example:
        sorted(shiftInstance << 5 << 6 << 4)
    Expected:
        [4, 5, 6]
    Got:
        [4, 4, 5, 6]
    

    This should however put you towards a direction on how you might want to proceed from what you got.