Wells
Wells

Reputation: 10969

**kwargs search mechanism in an object (python)

Want to be able to provide a search interface for a collection of objects to be used by passing a list of keyword arguments like so:

playerID = players.search(nameFirst='ichiro', nameLast='suzuki')

Where players.search is defined like so:

def search(self, **args):
    ret = []
    for playerID, player in self.iteritems():
        for key, value in args.iteritems():
            if getattr(player, key) == value:
                ret.append(player.playerID)

    return ret

Obviously the above code doesn't work. I want to, to borrow some SQL idioms, to work like where player.key == value and player.keyN = valueN, and so on for N number of kwargs passed.

Any ideas? Thanks!

Upvotes: 0

Views: 184

Answers (2)

Alex Martelli
Alex Martelli

Reputation: 882421

I want to, to borrow some SQL idioms, to work like where player.key == value and player.keyN = valueN, and so on for N number of kwargs passed.

So you're currently implementing an OR and want to implement an AND instead -- is that it?

If so, then the all suggested in @Mark's answer would work -- or alternatively, and equivalently albeit at a lower level of abstraction:

def search(self, **args):
  ret = []
  for playerID, player in self.iteritems():
    for key, value in args.iteritems():
      if getattr(player, key) != value: break
    else:
       ret.append(player.playerID)

  return ret

I'm not quite sure why you're looping on iteritems and then ignoring the key you're getting (appending player.playerID rather than the playerID key directly).

Anyway, another high-abstraction approach, assuming you don't need the keys...:

def search(self, **args):
  def vals(p):
    return dict((k, getattr(p, k, None)) for k in args)
  return [p.playerID for p in self.itervalues() if vals(p) == args]

This one doesn't "short-circuit" but is otherwise equivalent to Mark's. Fully equivalent, but quite concise:

def search(self, **args):
  return [p.playerID for p in self.itervalues()
          if all(getattr(p, k, None)==args[k] for k in args)]

If these code snippets don't meet your needs, and you can clarify why exactly they don't (ideally with an example or three!-), I'm sure they can be tweaked to satisfy said needs.

Upvotes: 1

Mark Rushakoff
Mark Rushakoff

Reputation: 258388

You should be able to change it to a list comprehension with the all builtin, which returns True iff all the elements in its argument are true (or if the iterable is empty). Something like this should do the trick:

for playerID, player in self.iteritems():
    if all(getattr(player, key) == value for key, value in args.iteritems()):
         ret.append(player.playerID)

Upvotes: 1

Related Questions