Finding attributes in a list

Bengt Richter bokr at oz.net
Sun Apr 3 03:14:55 EDT 2005


On Sat, 2 Apr 2005 23:44:11 -0500, Marcus Goldfish <magoldfish at gmail.com> wrote:

>> class Player(object):
>>    def __init__(self, **kw): self.__dict__.update(kw)
>>    def __repr__(self): return '<Player %s>'%getattr(self, 'name', '(anonymous)')
>>
>> import operator
>> [p.name for p in sorted(players, key=operator.attrgetter('attacking'), reverse=True)]
>
>Just happened to read this thread and wanted to say this is a neat
>little example-- thank you!  I have a couple of followup questions.
>
>   (1) Is there a performance penalty for using key=operator.attrgetter()?
        I would think sorted would make it as efficient as possible, but possibly.
        I can conceive of low level optimization for key=<some C builtin that can be recognized>
        But timing is best ;-) Also, vs what? There are a number of alternatives.

>   (2) The Player class looks like a nice model for a data table when one
>        wants to sort by arbitrary column.  Would you agree?
         Depends on scale, and what else use you have for a custom object representation. E.g.,
         (using below lists available from interactive session below) a plain dict by names (unique required)
         with (attack, defense) tuples as values is probably pretty efficient and fast.

          >>> dict(zip(names, zip(attack, defense)))
          {'linda': (8, 6), 'bob': (7, 6), 'sam': (5, 8)}

         But if objects are going to be complex and have methods or properties, OO makes it easy.

>   (3) Suppose one wished to construct a player list from a collection of
>        attribute lists, e.g.,
>
>                names = ['bob', 'sam', 'linda']
>                attack = [7, 5, 8]
>                defense = [6, 8, 6]
>                # construct players list here
>
>       Can you recommend an efficient way to construct the player list?
>
I wouldn't worry about efficiency unless you are dealing with a database of
all the worlds teams ;-) (And in that case, you probably want to look into
interfacing with the database your data is already in, to let it do things
for you natively e.g. via SQL).

If you know the "columns" as matching attribute lists as above, zip will associate them
into tuples. Also, I used a keyword argument in the Player __init__ above because I
wanted to copy and paste the dict calls from the prior post, but knowing exact columns,
I'd probably do some thing like:

 >>> class Player(object):
 ...     def __init__(self, name, attack=None, defense=None): # require name
 ...         self.name = name
 ...         self.attack = attack
 ...         self.defense = defense
 ...
 >>> names = ['bob', 'sam', 'linda']
 >>> attack = [7, 5, 8]
 >>> defense = [6, 8, 6]
 >>>

Then the players list is just
 >>> players = [Player(*tup) for tup in zip(names, attack, defense)]
where *tup unpacks a tuple from the output of zip into the arg list of Player's __init__.

And you can extract the names like
 >>> [player.name for player in players]
 ['bob', 'sam', 'linda']

Or whatever you want
 >>> for player in players: print player.name, player.attack, player.defense
 ...
 bob 7 6
 sam 5 8
 linda 8 6

As mentioned, zip makes tuples of corresponding elements of its argument lists:
 >>> zip(names, attack, defense)
 [('bob', 7, 6), ('sam', 5, 8), ('linda', 8, 6)]

If you had huge input lists, you could avoid the terporary tuple list using
 >>> import itertools
 >>> iplayers = list(itertools.starmap(Player, itertools.izip(names, attack, defense)))
 >>> for player in iplayers: print player.name, player.attack, player.defense
 ...
 bob 7 6
 sam 5 8
 linda 8 6

You can look into __slots__ if you want to have objects but need reduced memory footprint.
But don't worry about optimizing 'til you can prove you need it, unless just for fun ;-)

Ok. That's enough relief for me. Got other stuff ...

Regards,
Bengt Richter



More information about the Python-list mailing list