Three questions, three tools. First: is it there at all?
fruits = ["apple", "banana", "cherry"] print("banana" in fruits) print("mango" in fruits)
True False
Where is it?
index() returns the position of the first match, and raises ValueError if there isn't one. Check first or catch it:
fruits = ["apple", "banana", "cherry", "banana"] print(fruits.index("banana")) try: fruits.index("mango") except ValueError: print("no mango")
1 no mango
Every position, not just the first:
fruits = ["apple", "banana", "cherry", "banana"] print([i for i, f in enumerate(fruits) if f == "banana"])
[1, 3]
The first one that matches a condition
Usually you're not looking for an exact value but for "the first order over 100" or "the user called Hedy". next() with a generator stops at the first match, and the second argument is what you get when nothing matches:
orders = [
{"id": "A-1", "total": 40},
{"id": "A-2", "total": 180},
{"id": "A-3", "total": 250},
]
big = next((o for o in orders if o["total"] > 100), None)
print(big)
huge = next((o for o in orders if o["total"] > 1000), None)
print(huge){'id': 'A-2', 'total': 180}
NoneLeave out the None and a search with no match raises StopIteration, which is a confusing error to meet in the middle of a program.
Looking things up again and again?
x in some_list checks items one by one. Fine for a few hundred, slow for a big list searched in a loop. If you look up by a key, build a dictionary once; if you only ask "is it there?", use a set. The set membership tip has the measured difference.
orders = [{"id": "A-1", "total": 40}, {"id": "A-2", "total": 180}]
by_id = {o["id"]: o for o in orders} # build once
print(by_id["A-2"]["total"]) # then every lookup is instant180