Python len(): How It Works and What It Actually Counts

len() returns how many items an object contains. It takes exactly one argument, always returns an integer, and runs in constant time regardless of size.

That last part surprises people: len() on a list of a million items is exactly as fast as on a list of three. It doesn’t count — it reads a number the object already stores.

This guide covers what len() counts for each type, which objects it refuses, how to make your own classes work with it, and the one case where the answer isn’t what you’d expect.

The short version
ArgumentsExactly one — there is no second parameter
ReturnsAn int, always zero or greater
SpeedO(1) — constant, whatever the size
Doesn’t work onNumbers, generators, None
The surpriseOn strings it counts code points, not visible characters

The syntax

One argument. That’s the whole signature.

len(object)

⚠️ A correction worth making explicitly: len() has no second parameter. Some material online describes a base argument for numeric strings — it doesn’t exist:

len("101010", 2) # TypeError: len() takes exactly one argument (2 given)

len() counts items. It has nothing to do with number bases — that’s int("101010", 2), a different function entirely.

What it counts, by type

What Python's len() counts for strings, lists, dictionaries and sets, and the four types it raises TypeError on — numbers, floats, None and generators
len("Hello, World!") # 13 ← characters, spaces included len([1, 2, 3]) # 3 ← items len((1, 2, 3)) # 3 ← items in a tuple len({"a": 1, "b": 2}) # 2 ← key-value pairs len({1, 2, 2, 3}) # 3 ← unique items in a set len(range(1000000)) # 1000000

Two details that catch people out:

Nesting doesn’t recurse. len([[1, 2], [3, 4]]) is 2, not 4 — it counts the outer items, and each inner list is one item.

And empty is zero, not an error. len("") and len([]) both return 0.

Why it’s always fast

len() doesn’t iterate. It reads a number the object keeps.

import timeitsmall = [1, 2, 3] big = list(range(1_000_000))timeit.timeit(lambda: len(small), number=100_000) # ~0.008s timeit.timeit(lambda: len(big), number=100_000) # ~0.005s

A list of a million items is not slower than a list of three. Every built-in container in CPython stores its own size and updates it on every insertion and removal.

⚠️ This is O(1) — constant time. If you see a claim that len() may be O(n) for lists or dictionaries “because it has to loop through the elements”, it’s wrong. The looping already happened, once, when the items were added.

What len() refuses

Not everything has a length.

len(42) # TypeError: object of type 'int' has no len()len(3.14) # TypeError: object of type 'float' has no len()len(None) # TypeError: object of type 'NoneType' has no len()len(x for x in range(5)) # TypeError: object of type 'generator' has no len()

Numbers have no items — a number is one value, not a container. To count the digits of an integer, convert it first: len(str(1234)) gives 4.

⚠️ The generator case is the interesting one. A generator produces items on demand and doesn’t know how many there will be — asking would mean consuming it. To count one, convert it to a list first: len(list(gen)), which exhausts the generator in the process.

And None raising here is common in real code — it usually means a function returned None where you expected a collection. Our guide on None in Python covers the object itself, and every method that returns None lists the ones that cause it — sort, append and update among them.

⚠️ The surprise: len() on strings counts code points

This is the part almost no guide mentions, and it matters for anything handling user input.

len("café") # 4 len("naïve") # 5

So far, so intuitive. But an accented character can be written two ways, and they don’t have the same length:

a = "é" # one code point b = "e\u0301" # letter e + combining acute accentprint(a, b) # é é ← they look identical print(len(a)) # 1 print(len(b)) # 2 ← same appearance, different length

Both render as “é”. One has length 1, the other 2.

The fix is normalisation:

import unicodedatab = "e\u0301" print(len(unicodedata.normalize("NFC", b))) # 1

⚠️ Why this matters in practice: a form that limits a name to 50 characters can reject text that looks shorter, because the user’s keyboard or operating system produced the decomposed form. Normalise before measuring.

Emoji are worse

len("😀") # 1 len("👨‍👩‍👧") # 5 ← one visible emoji

The family emoji is three people joined by two invisible characters. Python counts five, the user sees one.

⚠️ There’s no fix in the standard library. If you need to count what the user perceives as characters, you need a grapheme-aware library.

And bytes are different again

len("café") # 4 ← characters len("café".encode("utf-8")) # 5 ← bytes, because é takes two

This is the one to remember for databases. A column declared as 50 bytes holds fewer than 50 accented characters — and the error only appears with real data.

Three ways len() surprises with text in Python: combining accents produce a different length from precomposed characters, emoji sequences count as several code points, and byte length differs from character length

Making your own class work with len()

len(obj) calls obj.__len__(). Define that method and your class works with the built-in:

class Basket: def __init__(self, items): self.items = itemsdef __len__(self): return len(self.items)basket = Basket(["apple", "pear", "fig"]) print(len(basket)) # 3

And there’s a side effect worth knowing: defining __len__ also makes the object usable in a boolean test.

print(bool(Basket([]))) # False ← length 0 is falsy print(bool(Basket(["apple"]))) # True

⚠️ Two rules __len__ must follow: it has to return a non-negative integer, and Python enforces it.

class Broken: def __len__(self): return -1len(Broken()) # ValueError: __len__() should return >= 0

