0

I have a list with a few hundred of objects, and I want to check, if a newcomer object is already added to my list (not an equal object, but exactly this exact instance).

I have a dumb realization like this:

def is_one_of(test_object, all_objects):
    for elm in all_objects:
        if test_object is elm:
            return True
    return False

Cannot it be more beautiful?

Felix
  • 3,351
  • 6
  • 40
  • 68

5 Answers5

4

use any:

if any(x is test_object for x in all_objects):

The example in the python reference looks remarkably similar to your code already :)

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Cannot I make it faster, than linear search? Maybe, there is some way to force Python `set` to use `id(...)` as hash function? – Felix Apr 21 '14 at 16:30
  • @Felix -- No way to make it faster for `all_objects` as an arbitrary container. Of course, if `all_objects` was a `dict` or a `set` ... then yes with an appropriately defined `__hash__` ... – mgilson Apr 21 '14 at 16:36
  • My solution was bad and this is actually the best one, so +1 – vaultah Apr 21 '14 at 16:56
2

Use the any() function:

def is_one_of(test_object, all_objects):
    return any(test_object is elm for elm in all_objects)

It'll stop iterating over the generator expression as soon as a True result is found.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

Eh, I made it by putting id(element) to a set:

def _unit_list(self):
    """
    Returns all units in the order they should be initialized.
    (Performs search by width from start_point).
    """
    unit_id_set = set()
    unit_list = []
    unit_id_set.add(self.start_point)
    unit_list.append(self.start_point)
    pos = 0
    while pos < len(unit_list):
        cur_unit = unit_list[pos]
        for child in cur_unit.links_to:
            if not (id(child) in unit_id_set):
                unit_list.append(child)
                unit_id_set.add(id(child))
        pos += 1
    return unit_list
Felix
  • 3,351
  • 6
  • 40
  • 68
0

You can use

if any(test_object is x for x in all_objects): ...

if you need to do this test often however may be you can keep a set of all object ids instead

all_ids = set(map(id, all_objects))

then you can check faster with

if id(test_object) in all_ids: ...

Another common solution that may apply is to store in the object itself in a specific field if it has been already processed:

# add the object to the list
all_objects.append(x)
x.added = True

...

# Check if already added
if test_object.added: ...
6502
  • 112,025
  • 15
  • 165
  • 265
-1

I think you're looking for the in operator. The equivalent function would be:

def is_one_of(test_object, all_objects):
    return test_object in all_objects

(but you really wouldn't want to write that as a function).

Edit: I'm wrong. According to the Expressions page:

For the list and tuple types, x in y is true if and only if there exists an index i such that x == y[i] is true.

That would work if your class doesn't define __eq__, but that's more fragile than I'd want to rely on. For example:

class ObjWithEq(object):
    def __init__(self, val):
        self.val = val

    def __eq__(self, other):
        return self.val == other.val

a = ObjWithEq(1)
b = ObjWithEq(1)

assert a == b
assert a in [b]


class ObjWithoutEq(object):
    def __init__(self, val):
        self.val = val

a = ObjWithoutEq(1)
b = ObjWithoutEq(1)

assert a != b
assert a not in [b]
Kirk Strauser
  • 30,189
  • 5
  • 49
  • 65
  • 2
    No, because `in` uses *equality* tests, not identity. – Martijn Pieters Apr 21 '14 at 16:20
  • @MartijnPieters That's *probably* true, in that it's correct if equality is defined. The default behavior for classes without `__eq__` is actually what OP is asking for, but I wouldn't trust it. If a later maintainer decides comparison is a good thing and defines it, then code will break. Still, it *does* work by default. – Kirk Strauser Apr 21 '14 at 16:31
  • That's because the *default object.__eq__` implementation is to return True only when the objects are identical. But it still uses `.__eq__`. – Martijn Pieters Apr 21 '14 at 16:32
  • 1
    @KirkStrauser -- But OP already specified "Not an _equal object_, but exactly this instance". FWIW though, I always thought it was a bit of a shame that for various list methods (`.remove`, `.index`, ...) the [tutorial](https://docs.python.org/2/tutorial/datastructures.html) uses the word "is" when they really mean "equals"... – mgilson Apr 21 '14 at 16:32