Search code examples
pythonsumtypeerrormagic-methodsvariable-length

len() vs __len__: TypeError: 'float' object cannot be interpreted as an integer


I met a weird bug and I don't think it's my code's problem.

Here are the objects I have got:

  • Boundary: represents domain and range, such that [1, 10], it has a low attribute and a high attribute, in this case low = 1 and high=10
  • Lim: represents union set, such that [1, 10]U[20, 30], stored as self.boundaries = [Boundary([1, 10]), Boundary([20, 30])]

Here's what I am trying to do,

  1. I defined __len__ in boundary, such that len(Boundary([1, 10])) #=> 9
class Boundary:
    def __len__(self):
            return abs(self.high - self.low)
  1. in Lim object, I have self.boundaries, which is a list of Boundary objects. with __len__ in Boundary defined, I coded Lim's __len__ in following:
class Lim:
    def __len__(self):
            return sum([len(bd) for bd in self.boundaries])

Here's how the issue occurred, with following composition:

class Boundary:
    def __len__(self):
        return abs(self.high - self.low)

class Lim:
    def __len__(self):
        return sum([len(bd) for bd in self.boundaries])

print(len(Lim([1, 10], [20, 30])))

# Traceback (most recent call last):
#   File "boundary.py" in <module>
#     print(len(Lim([1, 10], [20, 30])))
#   File "boundary.py", in __len__
#     return sum([len(bd) for bd in self.boundaries])
#   File "boundary.py", in <listcomp>
#     return sum([len(bd) for bd in self.boundaries])
# TypeError: 'float' object cannot be interpreted as an integer

But with this composition:

class Boundary:
    def __len__(self):
        return abs(self.high - self.low)

class Lim:
    def __len__(self):
        return sum([bd.__len__() for bd in self.boundaries])

print(len(Lim([1, 10], [20, 30])))

# Traceback (most recent call last):
#   File "boundary.py",in <module>
#   print(len(Lim([1, 10], [20, 30])))
# TypeError: 'float' object cannot be interpreted as an integer

However, the code finally executes with this composition:

class Boundary:
    def __len__(self):
        return abs(self.high - self.low)

class Lim:
    def __len__(self):
        return sum([bd.__len__() for bd in self.boundaries])

print(Lim([1, 10], [20, 30]).__len__())
# 19

Why changing len() to __len__() will dismiss the error? I will be so glad if you offer some help.


Solution

  • As pointed out in the comments, len is not compatible with your use case. I do not know how flexible your solution needs to be but I came up with a minimum working example:

    from typing import List
    
    def dist(boundary: object, m='euclidean') -> float:
        """Computes a given distance for object-typed boundaries.
        """
        return boundary.__dist__(m)
    
    def converge(lim: object) -> float:
        """_Integrates_ the distances over boundaries."""
        return lim.__converge__()
    
    
    class Boundary(object):
        low = 0
        high = 0
        
        def __init__(self, lo: float, hi: float):
            self.low = lo
            self.high = hi
            
        def __dist__(self, m: str) -> float:
            if m == 'euclidean':
                return abs(self.high - self.low)
            else:
                raise Error(f'Unknown distance {m}')
    
    class Lim(object):
        boundaries = []
        def __init__(self, boundaries: List[Boundary]):
            self.boundaries = boundaries
    
        def __converge__(self) -> float:
            return sum([dist(bd) for bd in self.boundaries])
    
    print(converge(Lim([Boundary(1.0, 10.0), Boundary(20.0, 30.0)])))
    # expect abs(10-1) + abs(30-20) = 9+10 = 19
    

    that does what you want (to the best of my knowledge). Further, you can introduce different Boundary classes that base off of the Boundary class. For the base Boundary class, you are able to introduce different distance measures and for the Lim class (being a base class), you can create different convergence schemes. While this answer does not make a single point about your initial problem (it is all in the comments), does that somehow outline a way forward for you?