0
ids = []

for object in objects:
  ids += [object.id]
cyberguijarro
  • 715
  • 3
  • 9
  • 21
  • Note that `ids += [object.id]` is usually written simply as `ids.append(object.id)`. – Eric O. Lebigot Oct 15 '10 at 09:12
  • @Tumbleweed: `object` is not a keyword, it's a built-in function. had it been a keyword this code would raise a `SyntaxError`. – SilentGhost Oct 15 '10 at 15:11
  • ye right its builtin type - but I would suggest not to replace it !! – shahjapan Oct 16 '10 at 02:24
  • Possible duplicate of [How to extract from a list of objects a list of specific attribute?](https://stackoverflow.com/questions/677656/how-to-extract-from-a-list-of-objects-a-list-of-specific-attribute) – Georgy Oct 26 '18 at 09:00

4 Answers4

27

You can use a list comprehension:

ids = [object.id for object in objects]

For your reference:

Both produce the same result. In many cases, a list comprehension is an elegant and pythonic way to do the same as what you mentioned.

tzot
  • 92,761
  • 29
  • 141
  • 204
pyfunc
  • 65,343
  • 15
  • 148
  • 136
4

The standard (i.e “pythonic” a.k.a cleanest :) way is to use a list comprehension:

ids= [obj.id for obj in objects]

The above works for all Python versions ≥ 2.0.

Other ways (just FYI)

In Python 2, you can also do:

ids= map(lambda x: x.id, objects)

which should be the slowest method, or

# note: Python ≥ 2.4
import operator
ids= map(operator.attrgetter('id'), objects)

which might be the fastest method, although I assume the difference won't be that much; either way, the clarity of the list comprehension outweighs speed gains.

Should you want to use an alternative way in Python 3, you should enclose the map call in a list call:

ids= list(map(operator.attrgetter('id'), objects))

because the map builtin returns a generator instead of a list in Python 3.

tzot
  • 92,761
  • 29
  • 141
  • 204
  • in this case the list comprehension is twice as fast as mapping `lambda` which is itself around ten percent faster than mapping `attrgetter('id')` (surprised me too) – aaronasterling Oct 15 '10 at 13:15
  • @aaron: I'm also surprised; please provide some sample `objects` where `lambda` is *faster* than mapping `attrgetter`; if `id` is a simple attribute (i.e not a property), `attrgetter` should be faster than `lambda`. – tzot Oct 15 '10 at 13:33
  • the test I used was `class Foo(object): def __init__(self, id_): self.id = id_`. The time was linear so it didn't make a difference if the list had one element or 10000. – aaronasterling Oct 15 '10 at 13:51
  • @aaron: yep, same results here. The age-old suggestion to “never guess, always time” applies :) – tzot Oct 15 '10 at 14:02
  • 1
    @aaron: there was an issue in the operator.c code after the ability to process dotted names, and attrgetter was slowed down; I've submitted a [patch](http://bugs.python.org/issue10160) that once again makes `attrgetter` run almost twice as fast as the `lambda`. Hopefully the issue will be resolved. (And, wow: my usage of English is deteriorating very fast since last midnight; I need some sleep). – tzot Oct 21 '10 at 07:07
3

Another way:

ids = map(lambda x: x.id, objects)
anti_social
  • 323
  • 3
  • 9
1

You don't need the operator module:

In [12]: class Bar(object):
             def __init__(self, id_):
                 self.id = id_         

In [15]: foo = [Bar(1) for _ in xrange(10000)]

In [16]: foobar = map(lambda bar: getattr(bar, 'id'), foo)

In [17]: len(foobar)
Out[17]: 10000

In [18]: foobar[:10]
Out[18]: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
rubik
  • 8,814
  • 9
  • 58
  • 88
  • 1
    I'm curious, too, rubik. Maybe someone has puritanical beliefs about the '_' name. In any case, I've brought you back to zero. – Cameron Laird Oct 21 '10 at 16:10
  • Thank you. But I usually use the '_' in for loops where I don't need the counter... – rubik Oct 21 '10 at 18:25
  • I didn't vote you down, but there are actually good reasons not to use `_` as temporary variable. As the variable remains in the current scope, you cannot use gettext's `_` anymore in the same function (if you imported `gettext.gettext` as `_` for translation). I usually call such variables "unused" or "unused_xyz" because then PyDev doesn't show a warning about it being actually unused. – AndiDog Oct 22 '10 at 17:29