Common patterns with len()

Don’t use it to test for empty

This is the most common len() misuse.

if len(items) == 0: # works, but not idiomatic ...if not items: # preferred ...

Empty containers are already falsy, so the comparison adds nothing. PEP 8 recommends the second form.

⚠️ The exception is when None is possible. not items is true for both an empty list and None — if you need to tell them apart, check for None explicitly first.

Validating input length

password = input("Password: ")if len(password) < 8: print("Too short — at least 8 characters.") else: print("Accepted")

A genuine use — and one of the few where comparing the number is the point. Our guide on input in Python covers reading it safely.

Don’t loop over indices

for i in range(len(fruits)): # works, but not Pythonic print(fruits[i])for fruit in fruits: # preferred print(fruit)for i, fruit in enumerate(fruits): # when you need the index print(i, fruit)

range(len(x)) is the clearest sign of someone writing Python with another language’s habits. Our guide on the for loop covers the alternatives.

Functions that count something else

Four built-ins are often confused with len().

FunctionWhat it returns
len(x)How many items — always O(1)
x.count(v)How many times v appears — a method, and O(n)
sum(x)The total of the values, not how many
any(x) / all(x)Whether some or all items are truthy
numbers = [1, 2, 3, 4, 2, 5, 2]len(numbers) # 7 ← how many items numbers.count(2) # 3 ← how many times 2 appears sum(numbers) # 19 ← the total

⚠️ And don’t shadow the built-ins. Writing sum = sum(numbers) replaces the function with a number, and the next call raises TypeError: 'int' object is not callable.

Accented characters break more than string length

A database column sized in bytes holds fewer accented characters than you’d expect — and the error only shows up with real data. Copahost VPS plans give you root access, your choice of Python and MySQL versions, and full control over collation and encoding. From €3.99/month, with snapshots before every change.

See VPS plans

Frequently asked questions

What does len() do in Python?

It returns how many items an object contains — characters in a string, items in a list, key-value pairs in a dictionary. It takes exactly one argument and always returns a non-negative integer.

Does len() have a second parameter?

No. len() takes exactly one argument, and passing two raises TypeError: len() takes exactly one argument. Any description of a base parameter is confusing it with int(), which does accept a base for converting numeric strings.

Is len() slow on large lists?

No — it’s O(1), constant time. A list of a million items is no slower than one of three, because every built-in container stores its own size and updates it as items are added or removed. len() reads that number rather than counting.

Why does len() fail on a number?

Because a number isn’t a container — it’s a single value with no items to count. To count the digits of an integer, convert it to a string first: len(str(1234)) returns 4.

Why can’t I use len() on a generator?

Because a generator produces items on demand and doesn’t know how many there will be — finding out would mean consuming it. If you need the count, convert to a list first with len(list(gen)), accepting that the generator is exhausted afterwards.

Why is len() of my string different from what I see?

Because it counts code points, not visible characters. An accented letter can be stored as one code point or as a letter plus a combining accent — the two look identical and have lengths 1 and 2. Normalise with unicodedata.normalize("NFC", text) before measuring.

Why does len() of an emoji return more than 1?

Because many emoji are sequences. A family emoji is three people joined by invisible characters, which Python counts as five code points while the user sees one symbol. The standard library has no fix — counting what the user perceives requires a grapheme-aware library.

What’s the difference between len() on a string and on its bytes?

len("café") is 4 characters, but len("café".encode("utf-8")) is 5 bytes, because the accented letter takes two. ⚠️ This matters for databases: a column sized in bytes holds fewer accented characters than its number suggests.

How do I make len() work with my own class?

Define a __len__ method that returns a non-negative integer. len(obj) calls it. A side effect worth knowing: defining __len__ also makes your object work in boolean tests, with length zero being falsy.

Should I use len(x) == 0 to check if something is empty?

It works, but it isn’t idiomatic. Empty containers are already falsy, so if not x is the preferred form and PEP 8 recommends it. ⚠️ The exception is when None is possible — not x is true for both, so check for None explicitly if the difference matters.

Does len() count nested items?

No. len([[1, 2], [3, 4]]) is 2 — it counts the outer items, and each inner list counts as one. To count everything, flatten first or sum the lengths of the inner items.

What’s the difference between len() and count()?

len(x) returns how many items there are, and is constant time. x.count(v) returns how many times a specific value appears, and has to scan the whole object. They’re also different kinds of thing: len() is a built-in function, count() is a method.

Conclusion

len() is among the first functions anyone learns in Python, and one of the simplest: one argument, one integer, constant time. It doesn’t count — it reads a number the object already keeps, which is why size never affects speed.

Three things are worth carrying away. It takes exactly one argument — there is no base parameter, and anything suggesting otherwise is confusing it with int(). It’s O(1) always, not sometimes. And on strings it counts code points, which is why "é" can have length 1 or 2 depending on how it was typed.

That last one is the only real trap, and it only shows up with real user data — a name with an accent, an emoji in a bio, a form limit that rejects text that looks short enough. Normalise before you measure, and it goes away.

Share the Post:
Picture of Gustavo Gallas

Gustavo Gallas

Graduated in Computing at PUC-Rio, Brazil. Specialized in IT, networking, systems administration and human and organizational development​. Also have brewing skills.