Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to modify the behavior of len()?

I'm aware of creating a custom __repr__ or __add__ method (and so on), to modify the behavior of operators and functions. Is there a method override for len?

For example:

class Foo:
    def __repr__(self):
        return "A wild Foo Class in its natural habitat."

foo = Foo()

print(foo)         # A wild Foo Class in its natural habitat.
print(repr(foo))   # A wild Foo Class in its natural habitat.

Could this be done for len, with a list? Normally, it would look like this:

foo = []
print(len(foo))    # 0

foo = [1, 2, 3]
print(len(foo))    # 3

What if I want to leave search types out of the count? Like this:

class Bar(list):
    pass

foo = [Bar(), 1, '']
print(len(foo))    # 3

count = 0
for item in foo:
    if not isinstance(item, Bar):
        count += 1

print(count)       # 2

Is there a way to do this from within a list subclass?

like image 279
Zach Gates Avatar asked May 27 '15 13:05

Zach Gates


People also ask

What does the LEN () function do?

The len() function returns the number of items in an object. When the object is a string, the len() function returns the number of characters in the string.

Is Len () a function or a method?

The fact that len is a function means that classes cannot override this behaviour to avoid the check. As such, len(obj) gives a level of safety that obj.

What datatype does LEN () return?

The len function returns the length of the string, which happens to be an integer.

Can we use Len as variable in Python?

Basic Syntax for len() in Python To use the len() function to get the length of a data type, assign the data type to a variable, then pass the variable name to the len() function.


4 Answers

Yes, implement the __len__ method:

def __len__(self):
    return 42

Demo:

>>> class Foo(object):
...     def __len__(self):
...         return 42
... 
>>> len(Foo())
42

From the documentation:

Called to implement the built-in function len(). Should return the length of the object, an integer >= 0. Also, an object that doesn’t define a __bool__() method and whose __len__() method returns zero is considered to be false in a Boolean context.

For your specific case:

>>> class Bar(list):
...     def __len__(self):
...         return sum(1 for ob in self if not isinstance(ob, Bar))
... 
>>> len(Bar([1, 2, 3]))
3
>>> len(Bar([1, 2, 3, Bar()]))
3
like image 97
Martijn Pieters Avatar answered Oct 19 '22 20:10

Martijn Pieters


Yes, just as you have already discovered that you can override the behaviour of a repr() function call by implementing the __repr__ magic method, you can specify the behaviour from a len() function call by implementing (surprise surprise) then __len__ magic:

>>> class Thing:
...     def __len__(self):
...         return 123
...     
>>> len(Thing())
123

A pedant might mention that you are not modifying the behaviour of len(), you are modifying the behaviour of your class. len just does the same thing it always does, which includes checking for a __len__ attribute on the argument.

like image 34
wim Avatar answered Oct 19 '22 20:10

wim


Remember: Python is a dynamically and Duck Typed language.

If it acts like something that might have a length;

class MyCollection(object):

    def __len__(self):
        return 1234

Example:

>>> obj = MyCollection()
>>> len(obj)
1234

if it doesn't act like it has a length; KABOOM!

class Foo(object):

    def __repr___(self):
        return "<Foo>"

Example:

>>> try:
...     obj = Foo()
...     len(obj)
... except:
...     raise
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
TypeError: object of type 'Foo' has no len()

From Typing:

Python uses duck typing and has typed objects but untyped variable names. Type constraints are not checked at compile time; rather, operations on an object may fail, signifying that the given object is not of a suitable type. Despite being dynamically typed, Python is strongly typed, forbidding operations that are not well-defined (for example, adding a number to a string) rather than silently attempting to make sense of them.

Example:

>>> x = 1234
>>> s = "1234"
>>> x + s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
like image 26
James Mills Avatar answered Oct 19 '22 20:10

James Mills


You can just add a __len__ method to your class.

class Test:
    def __len__(self):
        return 2

a=Test()
len(a) # --> 2
like image 45
ForceBru Avatar answered Oct 19 '22 20:10

ForceBru